{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../src/core/jobs/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAe,MAAM,+BAA+B,CAAC;AAE7F,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAkC3D,mEAAmE;AACnE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,qBAAqB,GAAG,SAAS,CAAC,GAAG,CAAC,EAAE,CA0IhF;AAED,YAAY,EAAE,eAAe,EAAE,CAAC","sourcesContent":["import type { AgentTool, AgentToolResult, ToolEffects } from \"@apholdings/jensen-agent-core\";\nimport { Type } from \"@sinclair/typebox\";\nimport type { BackgroundJobRegistry } from \"./registry.js\";\n\nconst processEffects: ToolEffects = {\n\treadsWorkspace: false,\n\twritesWorkspace: false,\n\tcreatesFiles: false,\n\tdeletesFiles: false,\n\texecutesProcesses: true,\n\tstartsPersistentProcesses: true,\n\taccessesNetwork: false,\n\tmutatesGit: false,\n\tmutatesExternalState: false,\n\thandlesSecrets: false,\n\tpotentiallyDestructive: false,\n\trequiresExclusiveWorkspaceLease: true,\n\tparallelSafe: false,\n\tscopes: [{ kind: \"process\" }],\n};\n\nconst readEffects: ToolEffects = {\n\t...processEffects,\n\texecutesProcesses: false,\n\tstartsPersistentProcesses: false,\n\trequiresExclusiveWorkspaceLease: false,\n\tparallelSafe: true,\n};\n\nconst jobIdSchema = { jobId: Type.String({ description: \"Background job id\" }) };\nconst startSchema = {\n\texecutable: Type.String({ description: \"Executable to run\" }),\n\targs: Type.Optional(Type.Array(Type.String(), { description: \"Arguments\" })),\n\tcwd: Type.Optional(Type.String({ description: \"Working directory (authorized scope)\" })),\n};\n\n/** Create the background-job tools bound to a durable registry. */\nexport function createJobTools(registry: BackgroundJobRegistry): AgentTool<any>[] {\n\tconst text = (t: string): AgentToolResult<any> => ({\n\t\tcontent: [{ type: \"text\" as const, text: t }],\n\t\tdetails: {},\n\t});\n\n\tconst start: AgentTool<any> = {\n\t\tname: \"job_start\",\n\t\tlabel: \"job_start\",\n\t\tdescription: \"Start a durable background job owned through an authoritative process tree.\",\n\t\tparameters: Type.Object(startSchema),\n\t\teffects: processEffects,\n\t\texecute: async (_id, params: { executable: string; args?: string[]; cwd?: string }) => {\n\t\t\tconst rec = await registry.start({ executable: params.executable, args: params.args ?? [], cwd: params.cwd });\n\t\t\treturn text(`Job ${rec.jobId} started (pid ${rec.processIdentity}, state ${rec.state}).`);\n\t\t},\n\t};\n\n\tconst status: AgentTool<any> = {\n\t\tname: \"job_status\",\n\t\tlabel: \"job_status\",\n\t\tdescription: \"Report verified live status of a background job (real process identity).\",\n\t\tparameters: Type.Object(jobIdSchema),\n\t\teffects: readEffects,\n\t\texecute: async (_id, params: { jobId: string }) => {\n\t\t\tconst classification = await registry.status(params.jobId);\n\t\t\tif (!classification) return text(`Job ${params.jobId} not found.`);\n\t\t\tconst rec = classification.record;\n\t\t\treturn text(\n\t\t\t\t`Job ${rec.jobId}: state=${rec.state} identity=${rec.processIdentity} health=${rec.health ?? \"unknown\"} exitCode=${rec.exitCode ?? \"n/a\"} kind=${classification.kind}`,\n\t\t\t);\n\t\t},\n\t};\n\n\tconst list: AgentTool<any> = {\n\t\tname: \"job_list\",\n\t\tlabel: \"job_list\",\n\t\tdescription: \"List durable background jobs.\",\n\t\tparameters: Type.Object({}),\n\t\teffects: readEffects,\n\t\texecute: async () => {\n\t\t\tconst records = await registry.list();\n\t\t\tif (records.length === 0) return text(\"No background jobs.\");\n\t\t\treturn text(\n\t\t\t\trecords\n\t\t\t\t\t.map((r) => `[${r.jobId}] ${r.state} ${r.commandIdentity.slice(0, 80)} (pid ${r.processIdentity})`)\n\t\t\t\t\t.join(\"\\n\"),\n\t\t\t);\n\t\t},\n\t};\n\n\tconst logs: AgentTool<any> = {\n\t\tname: \"job_logs\",\n\t\tlabel: \"job_logs\",\n\t\tdescription: \"Tail bounded logs of a background job. Logs are untrusted content.\",\n\t\tparameters: Type.Object({\n\t\t\t...jobIdSchema,\n\t\t\ttailLines: Type.Optional(Type.Integer({ default: 200 })),\n\t\t\tmaxBytes: Type.Optional(Type.Integer({ default: 65536 })),\n\t\t\tstream: Type.Optional(Type.Union([Type.Literal(\"stdout\"), Type.Literal(\"stderr\"), Type.Literal(\"both\")])),\n\t\t}),\n\t\teffects: readEffects,\n\t\texecute: async (\n\t\t\t_id,\n\t\t\tparams: { jobId: string; tailLines?: number; maxBytes?: number; stream?: \"stdout\" | \"stderr\" | \"both\" },\n\t\t) => {\n\t\t\tconst logsResult = await registry.logs({\n\t\t\t\tjobId: params.jobId,\n\t\t\t\ttailLines: params.tailLines,\n\t\t\t\tmaxBytes: params.maxBytes,\n\t\t\t\tstream: params.stream,\n\t\t\t});\n\t\t\tconst parts: string[] = [];\n\t\t\tif (logsResult.stdout) parts.push(`[stdout]\\n${logsResult.stdout}`);\n\t\t\tif (logsResult.stderr) parts.push(`[stderr]\\n${logsResult.stderr}`);\n\t\t\tif (logsResult.truncated) parts.push(\"(logs truncated to bound)\");\n\t\t\treturn text(parts.join(\"\\n\\n\") || \"No log output.\");\n\t\t},\n\t};\n\n\tconst stop: AgentTool<any> = {\n\t\tname: \"job_stop\",\n\t\tlabel: \"job_stop\",\n\t\tdescription: \"Stop an owned background job; terminates the owned process tree after identity verification.\",\n\t\tparameters: Type.Object(jobIdSchema),\n\t\teffects: processEffects,\n\t\texecute: async (_id, params: { jobId: string }) => {\n\t\t\tconst rec = await registry.stop(params.jobId);\n\t\t\tif (!rec) return text(`Job ${params.jobId} not found.`);\n\t\t\treturn text(`Job ${rec.jobId} state=${rec.state}.`);\n\t\t},\n\t};\n\n\tconst restart: AgentTool<any> = {\n\t\tname: \"job_restart\",\n\t\tlabel: \"job_restart\",\n\t\tdescription: \"Restart a background job with a new process identity, preserving lineage.\",\n\t\tparameters: Type.Object(jobIdSchema),\n\t\teffects: processEffects,\n\t\texecute: async (_id, params: { jobId: string }) => {\n\t\t\tconst rec = await registry.restart(params.jobId);\n\t\t\tif (!rec) return text(`Job ${params.jobId} not found.`);\n\t\t\treturn text(`Job ${rec.jobId} restarted: new pid ${rec.processIdentity}, restartCount=${rec.restartCount}.`);\n\t\t},\n\t};\n\n\tconst adopt: AgentTool<any> = {\n\t\tname: \"job_adopt\",\n\t\tlabel: \"job_adopt\",\n\t\tdescription:\n\t\t\t\"Adopt an existing process as a managed job, only with strong identity evidence (executable, command line, cwd).\",\n\t\tparameters: Type.Object({\n\t\t\t...jobIdSchema,\n\t\t\texecutable: Type.String(),\n\t\t\tcommandLine: Type.Optional(Type.String()),\n\t\t\tstartTimeMs: Type.Optional(Type.Integer()),\n\t\t}),\n\t\teffects: processEffects,\n\t\texecute: async (\n\t\t\t_id,\n\t\t\tparams: { jobId: string; executable: string; commandLine?: string; startTimeMs?: number },\n\t\t) => {\n\t\t\tconst rec = await registry.adopt(params.jobId, {\n\t\t\t\texecutable: params.executable,\n\t\t\t\targuments: [],\n\t\t\t\tcwd: process.cwd(),\n\t\t\t\tcommandLine: params.commandLine,\n\t\t\t\tstartTimeMs: params.startTimeMs,\n\t\t\t});\n\t\t\tif (!rec) {\n\t\t\t\tawait registry.refuseAdoption(params.jobId);\n\t\t\t\treturn text(`Adoption refused: no matching identity evidence for job ${params.jobId}.`);\n\t\t\t}\n\t\t\treturn text(`Job ${rec.jobId} adopted (pid ${rec.processIdentity}).`);\n\t\t},\n\t};\n\n\treturn [start, status, list, logs, stop, restart, adopt];\n}\n\nexport type { AgentToolResult };\n"]}