@tea-agent/loop-agent 0.42.0-next.9 → 0.43.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -59
- package/dist/application/dag/run-dag.js +3 -2
- package/dist/application/task-lifecycle/advance.js +9 -4
- package/dist/build-stamp.json +3 -3
- package/dist/cli/program.js +1 -1
- package/dist/commands/dag-rerun-task.js +2 -0
- package/dist/commands/task-source-prepare.js +3 -1
- package/dist/executors/dag-pi-executor.js +1514 -175
- package/dist/executors/pi-extension-resolver.js +14 -2
- package/dist/executors/shell-executor.js +131 -54
- package/dist/shared/dag-failure-category.js +6 -0
- package/dist/task/contract/apply.js +36 -2
- package/dist/task/source-prepare/parse-intent.js +7 -0
- package/dist/worker/console/chat/chat-event-store.js +4 -2
- package/dist/worker/console/chat/pi-runtime.js +45 -2
- package/dist/worker/console/chat/resource-loader.js +4 -1
- package/dist/worker/console/chat/routes.js +28 -12
- package/dist/worker/console/chat/sdd-data-alignment.js +13 -2
- package/dist/worker/console/chat/session-store.js +5 -1
- package/dist/worker/console/chat/subagents/agent-tool.js +62 -0
- package/dist/worker/console/chat/subagents/explore-agent.js +169 -0
- package/dist/worker/console/chat/subagents/index.js +5 -0
- package/dist/worker/console/chat/subagents/orchestrator.js +273 -0
- package/dist/worker/console/chat/subagents/tool-policy.js +53 -0
- package/dist/worker/console/chat/subagents/types.js +13 -0
- package/dist/worker/console/chat/tool-preview.js +10 -0
- package/dist/worker/console/chat/tools.js +11 -1
- package/dist/worker/console/chat/turn-process.js +1 -0
- package/dist/worker/console/interview/tools.js +1 -0
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C6n9_m0P.js → abnfDiagram-N423BO3Z-B0Q0nClf.js} +1 -1
- package/dist/worker/console/static/assets/{arc-DQh-IfZ1.js → arc-DCPjC19G.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-54NnrwUC.js → architectureDiagram-T3A2C74G-CP5n9jmG.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-pivRALGK.js → blockDiagram-VBNYF7ZC-CmaWi_Wg.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BR7OV2NJ.js → c4Diagram-5PPSVZJV-Ct5tcMfZ.js} +1 -1
- package/dist/worker/console/static/assets/channel-DAS07MdS.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-B1Aq6BcK.js → chunk-2GRJ4B5K-C0osm1Zf.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-CbdU0rjo.js → chunk-2Q5K7J3B-CDWviED7.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-Dqi2GJeD.js → chunk-5RXB4S5H-mtSk1-j7.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BYn1Hu4R.js → chunk-5VM5RSS4-D5J9PHC7.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DRLjDL0k.js → chunk-6Q2QTUOP-Begg4WAa.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CY9Xx2jV.js → chunk-GF5L2VYU-sF1AEy7T.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BNCZs7_z.js → chunk-JWPE2WC7-BqbsWXZb.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DZ8AStLL.js → chunk-KBJHAD2P-BKJrPcJ6.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DsxjYrzz.js → chunk-RYQCIY6F-CcwNMRho.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-Ddx1KC1l.js → chunk-XXDRQBXY-BEJi3iIs.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Cwk-5jiW.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Cwk-5jiW.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-W9TveCnK.js → cose-bilkent-JH36ORCC-DRLgTvVu.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-l9j_ztZH.js → cynefin-VYW2F7L2-BoAVG3cs.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BMJMKi4G.js → cynefinDiagram-MW4NZA55-Bzol06hR.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-Bb4mG9pH.js → dagre-VZM6K2ZE-UmliJM77.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-BMgL_Qv0.js → diagram-7IWD3JNH-BWLcJkFo.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-Bvo2T4OQ.js → diagram-B4RE2ZJO-CgApOm-O.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-_5kWRN9c.js → diagram-LBJQPF4R-Bh3RgTLs.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DqdMrltM.js → diagram-Q27KOJAE-Dsh-D5nE.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-CDDYsNkp.js → diagram-UB23O5K3-Dkbbpcpb.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-c4nfnZUV.js → ebnfDiagram-BXEA7PRR-3EBaA3t3.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DnkUcNMs.js → erDiagram-JOGREHBK-Bq2Dc5ok.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-Dz0KGLZE.js → flowDiagram-UKHOOZJN-De7y55ha.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-Cj1t1uka.js → ganttDiagram-PKOTCBZU-fLiDbQRh.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-tp2FrBHd.js → gitGraphDiagram-DS77QQ5N-Du617mp1.js} +1 -1
- package/dist/worker/console/static/assets/index-C0O48S_P.js +449 -0
- package/dist/worker/console/static/assets/index-CzKf4U8P.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DQwJS8DH.js → infoDiagram-6WML65LV-BwmlF-XP.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-eimCQWD4.js → ishikawaDiagram-WSZJBQD7-zsGtS59u.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-Dd9Gbwgv.js → journeyDiagram-NVQOT4AX-DTTPTe3f.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Qve3cyft.js → kanban-definition-27J2QSJJ-BkXg9aP-.js} +1 -1
- package/dist/worker/console/static/assets/{linear-UXKSe36Z.js → linear-7U2ue5IE.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-CWhj4JXN.js → mermaid.core-BUuGHmWO.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-yTcwf4SJ.js → mindmap-definition-FAOFIHXS-7q6fX0Sv.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-_7qToaZP.js → pegDiagram-VL7TDLO6-CkQPJN55.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CXmrKmDm.js → pieDiagram-7S7Q4E2Y-CCal_tfX.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-hMmrJyaS.js → quadrantDiagram-CIZ2JOQS-B7bpxyAZ.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BXw8tCBe.js → railroadDiagram-AXF67PYL-CJmww7D-.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BmjgnLZl.js → requirementDiagram-LRYGKXZP-D6ldWJEv.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Bj77SPpN.js → sankeyDiagram-W5VNT64P-VGh33I9e.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-B2FDVysL.js → sequenceDiagram-SI44F4Z6-CCkDrIjU.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-UChNY9ZZ.js → sizeCapture-X5ZJPWSS-NC8f5Otb.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-wbAEkbd7.js → stateDiagram-OKZ733FA-_92ezZdF.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DyJg5gzW.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DCEkU8HR.js → swimlanes-SLNWSIFB-BdUCGtbP.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-B1cw_0Uy.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-BGp7H06U.js → timeline-definition-Z64GVDOM-CdD1H8ia.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DsCIQuWR.js → vennDiagram-T6HMQDX7-Dkdk3oFo.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-cRZLP4Xe.js → wardleyDiagram-T6FBY63Y-CKu2uPPY.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CP0xLR9Y.js → xychartDiagram-ELKLHX3M-ClqAnuG8.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/chat-view-types.js +1 -1
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +23 -0
- package/dist/worker/console/static-src/operator-chat/pending-user-message.js +32 -0
- package/dist/worker/console/static-src/operator-chat/tools-catalog.js +21 -2
- package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +2 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +2 -0
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +30 -14
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +133 -5
- package/dist/worker/console/static-src/shell/workspace-route.js +4 -0
- package/dist/worker/observe/static/constants.js +22 -22
- package/dist/worker/observe/static/dag-context-reason-labels.js +19 -0
- package/dist/worker/observe/static/dag-history-labels.js +1 -0
- package/dist/worker/observe/static/dag-inspector-humanize.d.ts +16 -0
- package/dist/worker/observe/static/dag-inspector-humanize.js +339 -0
- package/dist/worker/observe/static/dag-node-purpose.js +10 -10
- package/dist/worker/observe/static/index.html +4 -4
- package/dist/worker/observe/static/prompt-restart-candidates.js +4 -2
- package/dist/worker/observe/static/relations.js +5 -5
- package/dist/worker/observe/static/styles.css +171 -0
- package/dist/worker/observe/static/views/dag-graph.js +1 -1
- package/dist/worker/observe/static/views/dag-inspector.js +329 -174
- package/dist/worker/observe/static/views/dag.js +16 -12
- package/dist/worker/observe/static/views/session-timeline.js +5 -3
- package/dist/workflows/dag/backend-test-plan-protocol.js +104 -0
- package/dist/workflows/dag/dag-retry-schema.js +3 -0
- package/dist/workflows/dag/frontend-contract-facts.js +130 -0
- package/dist/workflows/dag/frontend-design-policy.js +101 -16
- package/dist/workflows/dag/frontend-implementation-contract.js +410 -41
- package/dist/workflows/dag/frontend-plan-render.js +13 -2
- package/dist/workflows/dag/frontend-prewrite-gate.js +1 -1
- package/dist/workflows/dag/frontend-recovery-controller.js +25 -1
- package/dist/workflows/dag/frontend-recovery-plan.js +4 -1
- package/dist/workflows/dag/frontend-recovery-run.js +142 -16
- package/dist/workflows/dag/frontend-risk.js +2 -0
- package/dist/workflows/dag/frontend-shadow-dual-write.js +17 -1
- package/dist/workflows/dag/frontend-shape.js +16 -6
- package/dist/workflows/dag/frontend-test-execution-evidence.js +48 -0
- package/dist/workflows/dag/frontend-typed-event-store.js +3 -0
- package/dist/workflows/dag/frontend-verification-trace.js +38 -4
- package/dist/workflows/dag/frontend-writer-admission.js +2 -2
- package/dist/workflows/dag/init-hybrid.js +57 -28
- package/dist/workflows/dag/node-execution.js +201 -85
- package/dist/workflows/dag/recovery-lease.js +106 -16
- package/dist/workflows/dag/rerun-feedback.js +1 -0
- package/dist/workflows/dag/rerun-plan.js +90 -4
- package/dist/workflows/dag/rerun-run.js +28 -6
- package/dist/workflows/dag/rerun-task.js +221 -18
- package/dist/workflows/dag/retry-policy.js +27 -10
- package/dist/workflows/dag/runner-exit-diagnostics.js +125 -0
- package/dist/workflows/dag/runner.js +220 -18
- package/dist/workflows/dag/structured-output-repair.js +4 -1
- package/dist/workflows/dag/types.js +4 -3
- package/dist/workflows/dag/validate.js +10 -8
- package/dist/workflows/dag/workspace-checkpoint.js +66 -0
- package/docs/operations/README.md +1 -0
- package/docs/templates/agent-dag.schema.json +2 -2
- package/docs/templates/backend-test-dag.json +6 -4
- package/docs/templates/frontend-implementation-contract.schema.json +34 -2
- package/harness.json +1 -1
- package/package.json +4 -5
- package/skills/frontend-plan/SKILL.md +14 -1
- package/skills/frontend-plan/references/decision-contract.md +101 -5
- package/skills/frontend-plan/references/design-decisions.md +32 -0
- package/dist/worker/console/static/assets/channel-DsZxrgqe.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-CuToPeGV.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-CuToPeGV.js +0 -1
- package/dist/worker/console/static/assets/index-D9Sc0f0y.js +0 -449
- package/dist/worker/console/static/assets/index-DDQc5a50.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-yPoT19ft.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-VWdarchG.js +0 -8
|
@@ -2,6 +2,12 @@ import { randomBytes } from "node:crypto";
|
|
|
2
2
|
import { readFile, stat } from "node:fs/promises";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
|
+
export const SDD_SKILL_NAMES = [
|
|
6
|
+
"SDD-design-analysis",
|
|
7
|
+
"SDD-implementation-test-review",
|
|
8
|
+
"SDD-requirement-analysis",
|
|
9
|
+
];
|
|
10
|
+
/** Backward-compatible name for callers that need a stable representative value. */
|
|
5
11
|
export const SDD_DEFAULT_SKILL_NAME = "SDD-implementation-test-review";
|
|
6
12
|
export const SDD_TELEMETRY_URL = "https://wizard-ai.paic.com.cn/code_pilot/api/skill-usage-log";
|
|
7
13
|
export const SDD_TELEMETRY_SIGN = "52aad0c09e6b46818a11ee702d6fe0ba";
|
|
@@ -80,12 +86,16 @@ export class SddDataAlignmentError extends Error {
|
|
|
80
86
|
this.name = "SddDataAlignmentError";
|
|
81
87
|
}
|
|
82
88
|
}
|
|
83
|
-
export function
|
|
89
|
+
export function selectRandomSddSkillName(random = Math.random) {
|
|
90
|
+
const index = Math.min(SDD_SKILL_NAMES.length - 1, Math.max(0, Math.floor(random() * SDD_SKILL_NAMES.length)));
|
|
91
|
+
return SDD_SKILL_NAMES[index];
|
|
92
|
+
}
|
|
93
|
+
export function normalizeSddSkillName(value, random = Math.random) {
|
|
84
94
|
if (value !== undefined && typeof value !== "string") {
|
|
85
95
|
throw new SddDataAlignmentError("SDD_INVALID_SKILL_NAME", "skillName 必须是字符串", 400);
|
|
86
96
|
}
|
|
87
97
|
const raw = value ?? "";
|
|
88
|
-
const normalized = raw.trim() ||
|
|
98
|
+
const normalized = raw.trim() || selectRandomSddSkillName(random);
|
|
89
99
|
if ([...normalized].length > 256 || /[\u0000-\u001f\u007f]/u.test(raw)) {
|
|
90
100
|
throw new SddDataAlignmentError("SDD_INVALID_SKILL_NAME", "skillName 不能超过 256 个字符或包含控制字符", 400);
|
|
91
101
|
}
|
|
@@ -149,6 +159,7 @@ export class SddDataAlignmentService {
|
|
|
149
159
|
if (!response.ok || body?.ret !== 0 || body.succ !== true) {
|
|
150
160
|
throw new SddDataAlignmentError("SDD_UPSTREAM_FAILED", "SDD 数据对齐上报失败", 502);
|
|
151
161
|
}
|
|
162
|
+
return { skillName };
|
|
152
163
|
}
|
|
153
164
|
}
|
|
154
165
|
async function readBoundedTelemetryJson(response) {
|
|
@@ -589,7 +589,11 @@ export async function runChatTurn(options) {
|
|
|
589
589
|
};
|
|
590
590
|
}
|
|
591
591
|
if (options.persistUserMessage !== false) {
|
|
592
|
-
const userMessage = await store.appendMessage(sessionId, {
|
|
592
|
+
const userMessage = await store.appendMessage(sessionId, {
|
|
593
|
+
role: "user",
|
|
594
|
+
text,
|
|
595
|
+
...(options.clientRequestId ? { clientRequestId: options.clientRequestId } : {}),
|
|
596
|
+
});
|
|
593
597
|
const afterUserMessage = await store.get(sessionId);
|
|
594
598
|
if (afterUserMessage)
|
|
595
599
|
await options.onUserMessagePersisted?.(afterUserMessage, userMessage);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parent-session Agent custom tool. First slice: Explore only, one-shot.
|
|
3
|
+
*/
|
|
4
|
+
import { AGENT_TOOL_DESCRIPTION, AGENT_TOOL_PROMPT_GUIDELINES, AGENT_TOOL_PROMPT_SNIPPET, parseExploreAgentParams, } from "./explore-agent.js";
|
|
5
|
+
import { OPERATOR_CHAT_AGENT_TOOL_NAME } from "./types.js";
|
|
6
|
+
function textResult(payload, isError = false) {
|
|
7
|
+
return {
|
|
8
|
+
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
9
|
+
details: payload,
|
|
10
|
+
...(isError ? { isError: true } : {}),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export async function buildAgentCustomTool(options) {
|
|
14
|
+
const [{ Type }, { defineTool }] = await Promise.all([
|
|
15
|
+
import("typebox"),
|
|
16
|
+
import("@earendil-works/pi-coding-agent"),
|
|
17
|
+
]);
|
|
18
|
+
return defineTool({
|
|
19
|
+
name: OPERATOR_CHAT_AGENT_TOOL_NAME,
|
|
20
|
+
label: "Explore",
|
|
21
|
+
description: AGENT_TOOL_DESCRIPTION,
|
|
22
|
+
promptSnippet: AGENT_TOOL_PROMPT_SNIPPET,
|
|
23
|
+
promptGuidelines: [...AGENT_TOOL_PROMPT_GUIDELINES],
|
|
24
|
+
executionMode: "parallel",
|
|
25
|
+
parameters: Type.Object({
|
|
26
|
+
description: Type.String({
|
|
27
|
+
description: "Short 3-5 word summary shown on the tool card",
|
|
28
|
+
}),
|
|
29
|
+
prompt: Type.String({
|
|
30
|
+
description: "Complete Explore brief. Include goal, known clues, exclusions, success criteria, and thoroughness (quick | medium | very thorough).",
|
|
31
|
+
}),
|
|
32
|
+
subagent_type: Type.Optional(Type.String({
|
|
33
|
+
description: "Built-in agent name. Omit or set Explore.",
|
|
34
|
+
})),
|
|
35
|
+
}, { additionalProperties: false }),
|
|
36
|
+
async execute(_toolCallId, params, signal) {
|
|
37
|
+
const parsed = parseExploreAgentParams(params);
|
|
38
|
+
if (!parsed.ok) {
|
|
39
|
+
const failure = {
|
|
40
|
+
schemaVersion: 1,
|
|
41
|
+
kind: "subagent",
|
|
42
|
+
agent: "Explore",
|
|
43
|
+
status: "failed",
|
|
44
|
+
description: typeof params.description === "string" ? params.description : "",
|
|
45
|
+
error: parsed.message,
|
|
46
|
+
runId: "",
|
|
47
|
+
};
|
|
48
|
+
return textResult({
|
|
49
|
+
...failure,
|
|
50
|
+
code: parsed.code,
|
|
51
|
+
}, true);
|
|
52
|
+
}
|
|
53
|
+
const result = await options.orchestrator.launch({
|
|
54
|
+
parentSessionId: options.parentSessionId,
|
|
55
|
+
request: parsed.request,
|
|
56
|
+
signal,
|
|
57
|
+
model: options.resolveModel?.(),
|
|
58
|
+
});
|
|
59
|
+
return textResult(result, result.status !== "completed");
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in Explore role: parameter validation + Chinese system prompt.
|
|
3
|
+
* Frontmatter `tools: "*"` / `shellPolicy: unrestricted` are role metadata
|
|
4
|
+
* only — runtime tool args come from tool-policy.ts.
|
|
5
|
+
*/
|
|
6
|
+
import { EXPLORE_AGENT_NAME, EXPLORE_RESULT_KIND, EXPLORE_RESULT_MAX_BYTES, EXPLORE_RESULT_MAX_LINES, EXPLORE_RESULT_SCHEMA_VERSION, } from "./types.js";
|
|
7
|
+
const FORBIDDEN_OPTION_KEYS = [
|
|
8
|
+
"tasks",
|
|
9
|
+
"model",
|
|
10
|
+
"run_in_background",
|
|
11
|
+
"isolation",
|
|
12
|
+
"cwd",
|
|
13
|
+
"name",
|
|
14
|
+
];
|
|
15
|
+
export const EXPLORE_SYSTEM_PROMPT = [
|
|
16
|
+
"你是运行在 Pi 中的文件搜索和代码库探索专家。请按照用户要求的探索深度,彻底浏览仓库,并尽快返回基于证据的答案。",
|
|
17
|
+
"",
|
|
18
|
+
"=== 关键要求:只读模式,不得修改任何内容 ===",
|
|
19
|
+
"",
|
|
20
|
+
"这是一个只读探索任务。你严格禁止:",
|
|
21
|
+
"",
|
|
22
|
+
"- 创建、编辑、删除、移动或复制文件;",
|
|
23
|
+
"- 创建临时文件;",
|
|
24
|
+
"- 使用 shell 重定向、命令替换,或任何会改变仓库或系统状态的命令;",
|
|
25
|
+
"- 安装依赖或执行 Git 写操作。",
|
|
26
|
+
"",
|
|
27
|
+
"Pi 工具在技术上可能对你可见,但这不代表你获得了修改权限。只读行为是严格的角色要求。Console runtime 不会向你提供 `edit`、`write` 或 `bash`;即使出现可变工具,你也必须保持非修改行为。你的职责仅限于搜索、阅读和分析现有代码。",
|
|
28
|
+
"",
|
|
29
|
+
"你的优势包括:",
|
|
30
|
+
"",
|
|
31
|
+
"- 使用 find 和 ls 查找文件;",
|
|
32
|
+
"- 使用 safe-grep 搜索代码和文本;",
|
|
33
|
+
"- 使用 safe-read 阅读并分析相关文件;",
|
|
34
|
+
"- 使用 git-status / git-diff 做只读仓库检查;",
|
|
35
|
+
"- 追踪符号、调用者、数据流、配置、测试和集成点。",
|
|
36
|
+
"",
|
|
37
|
+
"请根据用户要求的探索深度调整工作量:",
|
|
38
|
+
"",
|
|
39
|
+
"- **quick**:定位最可能的文件、符号或定义,并直接回答;",
|
|
40
|
+
"- **medium**:检查主要实现、重要调用方和附近测试;",
|
|
41
|
+
"- **very thorough**:搜索多种命名约定和目录位置,追踪数据流和集成关系,检查测试与配置,并协调相互冲突的证据。",
|
|
42
|
+
"",
|
|
43
|
+
"工作指南:",
|
|
44
|
+
"",
|
|
45
|
+
"- 当位置不确定时,先从宽范围开始,再直接读取最相关的文件;",
|
|
46
|
+
"- 知道具体路径时使用 safe-read;",
|
|
47
|
+
"- 只做只读检查;git 工具是固定 argv,不是任意 shell;",
|
|
48
|
+
"- 根据请求的探索深度调整搜索策略;第一次查询不足时切换方法;",
|
|
49
|
+
"- 高效使用彼此独立的 grep、find、ls 和 read 操作;可能时并行执行独立搜索;",
|
|
50
|
+
"- 在 medium 或 very thorough 探索中,每完成一个有意义的阶段,以及每使用约 8–12 轮工具后,输出一到两句简短阶段说明。说明当前已经确认的事实、下一步准备检查的内容以及是否存在阻塞,然后继续工作。不要等到最终报告时才暴露所有进展;",
|
|
51
|
+
"- 在最终回答中汇报发现,不要创建报告文件。",
|
|
52
|
+
"",
|
|
53
|
+
"返回清晰的发现结果,并提供相关绝对路径和行号范围。将已确认事实与推断分开,并明确指出尚未解决的问题。",
|
|
54
|
+
].join("\n");
|
|
55
|
+
export const AGENT_TOOL_DESCRIPTION = "Start the built-in read-only Explore subagent for codebase search, symbol lookup, and call-chain tracing. One call launches one Fresh one-shot Explore session. Do not pass tasks, model, cwd, isolation, or run_in_background.";
|
|
56
|
+
export const AGENT_TOOL_PROMPT_SNIPPET = "Agent: launch built-in read-only Explore (Fresh, one-shot). Not for known single-file reads.";
|
|
57
|
+
export const AGENT_TOOL_PROMPT_GUIDELINES = Object.freeze([
|
|
58
|
+
"Delegate codebase exploration, cross-directory search, call-chain tracing, and path-uncertain repo questions to Agent (Explore).",
|
|
59
|
+
"Brief must include the goal, known clues, exclusions, success criteria, and thoroughness (quick | medium | very thorough).",
|
|
60
|
+
"Do not delegate a known single-file read or single-symbol lookup; use read/grep/find directly.",
|
|
61
|
+
"Call Agent multiple times in one turn for non-overlapping questions; do not pack unrelated questions into one prompt. Do not pass tasks.",
|
|
62
|
+
"Treat Explore output as advisory evidence; synthesize before answering. Never delegate DAG writes, Human Gate, or operator mutations.",
|
|
63
|
+
]);
|
|
64
|
+
function asRecord(value) {
|
|
65
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
66
|
+
return undefined;
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
function readTrimmedString(value) {
|
|
70
|
+
if (typeof value !== "string")
|
|
71
|
+
return undefined;
|
|
72
|
+
const trimmed = value.trim();
|
|
73
|
+
return trimmed ? trimmed : undefined;
|
|
74
|
+
}
|
|
75
|
+
function detectThoroughness(prompt) {
|
|
76
|
+
const lower = prompt.toLowerCase();
|
|
77
|
+
if (/\bvery\s+thorough\b/.test(lower) || lower.includes("非常彻底")) {
|
|
78
|
+
return "very thorough";
|
|
79
|
+
}
|
|
80
|
+
if (/\bmedium\b/.test(lower) || lower.includes("中等"))
|
|
81
|
+
return "medium";
|
|
82
|
+
if (/\bquick\b/.test(lower) || lower.includes("快速"))
|
|
83
|
+
return "quick";
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
export function parseExploreAgentParams(raw) {
|
|
87
|
+
const record = asRecord(raw);
|
|
88
|
+
if (!record) {
|
|
89
|
+
return {
|
|
90
|
+
ok: false,
|
|
91
|
+
code: "invalid-params",
|
|
92
|
+
message: "Agent requires an object with description and prompt",
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
for (const key of FORBIDDEN_OPTION_KEYS) {
|
|
96
|
+
if (!Object.prototype.hasOwnProperty.call(record, key))
|
|
97
|
+
continue;
|
|
98
|
+
const value = record[key];
|
|
99
|
+
if (value === undefined || value === null)
|
|
100
|
+
continue;
|
|
101
|
+
if (key === "tasks") {
|
|
102
|
+
return {
|
|
103
|
+
ok: false,
|
|
104
|
+
code: "tasks-not-supported",
|
|
105
|
+
message: "Agent tasks[] fan-out is not supported in this slice; call Agent once per independent Explore",
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
ok: false,
|
|
110
|
+
code: "unsupported-option",
|
|
111
|
+
message: `Agent option "${key}" is not supported for Console Explore`,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
const description = readTrimmedString(record.description);
|
|
115
|
+
const prompt = readTrimmedString(record.prompt);
|
|
116
|
+
if (!description || !prompt) {
|
|
117
|
+
return {
|
|
118
|
+
ok: false,
|
|
119
|
+
code: "invalid-params",
|
|
120
|
+
message: "Agent requires non-empty description and prompt",
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const subagentRaw = record.subagent_type ?? record.subagentType;
|
|
124
|
+
const subagentType = readTrimmedString(subagentRaw) ?? EXPLORE_AGENT_NAME;
|
|
125
|
+
if (subagentType !== EXPLORE_AGENT_NAME) {
|
|
126
|
+
return {
|
|
127
|
+
ok: false,
|
|
128
|
+
code: "unknown-subagent-type",
|
|
129
|
+
message: `unknown subagent_type "${subagentType}"; only Explore is built in`,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
const thoroughness = detectThoroughness(prompt);
|
|
133
|
+
const request = {
|
|
134
|
+
description,
|
|
135
|
+
prompt,
|
|
136
|
+
subagentType: EXPLORE_AGENT_NAME,
|
|
137
|
+
...(thoroughness ? { thoroughness } : {}),
|
|
138
|
+
};
|
|
139
|
+
return { ok: true, request };
|
|
140
|
+
}
|
|
141
|
+
export function boundExploreText(text) {
|
|
142
|
+
const lines = text.split(/\r?\n/);
|
|
143
|
+
const clipped = lines.length > EXPLORE_RESULT_MAX_LINES
|
|
144
|
+
? `${lines.slice(0, EXPLORE_RESULT_MAX_LINES).join("\n")}\n…[truncated ${lines.length - EXPLORE_RESULT_MAX_LINES} lines]`
|
|
145
|
+
: text;
|
|
146
|
+
if (Buffer.byteLength(clipped, "utf8") <= EXPLORE_RESULT_MAX_BYTES) {
|
|
147
|
+
return clipped;
|
|
148
|
+
}
|
|
149
|
+
let out = clipped;
|
|
150
|
+
while (Buffer.byteLength(out, "utf8") > EXPLORE_RESULT_MAX_BYTES && out.length > 0) {
|
|
151
|
+
out = out.slice(0, Math.max(0, Math.floor(out.length * 0.9)));
|
|
152
|
+
}
|
|
153
|
+
return `${out}\n…[truncated to ${EXPLORE_RESULT_MAX_BYTES} bytes]`;
|
|
154
|
+
}
|
|
155
|
+
export function buildExploreResult(input) {
|
|
156
|
+
return {
|
|
157
|
+
schemaVersion: EXPLORE_RESULT_SCHEMA_VERSION,
|
|
158
|
+
kind: EXPLORE_RESULT_KIND,
|
|
159
|
+
agent: EXPLORE_AGENT_NAME,
|
|
160
|
+
status: input.status,
|
|
161
|
+
description: input.description,
|
|
162
|
+
runId: input.runId,
|
|
163
|
+
...(input.result !== undefined
|
|
164
|
+
? { result: boundExploreText(input.result) }
|
|
165
|
+
: {}),
|
|
166
|
+
...(input.error !== undefined ? { error: input.error } : {}),
|
|
167
|
+
...(input.usage ? { usage: input.usage } : {}),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { AGENT_TOOL_DESCRIPTION, AGENT_TOOL_PROMPT_GUIDELINES, AGENT_TOOL_PROMPT_SNIPPET, EXPLORE_SYSTEM_PROMPT, boundExploreText, buildExploreResult, parseExploreAgentParams, } from "./explore-agent.js";
|
|
2
|
+
export { buildAgentCustomTool } from "./agent-tool.js";
|
|
3
|
+
export { ExploreOrchestrator } from "./orchestrator.js";
|
|
4
|
+
export { EXPLORE_ALLOWED_TOOLS, EXPLORE_CHILD_RESOURCE_LOADER_OPTIONS, EXPLORE_EXCLUDE_TOOLS, extrasOutsideExploreAllowlist, filterExploreCustomTools, isExploreAllowedTool, pinExploreToolNames, } from "./tool-policy.js";
|
|
5
|
+
export { EXPLORE_AGENT_NAME, OPERATOR_CHAT_AGENT_TOOL_NAME, } from "./types.js";
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fresh one-shot Explore child sessions for Operator Chat.
|
|
3
|
+
*
|
|
4
|
+
* Registry is a Map (never a singleton currentExplore) so two Agent calls in
|
|
5
|
+
* the same parent turn stay independently abortable. Child sessions use an
|
|
6
|
+
* in-memory SessionManager and a closed resource surface: no extensions,
|
|
7
|
+
* no operator tools, no write/bash.
|
|
8
|
+
*/
|
|
9
|
+
import { randomBytes } from "node:crypto";
|
|
10
|
+
import { extractAssistantText } from "../assistant-content.js";
|
|
11
|
+
import { buildExploreResult, EXPLORE_SYSTEM_PROMPT, } from "./explore-agent.js";
|
|
12
|
+
import { EXPLORE_ALLOWED_TOOLS, EXPLORE_CHILD_RESOURCE_LOADER_OPTIONS, EXPLORE_EXCLUDE_TOOLS, extrasOutsideExploreAllowlist, filterExploreCustomTools, pinExploreToolNames, } from "./tool-policy.js";
|
|
13
|
+
const EXPLORE_IDLE_TIMEOUT_MS = 180_000;
|
|
14
|
+
function newExploreRunId() {
|
|
15
|
+
return `explore-${randomBytes(8).toString("hex")}`;
|
|
16
|
+
}
|
|
17
|
+
async function waitForChildIdle(session, signal) {
|
|
18
|
+
const start = Date.now();
|
|
19
|
+
while (true) {
|
|
20
|
+
if (signal?.aborted)
|
|
21
|
+
return { timedOut: false };
|
|
22
|
+
if (session.isIdle === true)
|
|
23
|
+
return { timedOut: false };
|
|
24
|
+
if (session.isStreaming === false)
|
|
25
|
+
return { timedOut: false };
|
|
26
|
+
if (Date.now() - start > EXPLORE_IDLE_TIMEOUT_MS)
|
|
27
|
+
return { timedOut: true };
|
|
28
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export class ExploreOrchestrator {
|
|
32
|
+
options;
|
|
33
|
+
runs = new Map();
|
|
34
|
+
constructor(options) {
|
|
35
|
+
this.options = options;
|
|
36
|
+
}
|
|
37
|
+
liveCount() {
|
|
38
|
+
return this.runs.size;
|
|
39
|
+
}
|
|
40
|
+
liveRunIds() {
|
|
41
|
+
return [...this.runs.keys()];
|
|
42
|
+
}
|
|
43
|
+
async abortAll() {
|
|
44
|
+
const live = [...this.runs.values()];
|
|
45
|
+
await Promise.all(live.map((run) => run.abort().catch(() => undefined)));
|
|
46
|
+
}
|
|
47
|
+
/** Abort only the runs launched by one parent session (Stop must never
|
|
48
|
+
* kill another session's live Explore — exec-plan acceptance #7). */
|
|
49
|
+
async abortByParent(parentSessionId) {
|
|
50
|
+
const mine = [...this.runs.values()].filter((run) => run.parentSessionId === parentSessionId);
|
|
51
|
+
await Promise.all(mine.map((run) => run.abort().catch(() => undefined)));
|
|
52
|
+
}
|
|
53
|
+
async launch(input) {
|
|
54
|
+
if (input.signal?.aborted) {
|
|
55
|
+
return buildExploreResult({
|
|
56
|
+
status: "aborted",
|
|
57
|
+
description: input.request.description,
|
|
58
|
+
runId: newExploreRunId(),
|
|
59
|
+
error: "Explore aborted before start",
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
const runId = newExploreRunId();
|
|
63
|
+
const { cwd, bindings } = this.options;
|
|
64
|
+
let session;
|
|
65
|
+
let unsub;
|
|
66
|
+
let output = "";
|
|
67
|
+
let turns = 0;
|
|
68
|
+
let toolCalls = 0;
|
|
69
|
+
let promptError;
|
|
70
|
+
let abortPromise;
|
|
71
|
+
let settled = false;
|
|
72
|
+
let idleTimedOut = false;
|
|
73
|
+
/** True once abortChild ran — an abort without a parent signal (dispose
|
|
74
|
+
* path) must still converge to status=aborted, never a hollow
|
|
75
|
+
* "completed" with whatever partial text the SDK flushed. */
|
|
76
|
+
let childAborted = false;
|
|
77
|
+
const disposeChild = async () => {
|
|
78
|
+
unsub?.();
|
|
79
|
+
unsub = undefined;
|
|
80
|
+
try {
|
|
81
|
+
await session?.abort?.();
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// ignore
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
session?.dispose();
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// ignore
|
|
91
|
+
}
|
|
92
|
+
session = undefined;
|
|
93
|
+
this.runs.delete(runId);
|
|
94
|
+
};
|
|
95
|
+
const abortChild = async () => {
|
|
96
|
+
if (abortPromise) {
|
|
97
|
+
await abortPromise;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
childAborted = true;
|
|
101
|
+
abortPromise = (async () => {
|
|
102
|
+
try {
|
|
103
|
+
await session?.abort?.();
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// ignore
|
|
107
|
+
}
|
|
108
|
+
})();
|
|
109
|
+
await abortPromise;
|
|
110
|
+
};
|
|
111
|
+
this.runs.set(runId, {
|
|
112
|
+
runId,
|
|
113
|
+
parentSessionId: input.parentSessionId,
|
|
114
|
+
description: input.request.description,
|
|
115
|
+
status: "starting",
|
|
116
|
+
startedAt: Date.now(),
|
|
117
|
+
abort: abortChild,
|
|
118
|
+
});
|
|
119
|
+
const onParentAbort = () => {
|
|
120
|
+
void abortChild();
|
|
121
|
+
};
|
|
122
|
+
input.signal?.addEventListener("abort", onParentAbort);
|
|
123
|
+
try {
|
|
124
|
+
if (!bindings.createInMemorySessionManager) {
|
|
125
|
+
return buildExploreResult({
|
|
126
|
+
status: "failed",
|
|
127
|
+
description: input.request.description,
|
|
128
|
+
runId,
|
|
129
|
+
error: "Explore requires an in-memory SessionManager",
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
const agentDir = await bindings.getAgentDir();
|
|
133
|
+
const { services } = await bindings.createServices({
|
|
134
|
+
cwd,
|
|
135
|
+
agentDir,
|
|
136
|
+
resourceLoaderOptions: {
|
|
137
|
+
...EXPLORE_CHILD_RESOURCE_LOADER_OPTIONS,
|
|
138
|
+
systemPrompt: EXPLORE_SYSTEM_PROMPT,
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
const sessionManager = await bindings.createInMemorySessionManager({
|
|
142
|
+
cwd,
|
|
143
|
+
});
|
|
144
|
+
const safeTools = await this.options.loadSafeExploreCustomTools();
|
|
145
|
+
const customTools = filterExploreCustomTools(safeTools);
|
|
146
|
+
const resolvedModel = input.model ??
|
|
147
|
+
((await this.options.resolveModel?.(services.modelRuntime)) ??
|
|
148
|
+
undefined);
|
|
149
|
+
const created = await bindings.createSessionFromServices({
|
|
150
|
+
services,
|
|
151
|
+
sessionManager,
|
|
152
|
+
tools: [...EXPLORE_ALLOWED_TOOLS],
|
|
153
|
+
excludeTools: [...EXPLORE_EXCLUDE_TOOLS],
|
|
154
|
+
customTools,
|
|
155
|
+
...(resolvedModel ? { model: resolvedModel } : {}),
|
|
156
|
+
});
|
|
157
|
+
session = created.session;
|
|
158
|
+
session.setActiveToolsByName(pinExploreToolNames());
|
|
159
|
+
const leaked = extrasOutsideExploreAllowlist(session.getActiveToolNames());
|
|
160
|
+
if (leaked.length > 0) {
|
|
161
|
+
return buildExploreResult({
|
|
162
|
+
status: "failed",
|
|
163
|
+
description: input.request.description,
|
|
164
|
+
runId,
|
|
165
|
+
error: `Explore child received forbidden tools: ${leaked.join(", ")}`,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
const live = this.runs.get(runId);
|
|
169
|
+
if (live)
|
|
170
|
+
live.status = "working";
|
|
171
|
+
unsub = session.subscribe((event) => {
|
|
172
|
+
if (event.type === "agent_start")
|
|
173
|
+
turns += 1;
|
|
174
|
+
if (event.type === "tool_execution_start")
|
|
175
|
+
toolCalls += 1;
|
|
176
|
+
if (event.type === "message_end") {
|
|
177
|
+
output = extractAssistantText(event.message);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
if (input.signal?.aborted) {
|
|
181
|
+
await abortChild();
|
|
182
|
+
return buildExploreResult({
|
|
183
|
+
status: "aborted",
|
|
184
|
+
description: input.request.description,
|
|
185
|
+
runId,
|
|
186
|
+
error: "Explore aborted",
|
|
187
|
+
usage: { turns, toolCalls },
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
await session.prompt(input.request.prompt, {
|
|
192
|
+
signal: input.signal,
|
|
193
|
+
});
|
|
194
|
+
const idle = await waitForChildIdle(session, input.signal);
|
|
195
|
+
idleTimedOut = idle.timedOut;
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
if (input.signal?.aborted) {
|
|
199
|
+
promptError = "Explore aborted";
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
promptError = error instanceof Error ? error.message : String(error);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (abortPromise)
|
|
206
|
+
await abortPromise.catch(() => undefined);
|
|
207
|
+
settled = true;
|
|
208
|
+
if (childAborted || input.signal?.aborted) {
|
|
209
|
+
return buildExploreResult({
|
|
210
|
+
status: "aborted",
|
|
211
|
+
description: input.request.description,
|
|
212
|
+
runId,
|
|
213
|
+
error: "Explore aborted",
|
|
214
|
+
usage: { turns, toolCalls },
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
if (promptError) {
|
|
218
|
+
return buildExploreResult({
|
|
219
|
+
status: "failed",
|
|
220
|
+
description: input.request.description,
|
|
221
|
+
runId,
|
|
222
|
+
error: promptError,
|
|
223
|
+
usage: { turns, toolCalls },
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
if (idleTimedOut) {
|
|
227
|
+
// The turn itself finished but a continuation (auto-compaction /
|
|
228
|
+
// follow-up) never settled within the idle budget. Never report a
|
|
229
|
+
// partial transcript as a clean completion.
|
|
230
|
+
return buildExploreResult({
|
|
231
|
+
status: "failed",
|
|
232
|
+
description: input.request.description,
|
|
233
|
+
runId,
|
|
234
|
+
error: `Explore idle timeout after ${EXPLORE_IDLE_TIMEOUT_MS / 1000}s`,
|
|
235
|
+
usage: { turns, toolCalls },
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
return buildExploreResult({
|
|
239
|
+
status: "completed",
|
|
240
|
+
description: input.request.description,
|
|
241
|
+
runId,
|
|
242
|
+
result: output,
|
|
243
|
+
usage: { turns, toolCalls },
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
const aborted = childAborted || Boolean(input.signal?.aborted);
|
|
248
|
+
let message;
|
|
249
|
+
if (aborted) {
|
|
250
|
+
message = "Explore aborted";
|
|
251
|
+
}
|
|
252
|
+
else if (error instanceof Error) {
|
|
253
|
+
message = error.message;
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
message = String(error);
|
|
257
|
+
}
|
|
258
|
+
return buildExploreResult({
|
|
259
|
+
status: aborted ? "aborted" : "failed",
|
|
260
|
+
description: input.request.description,
|
|
261
|
+
runId,
|
|
262
|
+
error: message,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
finally {
|
|
266
|
+
input.signal?.removeEventListener("abort", onParentAbort);
|
|
267
|
+
if (!settled && abortPromise) {
|
|
268
|
+
await abortPromise.catch(() => undefined);
|
|
269
|
+
}
|
|
270
|
+
await disposeChild();
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mechanical Explore child tool surface. Role prompts are not a sandbox:
|
|
3
|
+
* createSessionFromServices({ tools, excludeTools }) plus Gate-2 pin must
|
|
4
|
+
* actually withhold write/bash/operator/extension tools.
|
|
5
|
+
*/
|
|
6
|
+
export const EXPLORE_ALLOWED_BUILTIN_TOOLS = Object.freeze(["find", "ls"]);
|
|
7
|
+
export const EXPLORE_ALLOWED_CUSTOM_TOOLS = Object.freeze([
|
|
8
|
+
"safe-read",
|
|
9
|
+
"safe-grep",
|
|
10
|
+
"git-status",
|
|
11
|
+
"git-diff",
|
|
12
|
+
]);
|
|
13
|
+
export const EXPLORE_ALLOWED_TOOLS = Object.freeze([
|
|
14
|
+
...EXPLORE_ALLOWED_BUILTIN_TOOLS,
|
|
15
|
+
...EXPLORE_ALLOWED_CUSTOM_TOOLS,
|
|
16
|
+
]);
|
|
17
|
+
/** Defense-in-depth exclusions even if an SDK default would register them. */
|
|
18
|
+
export const EXPLORE_EXCLUDE_TOOLS = Object.freeze([
|
|
19
|
+
"write",
|
|
20
|
+
"edit",
|
|
21
|
+
"bash",
|
|
22
|
+
"read",
|
|
23
|
+
"grep",
|
|
24
|
+
"apply_patch",
|
|
25
|
+
"apply-patch",
|
|
26
|
+
"full-tools",
|
|
27
|
+
"full_tools",
|
|
28
|
+
"coding-chat",
|
|
29
|
+
"coding_chat",
|
|
30
|
+
"shell",
|
|
31
|
+
"Agent",
|
|
32
|
+
]);
|
|
33
|
+
const ALLOWED_LOWER = new Set(EXPLORE_ALLOWED_TOOLS.map((name) => name.toLowerCase()));
|
|
34
|
+
export function isExploreAllowedTool(toolId) {
|
|
35
|
+
return ALLOWED_LOWER.has(toolId.trim().toLowerCase());
|
|
36
|
+
}
|
|
37
|
+
export function pinExploreToolNames() {
|
|
38
|
+
return [...EXPLORE_ALLOWED_TOOLS];
|
|
39
|
+
}
|
|
40
|
+
export function extrasOutsideExploreAllowlist(activeToolNames) {
|
|
41
|
+
return activeToolNames.filter((name) => !isExploreAllowedTool(name));
|
|
42
|
+
}
|
|
43
|
+
export function filterExploreCustomTools(tools) {
|
|
44
|
+
return tools.filter((tool) => {
|
|
45
|
+
const name = tool?.name;
|
|
46
|
+
return typeof name === "string" && isExploreAllowedTool(name);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
export const EXPLORE_CHILD_RESOURCE_LOADER_OPTIONS = Object.freeze({
|
|
50
|
+
noExtensions: true,
|
|
51
|
+
noPromptTemplates: true,
|
|
52
|
+
noThemes: true,
|
|
53
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Console Chat built-in Explore subagent contracts (ADR 0022).
|
|
3
|
+
*
|
|
4
|
+
* First slice: one Agent tool call = one Fresh, read-only, one-shot Explore
|
|
5
|
+
* child session. `tasks[]` fan-out is rejected here and reserved for a later
|
|
6
|
+
* plan. The registry is a Map so concurrent parent-turn runs stay possible.
|
|
7
|
+
*/
|
|
8
|
+
export const EXPLORE_AGENT_NAME = "Explore";
|
|
9
|
+
export const OPERATOR_CHAT_AGENT_TOOL_NAME = "Agent";
|
|
10
|
+
export const EXPLORE_RESULT_SCHEMA_VERSION = 1;
|
|
11
|
+
export const EXPLORE_RESULT_KIND = "subagent";
|
|
12
|
+
export const EXPLORE_RESULT_MAX_BYTES = 200 * 1024;
|
|
13
|
+
export const EXPLORE_RESULT_MAX_LINES = 5_000;
|
|
@@ -93,6 +93,13 @@ function adapterPreview(toolName, input, maxLen) {
|
|
|
93
93
|
const query = fieldString(input, "query") ?? fieldString(input, "symbol");
|
|
94
94
|
return query ? clampOneLine(query, maxLen) : undefined;
|
|
95
95
|
}
|
|
96
|
+
if (name === "agent") {
|
|
97
|
+
const description = fieldString(input, "description");
|
|
98
|
+
const subagent = fieldString(input, "subagent_type") ?? "Explore";
|
|
99
|
+
if (description)
|
|
100
|
+
return clampOneLine(`${subagent} · ${description}`, maxLen);
|
|
101
|
+
return clampOneLine(subagent, maxLen);
|
|
102
|
+
}
|
|
96
103
|
if (name === "read" ||
|
|
97
104
|
name === "read_symbol" ||
|
|
98
105
|
name === "read_enclosing" ||
|
|
@@ -181,6 +188,9 @@ export function getToolPresentation(toolName) {
|
|
|
181
188
|
if (name === "mcp" || name.startsWith("mcp_") || name.includes("__")) {
|
|
182
189
|
return { kind: "mcp", label: name === "mcp" ? "MCP" : raw };
|
|
183
190
|
}
|
|
191
|
+
if (name === "agent") {
|
|
192
|
+
return { kind: "search", label: "Explore" };
|
|
193
|
+
}
|
|
184
194
|
return { kind: "tool", label: raw };
|
|
185
195
|
}
|
|
186
196
|
/** Section labels for ToolCard expand body. */
|