@tea-agent/loop-agent 0.31.1 → 0.32.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/AGENTS.md +1 -1
- package/CHANGELOG.md +46 -0
- package/README.md +1 -1
- package/dist/shared/operator/capabilities.js +22 -1
- package/dist/shared/operator/command-lifecycle.js +94 -0
- package/dist/shared/operator/index.js +1 -0
- package/dist/worker/cli.js +10 -24
- package/dist/worker/console/doctor.js +11 -4
- package/dist/worker/console/observe-health-match.js +18 -15
- package/dist/worker/console/operator-actions.js +2 -1
- package/dist/worker/console/static/assets/index-Bpa2qrc-.js +29 -0
- package/dist/worker/console/static/assets/index-BqfFDdnG.css +1 -0
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/console-types.js +140 -0
- package/dist/worker/console/static-src/app/useConsoleShell.js +101 -0
- package/dist/worker/console/static-src/app/useOperatorActions.js +304 -0
- package/dist/worker/console/static-src/app/usePrdImport.js +171 -0
- package/dist/worker/console/static-src/app/useRecoveryActions.js +257 -0
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +334 -0
- package/dist/worker/console/static-src/app/useTaskWizard.js +229 -0
- package/dist/worker/console/static-src/chat-view-types.js +2 -0
- package/dist/worker/console/static-src/night/night-types.js +24 -0
- package/dist/worker/console/static-src/night/useNightBoard.js +125 -0
- package/dist/worker/console/static-src/night/useNightWizard.js +171 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +253 -0
- package/dist/worker/console/static-src/operator-chat/format.js +71 -0
- package/dist/worker/console/static-src/operator-chat/refs.js +47 -0
- package/dist/worker/console/static-src/operator-chat/tools-catalog.js +77 -0
- package/dist/worker/console/static-src/operator-chat/types.js +1 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +320 -0
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +209 -0
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +218 -0
- package/dist/worker/console/static-src/operator-chat/useComposer.js +125 -0
- package/dist/worker/console/static-src/operator-chat/useInterview.js +108 -0
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +123 -0
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +207 -0
- package/dist/worker/feature/acceptance-policy.js +227 -0
- package/dist/worker/feature/decision-loader.js +1 -1
- package/dist/worker/feature/next-action.js +56 -6
- package/dist/worker/feature/profile-schema.js +3 -0
- package/dist/worker/feature/reducer.js +1 -0
- package/dist/worker/feature/review.js +72 -8
- package/dist/worker/feature/scaffold.js +14 -0
- package/dist/worker/loop-agent/controller-protocol.js +143 -0
- package/dist/worker/materialize/harness-task-lifecycle-probe.js +126 -0
- package/dist/worker/materialize/harness-task-lineage.js +220 -0
- package/dist/worker/materialize/harness-task-materializer.js +350 -80
- package/dist/worker/observability/progress-composite.js +1 -0
- package/dist/worker/observability/read-model.js +66 -19
- package/dist/worker/pool/attempt-identity.js +41 -0
- package/dist/worker/pool/attempt-lease.js +184 -0
- package/dist/worker/pool/attempt-transition.js +210 -0
- package/dist/worker/pool/begin-attempt-with-lease.js +26 -0
- package/dist/worker/pool/begin-attempt.js +35 -0
- package/dist/worker/pool/failure-routing.js +49 -0
- package/dist/worker/pool/recovery-decision.js +163 -0
- package/dist/worker/pool/run-owner-store.js +126 -0
- package/dist/worker/pool/run-store.js +32 -46
- package/dist/worker/pool/runtime-reconcile-inventory.js +127 -0
- package/dist/worker/pool/state-projection.js +57 -0
- package/dist/worker/run-task/run-task.js +31 -4
- package/dist/worker/runner/run-ready.js +64 -14
- package/dist/worker/runner/single-task-attempt.js +42 -13
- package/dist/worker/task-graph/acceptance-schema.js +3 -0
- package/docs/README.md +3 -1
- package/docs/architecture/evolution.md +1 -1
- package/docs/operations/README.md +1 -0
- package/docs/templates/README.md +1 -0
- package/docs/templates/agent-worker-production-readiness-checklist.md +47 -0
- package/docs/templates/evaluation/agents-map-slim-v1.md +1 -1
- package/docs/templates/evaluation/agents-map-verbose-v0.md +1 -1
- package/docs/templates/init-managed-agents.md +1 -1
- package/docs/templates/product-line/scaffold-samples/backend-only/acceptance.yaml +3 -0
- package/docs/templates/product-line/scaffold-samples/backend-only/feature.yaml +11 -0
- package/docs/templates/product-line/scaffold-samples/fe-with-api/acceptance.yaml +3 -0
- package/docs/templates/product-line/scaffold-samples/fe-with-api/feature.yaml +11 -0
- package/docs/templates/product-line/scaffold-samples/frontend-only/acceptance.yaml +3 -0
- package/docs/templates/product-line/scaffold-samples/frontend-only/feature.yaml +11 -0
- package/package.json +5 -4
- package/skills/agent-worker/SKILL.md +1 -1
- package/skills/agent-worker/references/agent-worker-operator.md +1 -1
- package/skills/loop-agent/references/command-reference.md +2 -2
- package/skills/loop-agent/references/harness-policy.md +1 -1
- package/dist/worker/console/static/assets/index-D9gnJn_l.js +0 -29
- package/dist/worker/console/static/assets/index-rajoXwkM.css +0 -1
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { artifactFromEventPayload, mergeArtifactCardState, } from "../../chat/artifact-card.js";
|
|
2
|
+
import { humanGateCardFromEventPayload, mergeHumanGateCardState, } from "../../chat/human-gate-card.js";
|
|
3
|
+
import { mergeOperationCardState, operationFromEventPayload, } from "../../chat/operation-card.js";
|
|
4
|
+
import { requestId } from "./format.js";
|
|
5
|
+
/** Shared handler for a single parsed Chat SSE event (M1 reconnect).
|
|
6
|
+
*
|
|
7
|
+
* Used by BOTH the inline prompt POST SSE and the background reconnect
|
|
8
|
+
* GET /events stream, so a page refresh reapplies the same events the
|
|
9
|
+
* original request would have. `assistantId` is the in-flight assistant
|
|
10
|
+
* message id to patch for streaming text; reconnect creates one if none. */
|
|
11
|
+
export function createChatSseEventApplier(deps) {
|
|
12
|
+
const { refs, setError, setProcessOpen, setInterview, setHumanGateCards, setArtifactCards, setStreaming, setStreamingAssistantId, setUnread, setOperationCards, setUsage, setMessages, setToolCalls, } = deps;
|
|
13
|
+
return (eventName, data, assistantId, options) => {
|
|
14
|
+
const replay = options?.replay === true;
|
|
15
|
+
if (eventName === "reconcile") {
|
|
16
|
+
// Cursor aged out of the server ring: history may be incomplete.
|
|
17
|
+
// Surface a non-fatal notice rather than silently rendering a gap.
|
|
18
|
+
const reason = typeof data.reason === "string" ? data.reason : "cursor_expired";
|
|
19
|
+
setError(`会话事件窗口已过期(${reason}),建议刷新历史或新建会话。`);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const payload = data.data && typeof data.data === "object"
|
|
23
|
+
? data.data
|
|
24
|
+
: data;
|
|
25
|
+
const turnId = typeof data.turnId === "string" ? data.turnId : undefined;
|
|
26
|
+
let resolvedAssistantId = assistantId;
|
|
27
|
+
if (turnId) {
|
|
28
|
+
const mapped = refs.turnAssistantIdsRef.current.get(turnId);
|
|
29
|
+
if (mapped)
|
|
30
|
+
resolvedAssistantId = mapped;
|
|
31
|
+
else
|
|
32
|
+
refs.turnAssistantIdsRef.current.set(turnId, assistantId);
|
|
33
|
+
}
|
|
34
|
+
if (eventName === "interview-turn") {
|
|
35
|
+
setInterview((current) => ({
|
|
36
|
+
...(current ?? {}),
|
|
37
|
+
interviewSessionId: typeof payload.interviewSessionId === "string"
|
|
38
|
+
? payload.interviewSessionId
|
|
39
|
+
: current?.interviewSessionId,
|
|
40
|
+
taskId: typeof payload.taskId === "string"
|
|
41
|
+
? payload.taskId
|
|
42
|
+
: current?.taskId,
|
|
43
|
+
state: typeof payload.state === "string" ? payload.state : current?.state,
|
|
44
|
+
question: payload.question && typeof payload.question === "object"
|
|
45
|
+
? payload.question
|
|
46
|
+
: undefined,
|
|
47
|
+
}));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (eventName === "draft") {
|
|
51
|
+
setInterview((current) => ({
|
|
52
|
+
...(current ?? {}),
|
|
53
|
+
taskId: typeof payload.taskId === "string"
|
|
54
|
+
? payload.taskId
|
|
55
|
+
: current?.taskId,
|
|
56
|
+
draft: payload.draft && typeof payload.draft === "object"
|
|
57
|
+
? payload.draft
|
|
58
|
+
: current?.draft,
|
|
59
|
+
draftSha256: typeof payload.draftSha256 === "string"
|
|
60
|
+
? payload.draftSha256
|
|
61
|
+
: current?.draftSha256,
|
|
62
|
+
taskKindRecommendation: typeof payload.taskKindRecommendation === "string"
|
|
63
|
+
? payload.taskKindRecommendation
|
|
64
|
+
: current?.taskKindRecommendation,
|
|
65
|
+
}));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (eventName === "assessment") {
|
|
69
|
+
setInterview((current) => ({
|
|
70
|
+
...(current ?? {}),
|
|
71
|
+
assessment: payload,
|
|
72
|
+
}));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (eventName === "taskKind-confirmed") {
|
|
76
|
+
setInterview((current) => ({
|
|
77
|
+
...(current ?? {}),
|
|
78
|
+
confirmedTaskKind: typeof payload.taskKind === "string"
|
|
79
|
+
? payload.taskKind
|
|
80
|
+
: current?.confirmedTaskKind,
|
|
81
|
+
taskKindRecommendation: undefined,
|
|
82
|
+
}));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (eventName === "human-gate-card") {
|
|
86
|
+
const card = humanGateCardFromEventPayload(payload);
|
|
87
|
+
if (card)
|
|
88
|
+
setHumanGateCards((current) => mergeHumanGateCardState(current, card));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (eventName === "artifact-ref") {
|
|
92
|
+
const artifact = artifactFromEventPayload(payload);
|
|
93
|
+
if (artifact)
|
|
94
|
+
setArtifactCards((current) => mergeArtifactCardState(current, artifact));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (eventName === "agent_start") {
|
|
98
|
+
setStreaming(true);
|
|
99
|
+
setStreamingAssistantId(resolvedAssistantId);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (eventName === "agent_settled") {
|
|
103
|
+
setStreaming(false);
|
|
104
|
+
setStreamingAssistantId(null);
|
|
105
|
+
if (!replay && document.hidden)
|
|
106
|
+
setUnread((current) => current + 1);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (eventName === "operation-ref" || eventName === "operation-status") {
|
|
110
|
+
const operation = operationFromEventPayload(payload);
|
|
111
|
+
if (operation) {
|
|
112
|
+
setOperationCards((current) => mergeOperationCardState(current, operation));
|
|
113
|
+
}
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (eventName === "usage") {
|
|
117
|
+
if (replay)
|
|
118
|
+
return;
|
|
119
|
+
const sample = payload;
|
|
120
|
+
const responseKey = typeof sample.responseKey === "string" && sample.responseKey
|
|
121
|
+
? sample.responseKey
|
|
122
|
+
: undefined;
|
|
123
|
+
if (responseKey && refs.usageResponseKeysRef.current.has(responseKey)) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (responseKey)
|
|
127
|
+
refs.usageResponseKeysRef.current.add(responseKey);
|
|
128
|
+
setUsage((current) => {
|
|
129
|
+
const input = sample.inputTokens ?? 0;
|
|
130
|
+
const output = sample.outputTokens ?? 0;
|
|
131
|
+
const cacheRead = sample.cacheReadTokens ?? 0;
|
|
132
|
+
const cacheWrite = sample.cacheWriteTokens ?? 0;
|
|
133
|
+
const total = sample.totalTokens ?? input + output + cacheRead + cacheWrite;
|
|
134
|
+
return {
|
|
135
|
+
inputTokens: current.inputTokens + input,
|
|
136
|
+
outputTokens: current.outputTokens + output,
|
|
137
|
+
cacheReadTokens: current.cacheReadTokens + cacheRead,
|
|
138
|
+
cacheWriteTokens: current.cacheWriteTokens + cacheWrite,
|
|
139
|
+
totalTokens: current.totalTokens + total,
|
|
140
|
+
cost: sample.cost === undefined
|
|
141
|
+
? current.cost
|
|
142
|
+
: (current.cost ?? 0) + sample.cost,
|
|
143
|
+
};
|
|
144
|
+
});
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (eventName === "message_update" || eventName === "message_end") {
|
|
148
|
+
if (replay) {
|
|
149
|
+
// Durable messages were restored from the session store; skip
|
|
150
|
+
// text replay so a fresh reconnect assistant id cannot create
|
|
151
|
+
// a duplicate bubble.
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const text = typeof payload.text === "string" ? payload.text : "";
|
|
155
|
+
setMessages((m) => {
|
|
156
|
+
const exists = m.some((msg) => msg.id === resolvedAssistantId);
|
|
157
|
+
if (exists) {
|
|
158
|
+
return m.map((msg) => msg.id === resolvedAssistantId ? { ...msg, text } : msg);
|
|
159
|
+
}
|
|
160
|
+
// Reconnect path: the assistant message may not exist yet
|
|
161
|
+
// (e.g. only tool_call events arrived). Append a placeholder.
|
|
162
|
+
return [
|
|
163
|
+
...m,
|
|
164
|
+
{
|
|
165
|
+
id: resolvedAssistantId,
|
|
166
|
+
role: "assistant",
|
|
167
|
+
text,
|
|
168
|
+
createdAt: Date.now(),
|
|
169
|
+
},
|
|
170
|
+
];
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
else if (eventName === "tool_call") {
|
|
174
|
+
const id = typeof payload.toolCallId === "string"
|
|
175
|
+
? payload.toolCallId
|
|
176
|
+
: requestId("t");
|
|
177
|
+
const toolName = typeof payload.toolName === "string" ? payload.toolName : "tool";
|
|
178
|
+
const argsPreview = typeof payload.argsPreview === "string" ? payload.argsPreview : "{}";
|
|
179
|
+
const now = Date.now();
|
|
180
|
+
setToolCalls((prev) => {
|
|
181
|
+
if (prev.some((t) => t.id === id)) {
|
|
182
|
+
return prev.map((t) => t.id === id
|
|
183
|
+
? {
|
|
184
|
+
...t,
|
|
185
|
+
args: argsPreview,
|
|
186
|
+
status: "running",
|
|
187
|
+
turnId: turnId ?? t.turnId,
|
|
188
|
+
assistantMessageId: resolvedAssistantId,
|
|
189
|
+
}
|
|
190
|
+
: t);
|
|
191
|
+
}
|
|
192
|
+
return [
|
|
193
|
+
...prev,
|
|
194
|
+
{
|
|
195
|
+
id,
|
|
196
|
+
toolName,
|
|
197
|
+
args: argsPreview,
|
|
198
|
+
status: "running",
|
|
199
|
+
createdAt: now,
|
|
200
|
+
turnId,
|
|
201
|
+
assistantMessageId: resolvedAssistantId,
|
|
202
|
+
},
|
|
203
|
+
];
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
else if (eventName === "tool_result") {
|
|
207
|
+
const id = typeof payload.toolCallId === "string"
|
|
208
|
+
? payload.toolCallId
|
|
209
|
+
: requestId("r");
|
|
210
|
+
const toolName = typeof payload.toolName === "string" ? payload.toolName : "tool";
|
|
211
|
+
const isError = payload.isError === true;
|
|
212
|
+
const resultText = typeof payload.resultPreview === "string"
|
|
213
|
+
? payload.resultPreview
|
|
214
|
+
: "{}";
|
|
215
|
+
const now = Date.now();
|
|
216
|
+
if (isError)
|
|
217
|
+
setProcessOpen(true);
|
|
218
|
+
setToolCalls((prev) => {
|
|
219
|
+
const exists = prev.find((t) => t.id === id);
|
|
220
|
+
if (exists) {
|
|
221
|
+
return prev.map((t) => t.id === id
|
|
222
|
+
? {
|
|
223
|
+
...t,
|
|
224
|
+
result: resultText,
|
|
225
|
+
resultText,
|
|
226
|
+
status: isError ? "error" : "ok",
|
|
227
|
+
finishedAt: now,
|
|
228
|
+
turnId: turnId ?? t.turnId,
|
|
229
|
+
assistantMessageId: t.assistantMessageId ?? resolvedAssistantId,
|
|
230
|
+
}
|
|
231
|
+
: t);
|
|
232
|
+
}
|
|
233
|
+
return [
|
|
234
|
+
...prev,
|
|
235
|
+
{
|
|
236
|
+
id,
|
|
237
|
+
toolName,
|
|
238
|
+
result: resultText,
|
|
239
|
+
resultText,
|
|
240
|
+
status: isError ? "error" : "ok",
|
|
241
|
+
createdAt: now,
|
|
242
|
+
finishedAt: now,
|
|
243
|
+
turnId,
|
|
244
|
+
assistantMessageId: resolvedAssistantId,
|
|
245
|
+
},
|
|
246
|
+
];
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
else if (eventName === "error") {
|
|
250
|
+
setError(typeof payload.message === "string" ? payload.message : "chat error");
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { truncateToolResult } from "../../chat/tool-preview.js";
|
|
2
|
+
export function confirmationToken() {
|
|
3
|
+
const w = window;
|
|
4
|
+
return w.__CONSOLE_CONFIRMATION_TOKEN__ ?? "";
|
|
5
|
+
}
|
|
6
|
+
export function requestId(prefix) {
|
|
7
|
+
return `${prefix}-${Math.random().toString(36).slice(2, 10)}`;
|
|
8
|
+
}
|
|
9
|
+
/** Resolve after `ms`, but reject early (AbortError) if the signal aborts.
|
|
10
|
+
* Used to back off / pace the reconnect loop without blocking teardown. */
|
|
11
|
+
export function sleep(ms, signal) {
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
if (signal?.aborted) {
|
|
14
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const t = setTimeout(() => {
|
|
18
|
+
signal?.removeEventListener("abort", onAbort);
|
|
19
|
+
resolve();
|
|
20
|
+
}, ms);
|
|
21
|
+
const onAbort = () => {
|
|
22
|
+
clearTimeout(t);
|
|
23
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
24
|
+
};
|
|
25
|
+
signal?.addEventListener("abort", onAbort);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
export function fmtTime(ts) {
|
|
29
|
+
const d = new Date(ts);
|
|
30
|
+
return d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
31
|
+
}
|
|
32
|
+
export function fmtDaySep(ts) {
|
|
33
|
+
const d = new Date(ts);
|
|
34
|
+
const now = new Date();
|
|
35
|
+
const isToday = d.getFullYear() === now.getFullYear() &&
|
|
36
|
+
d.getMonth() === now.getMonth() &&
|
|
37
|
+
d.getDate() === now.getDate();
|
|
38
|
+
if (isToday)
|
|
39
|
+
return "今天";
|
|
40
|
+
return d.toLocaleDateString([], { month: "long", day: "numeric" });
|
|
41
|
+
}
|
|
42
|
+
export function resultPreview(result) {
|
|
43
|
+
try {
|
|
44
|
+
const s = typeof result === "string" ? result : JSON.stringify(result, null, 2);
|
|
45
|
+
return truncateToolResult(s).text;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return String(result);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export function kindLabel(kind) {
|
|
52
|
+
switch (kind) {
|
|
53
|
+
case "mutation":
|
|
54
|
+
return "变更";
|
|
55
|
+
case "long-running":
|
|
56
|
+
return "长任务";
|
|
57
|
+
case "explore":
|
|
58
|
+
return "探查";
|
|
59
|
+
case "read":
|
|
60
|
+
default:
|
|
61
|
+
return "只读";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export function taskContextUnchanged(prev, next) {
|
|
65
|
+
if (!prev)
|
|
66
|
+
return false;
|
|
67
|
+
return (prev.taskId === next.taskId &&
|
|
68
|
+
prev.updatedAt === next.updatedAt &&
|
|
69
|
+
prev.lastEventId === next.lastEventId &&
|
|
70
|
+
prev.operations.length === next.operations.length);
|
|
71
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { useMemo, useRef } from "react";
|
|
2
|
+
export function useChatRefs() {
|
|
3
|
+
const sessionRef = useRef(null);
|
|
4
|
+
const streamingRef = useRef(false);
|
|
5
|
+
const abortRef = useRef(null);
|
|
6
|
+
const sessionAbortControllersRef = useRef(new Map());
|
|
7
|
+
const inlineStreamSessionIdsRef = useRef(new Set());
|
|
8
|
+
const assistantIdRef = useRef(null);
|
|
9
|
+
const lastEventIdRef = useRef(null);
|
|
10
|
+
const seenEventIdsRef = useRef(new Set());
|
|
11
|
+
const turnAssistantIdsRef = useRef(new Map());
|
|
12
|
+
const reconnectAbortRef = useRef(null);
|
|
13
|
+
const usageResponseKeysRef = useRef(new Set());
|
|
14
|
+
const isComposingRef = useRef(false);
|
|
15
|
+
const compositionEndedAtRef = useRef(0);
|
|
16
|
+
// Keep the bag identity stable: individual refs never change, so domain
|
|
17
|
+
// hooks can safely list `refs` in useCallback/useEffect dependency arrays.
|
|
18
|
+
return useMemo(() => ({
|
|
19
|
+
sessionRef,
|
|
20
|
+
streamingRef,
|
|
21
|
+
abortRef,
|
|
22
|
+
sessionAbortControllersRef,
|
|
23
|
+
inlineStreamSessionIdsRef,
|
|
24
|
+
assistantIdRef,
|
|
25
|
+
lastEventIdRef,
|
|
26
|
+
seenEventIdsRef,
|
|
27
|
+
turnAssistantIdsRef,
|
|
28
|
+
reconnectAbortRef,
|
|
29
|
+
usageResponseKeysRef,
|
|
30
|
+
isComposingRef,
|
|
31
|
+
compositionEndedAtRef,
|
|
32
|
+
}), [
|
|
33
|
+
sessionRef,
|
|
34
|
+
streamingRef,
|
|
35
|
+
abortRef,
|
|
36
|
+
sessionAbortControllersRef,
|
|
37
|
+
inlineStreamSessionIdsRef,
|
|
38
|
+
assistantIdRef,
|
|
39
|
+
lastEventIdRef,
|
|
40
|
+
seenEventIdsRef,
|
|
41
|
+
turnAssistantIdsRef,
|
|
42
|
+
reconnectAbortRef,
|
|
43
|
+
usageResponseKeysRef,
|
|
44
|
+
isComposingRef,
|
|
45
|
+
compositionEndedAtRef,
|
|
46
|
+
]);
|
|
47
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADR 0011: full Pi tools + optional safe-* explore surface.
|
|
3
|
+
*/
|
|
4
|
+
const EXPLORE_TOOLS = [
|
|
5
|
+
{
|
|
6
|
+
toolId: "read",
|
|
7
|
+
kind: "explore",
|
|
8
|
+
description: "Pi 内置读取文件。",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
toolId: "write",
|
|
12
|
+
kind: "explore",
|
|
13
|
+
description: "Pi 内置写入文件(软约束:大改优先走 DAG)。",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
toolId: "edit",
|
|
17
|
+
kind: "explore",
|
|
18
|
+
description: "Pi 内置编辑文件(软约束:大改优先走 DAG)。",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
toolId: "bash",
|
|
22
|
+
kind: "explore",
|
|
23
|
+
description: "Pi 内置 shell 执行(软约束:优先受治路径)。",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
toolId: "grep",
|
|
27
|
+
kind: "explore",
|
|
28
|
+
description: "Pi 内置内容搜索。",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
toolId: "find",
|
|
32
|
+
kind: "explore",
|
|
33
|
+
description: "按名称查找文件路径。",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
toolId: "ls",
|
|
37
|
+
kind: "explore",
|
|
38
|
+
description: "列出目录内容。",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
toolId: "safe-read",
|
|
42
|
+
kind: "explore",
|
|
43
|
+
description: "安全读取仓库相对路径文件;拒绝敏感文件并自动脱敏 secret。",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
toolId: "safe-grep",
|
|
47
|
+
kind: "explore",
|
|
48
|
+
description: "安全搜索仓库内容;跳过敏感文件并自动脱敏搜索结果。",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
toolId: "git-status",
|
|
52
|
+
kind: "explore",
|
|
53
|
+
description: "固定参数执行 git status --porcelain(无 shell)。",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
toolId: "git-diff",
|
|
57
|
+
kind: "explore",
|
|
58
|
+
description: "固定参数查看 git diff;敏感路径不可查看且结果自动脱敏。",
|
|
59
|
+
},
|
|
60
|
+
];
|
|
61
|
+
export function buildToolCatalog(capabilities) {
|
|
62
|
+
if (!capabilities)
|
|
63
|
+
return null;
|
|
64
|
+
const operatorTools = (capabilities.tools ?? []).map((t) => ({
|
|
65
|
+
toolId: t.toolId,
|
|
66
|
+
action: t.action,
|
|
67
|
+
description: t.description,
|
|
68
|
+
kind: t.kind ?? "read",
|
|
69
|
+
cli: t.cli,
|
|
70
|
+
}));
|
|
71
|
+
const explore = [...EXPLORE_TOOLS];
|
|
72
|
+
return {
|
|
73
|
+
explore,
|
|
74
|
+
operator: operatorTools,
|
|
75
|
+
total: explore.length + operatorTools.length,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|