Upgrade the runtime and native plugin API to OpenCode 2.0.24. Restore session-scoped learning, credential renewal, typed project queries and controlled frontend actions from the legacy Agent. Replace host Shell execution with an approved Landlock/seccomp executor and persistent per-session workspaces. Add typed Bocha search and Tianditu geocoding bridges using the owning session credential. Require a working kernel sandbox before startup and persist workspace files in deployments. Validation completed before this commit: TypeScript checks, 150 Bun tests, Docker build and UID 10001 sandbox execution, native Shell approval and session recovery. Tianditu succeeded; Bocha currently reports exhausted account quota. Unrelated investigation-report contract work is excluded.
121 lines
4.4 KiB
TypeScript
121 lines
4.4 KiB
TypeScript
import query_project_data from "../tools/query_project_data.js";
|
|
import web_search from "../tools/web_search.js";
|
|
import geocode from "../tools/geocode.js";
|
|
import final_answer from "../tools/final_answer.js";
|
|
import locate_features from "../tools/locate_features.js";
|
|
import zoom_to_map from "../tools/zoom_to_map.js";
|
|
import show_chart from "../tools/show_chart.js";
|
|
import view_history from "../tools/view_history.js";
|
|
import { Plugin, Rpc } from "@opencode/plugin";
|
|
import { z } from "zod";
|
|
import { resolve } from "node:path";
|
|
import { registerSessionSandbox } from "../session-sandbox.js";
|
|
|
|
import add_task_human_evidence from "../tools/add_task_human_evidence.js";
|
|
import clear_scada_analysis from "../tools/clear_scada_analysis.js";
|
|
import get_operational_task_context from "../tools/get_operational_task_context.js";
|
|
import memory_manager from "../tools/memory_manager.js";
|
|
import propose_task_action from "../tools/propose_task_action.js";
|
|
import publish_execution_plan from "../tools/publish_execution_plan.js";
|
|
import publish_task_report from "../tools/publish_task_report.js";
|
|
import query_operational_task_data from "../tools/query_operational_task_data.js";
|
|
import render_scada_analysis from "../tools/render_scada_analysis.js";
|
|
import request_task_data_access from "../tools/request_task_data_access.js";
|
|
import session_search from "../tools/session_search.js";
|
|
import skill_manager from "../tools/skill_manager.js";
|
|
|
|
const businessTools = {
|
|
web_search,
|
|
geocode,
|
|
query_project_data,
|
|
final_answer,
|
|
locate_features,
|
|
zoom_to_map,
|
|
show_chart,
|
|
view_history,
|
|
add_task_human_evidence,
|
|
clear_scada_analysis,
|
|
get_operational_task_context,
|
|
memory_manager,
|
|
propose_task_action,
|
|
publish_execution_plan,
|
|
publish_task_report,
|
|
query_operational_task_data,
|
|
render_scada_analysis,
|
|
request_task_data_access,
|
|
session_search,
|
|
skill_manager,
|
|
};
|
|
|
|
export default Plugin.define({
|
|
id: "tjwater.tools",
|
|
async setup(ctx) {
|
|
await registerSessionSandbox(ctx);
|
|
await ctx.tool.transform((editor) => {
|
|
for (const [name, definition] of Object.entries(businessTools)) {
|
|
const input = z.object(definition.args);
|
|
editor.add({
|
|
name,
|
|
description: definition.description,
|
|
input,
|
|
options: { codemode: false, permission: name },
|
|
async execute(args, context) {
|
|
// Validate again at the execution boundary, retaining domain refinements.
|
|
const parsed = input.parse(args);
|
|
const content = await definition.execute(parsed as never, {
|
|
sessionID: context.sessionID,
|
|
callID: context.id,
|
|
signal: context.signal,
|
|
});
|
|
return { content };
|
|
},
|
|
});
|
|
}
|
|
});
|
|
const todoItem = z.object({
|
|
id: z.string().optional(),
|
|
content: z.string(),
|
|
status: z.enum(["pending", "in_progress", "completed", "cancelled"]),
|
|
priority: z.enum(["low", "medium", "high"]).optional(),
|
|
});
|
|
await ctx.tool.transform((editor) => {
|
|
editor.add({
|
|
name: "todowrite",
|
|
description: "Update the current session's task checklist.",
|
|
input: z.object({ todos: z.array(todoItem) }),
|
|
options: { codemode: false, permission: "todowrite" },
|
|
async execute({ todos }, context) {
|
|
await ctx.storage.set(`todos/${context.sessionID}`, todos);
|
|
return { content: JSON.stringify(todos) };
|
|
},
|
|
});
|
|
editor.add({
|
|
name: "todoread",
|
|
description: "Read the current session's task checklist.",
|
|
input: z.object({}),
|
|
options: { codemode: false, permission: "todoread" },
|
|
async execute(_input, context) {
|
|
return { content: JSON.stringify(await ctx.storage.get(`todos/${context.sessionID}`) ?? []) };
|
|
},
|
|
});
|
|
});
|
|
await ctx.rpc.register(Rpc.define({
|
|
id: "tjwater.tools",
|
|
methods: {
|
|
list: {
|
|
input: z.object({}),
|
|
output: z.array(z.object({ id: z.string(), direct: z.boolean(), sandboxed: z.boolean(), workspaceRoot: z.string().optional() })),
|
|
},
|
|
},
|
|
events: {},
|
|
}), {
|
|
list: async () => (await ctx.tool.list()).map((tool) => ({
|
|
id: tool.id,
|
|
direct: tool.options?.codemode === false,
|
|
sandboxed: tool.id === "shell" && tool.description.includes("Landlock + seccomp"),
|
|
...(tool.id === "shell" ? { workspaceRoot: resolve(process.env.TJWATER_AGENT_WORKSPACE_ROOT ?? ".agent-workspaces") } : {}),
|
|
})),
|
|
});
|
|
},
|
|
});
|