feat: add scoped history and artifacts
Generic Container CI/CD / test-build-publish (push) Failing after 8m25s
Agent CI/CD / build-test-publish-and-deploy (push) Failing after 8m25s

This commit is contained in:
2026-10-22 18:40:20 +08:00
parent bc4e9834e5
commit 7d47a2df86
48 changed files with 2632 additions and 78 deletions
+19
View File
@@ -397,6 +397,25 @@ export class OpencodeRuntimeAdapter {
}
}
async cancelPendingUserInput(sessionId: string, id: string): Promise<ResponseInput> {
const item = (await this.listPendingUserInputs(sessionId)).find((input) => input.id === id);
if (!item) throw Object.assign(new Error("Message has already been consumed or is unavailable"), { status: 409 });
const client = await this.ensureClient();
try { await client.session.inbox.cancel({ sessionID: sessionId, inboxID: id }); }
catch (error) {
if (!(await this.listPendingUserInputs(sessionId)).some((input) => input.id === id)) {
throw Object.assign(new Error("Message has already been consumed or is unavailable"), { status: 409 });
}
throw error;
}
// Native cancel also succeeds for an already consumed ID. Check the native
// message record so a delivery racing cancellation never restores a sent draft.
if (await this.agentMessageState(sessionId, id) !== "missing") {
throw Object.assign(new Error("Message has already been consumed or is unavailable"), { status: 409 });
}
return { ...item, status: "cancelled" };
}
async prompt(sessionId: string, text: string, model?: RuntimeModelOverride) {
const client = await this.ensureClient();
const startedAt = Date.now();
+2 -2
View File
@@ -1,4 +1,4 @@
import { PRODUCT_MCP_TOOL_IDS, QUERY_MCP_TOOL_IDS, queryMcpToolId } from "./product-mcp-tools.js";
import { PRODUCT_MCP_TOOL_IDS, QUERY_MCP_AUTOALLOW_TOOL_IDS, PRODUCT_MCP_WRITE_TOOL_IDS, queryMcpToolId } from "./product-mcp-tools.js";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { normalizeMcpConfig } from "./mcp-config.js";
@@ -54,7 +54,7 @@ export function serviceOpencodeConfig(input: Record<string, unknown>, models: Ag
return {
...input,
...compileOpencodeModels(models),
permissions: sessionWorkspacePermissions([...(input.permissions ?? []) as PermissionRule[], ...QUERY_MCP_TOOL_IDS.filter((name) => !deniedProductQuery(name, (input.permissions ?? []) as PermissionRule[])).map((action): PermissionRule => ({ action, resource: "*", effect: "allow" }))], workspaceRoot),
permissions: sessionWorkspacePermissions([...(input.permissions ?? []) as PermissionRule[], ...QUERY_MCP_AUTOALLOW_TOOL_IDS.filter((name) => !deniedProductQuery(name, (input.permissions ?? []) as PermissionRule[])).map((action): PermissionRule => ({ action, resource: "*", effect: "allow" })), ...PRODUCT_MCP_WRITE_TOOL_IDS.filter((name) => !deniedProductQuery(name, (input.permissions ?? []) as PermissionRule[])).map((action): PermissionRule => ({ action, resource: "*", effect: "ask" }))], workspaceRoot),
snapshots: false,
mcp: normalizeMcpConfig(input.mcp),
plugins: ["*", ...((input.plugins ?? []) as unknown[]), resolve(".opencode/tjwater-plugin"),
+49 -2
View File
@@ -1,36 +1,83 @@
export const PRODUCT_MCP_READ_TOOLS = [
"get_mutation_status",
"export_project_model",
"export_sensor_placement",
"get_analysis_run",
"get_element_gis_properties",
"get_element_properties",
"get_model_baseline_at_time",
"get_model_configuration",
"get_network_options",
"get_network_schema",
"get_node_connections",
"get_project_info",
"get_region",
"get_region_nodes",
"get_scada_device",
"get_sensor_placement_candidate",
"get_sensor_placement_run",
"get_source_gis_properties",
"get_task_context",
"get_task_evidence",
"get_task_run_history",
"get_workbench_runtime",
"list_analysis_results",
"list_analysis_runs",
"list_elements",
"list_major_nodes",
"list_model_configurations",
"list_regions",
"list_scada_devices",
"list_sensor_placement_runs",
"list_task_schedules",
"query_analysis_series",
"query_analysis_values",
"query_element_history",
"query_model_baseline_field",
"query_model_baseline_series",
"query_nearby_elements",
"query_network_extent",
"query_peripheral_data",
"query_scada_readings",
"reverse_geocode",
"search_project_places",
] as const;
export const PRODUCT_MCP_ANALYSIS_TOOLS = ["run_simulation", "run_burst_detection", "run_burst_location", "analyze_valve_isolation", "run_leakage_identification", "run_contaminant_simulation", "analyze_valve_closure", "analyze_flushing", "analyze_pressure_regulation", "optimize_sensor_placement"] as const;
export const PRODUCT_MCP_TOOLS = [...PRODUCT_MCP_READ_TOOLS, ...PRODUCT_MCP_ANALYSIS_TOOLS] as const;
export const PRODUCT_MCP_ANALYSIS_TOOLS = [
"analyze_flushing",
"analyze_pressure_regulation",
"analyze_valve_closure",
"analyze_valve_isolation",
"calculate_demand_allocation",
"optimize_sensor_placement",
"run_burst_detection",
"run_burst_location",
"run_burst_simulation",
"run_contaminant_simulation",
"run_leakage_identification",
"run_simulation",
] as const;
export const PRODUCT_MCP_WRITE_TOOLS = [
"cancel_task_run",
"clean_scada_readings",
"create_operational_task",
"create_sensor_placement",
"create_task_schedule",
"delete_scada_readings",
"edit_model_configuration",
"edit_model_element",
"retry_operational_task",
"save_simulation_scenario",
"update_sensor_placement",
"update_task_schedule",
"write_scada_readings",
] as const;
export const PRODUCT_MCP_TOOLS = [...PRODUCT_MCP_READ_TOOLS, ...PRODUCT_MCP_ANALYSIS_TOOLS, ...PRODUCT_MCP_WRITE_TOOLS] as const;
export const QUERY_MCP_SERVER = "mcp_tjwater";
export const queryMcpToolId = (name: string) => `${QUERY_MCP_SERVER}_${name}`;
export const PRODUCT_MCP_TOOL_IDS = PRODUCT_MCP_TOOLS.map(queryMcpToolId);
export const PRODUCT_MCP_READ_TOOL_IDS = PRODUCT_MCP_READ_TOOLS.map(queryMcpToolId);
export const PRODUCT_MCP_WRITE_TOOL_IDS = PRODUCT_MCP_WRITE_TOOLS.map(queryMcpToolId);
export const LOCAL_QUERY_TOOLS = ["geocode", "session_search", "list_investigation_tasks", "get_investigation_task", "read_agent_messages", "todoread"] as const;
export const LOCAL_QUERY_TOOL_IDS = LOCAL_QUERY_TOOLS.map(queryMcpToolId);
export const QUERY_MCP_TOOL_IDS = [...PRODUCT_MCP_TOOL_IDS, ...LOCAL_QUERY_TOOL_IDS];
export const QUERY_MCP_AUTOALLOW_TOOL_IDS = [...PRODUCT_MCP_READ_TOOL_IDS, ...PRODUCT_MCP_ANALYSIS_TOOLS.map(queryMcpToolId), ...LOCAL_QUERY_TOOL_IDS];
+2 -2
View File
@@ -1,11 +1,11 @@
import type { RuntimeSessionContext } from "./sessionContext.js";
import { LOCAL_QUERY_TOOLS, PRODUCT_MCP_TOOLS, PRODUCT_MCP_READ_TOOLS, queryMcpToolId } from "./product-mcp-tools.js";
import { LOCAL_QUERY_TOOLS, PRODUCT_MCP_TOOLS, PRODUCT_MCP_READ_TOOLS, PRODUCT_MCP_WRITE_TOOLS, queryMcpToolId } from "./product-mcp-tools.js";
const projectLocal = new Set(["geocode", "list_investigation_tasks", "get_investigation_task", "read_agent_messages"]);
export function allowedQueryTools(context: RuntimeSessionContext, reader = false): string[] {
if (context.learningMode === "review") return [];
const project = !!context.projectId && !context.projectKey.startsWith("personal-") && ["ordinary_chat", "task_execution", "task_collaboration"].includes(context.purpose ?? "");
if (reader) return project ? PRODUCT_MCP_READ_TOOLS.map(queryMcpToolId) : [];
return [...(project ? PRODUCT_MCP_TOOLS : []), ...LOCAL_QUERY_TOOLS.filter((name) =>
return [...(project ? PRODUCT_MCP_TOOLS.filter((name) => !(PRODUCT_MCP_WRITE_TOOLS as readonly string[]).includes(name) || context.purpose === "ordinary_chat") : []), ...LOCAL_QUERY_TOOLS.filter((name) =>
(!projectLocal.has(name) || project) && !(context.purpose === "task_execution" && ["list_investigation_tasks", "get_investigation_task"].includes(name)))].map(queryMcpToolId);
}