@cjhyy/code-shell-core 0.7.0-beta.1 → 0.7.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/dist/arena/arena.d.ts +2 -0
- package/dist/arena/arena.js +30 -1
- package/dist/arena/phases/adjudication.d.ts +2 -1
- package/dist/arena/phases/adjudication.js +2 -1
- package/dist/arena/phases/build-consensus.d.ts +2 -1
- package/dist/arena/phases/build-consensus.js +3 -1
- package/dist/arena/phases/cross-review.d.ts +3 -1
- package/dist/arena/phases/cross-review.js +8 -2
- package/dist/arena/phases/debate-rounds.d.ts +2 -1
- package/dist/arena/phases/debate-rounds.js +4 -2
- package/dist/arena/phases/participant-research.d.ts +2 -1
- package/dist/arena/phases/participant-research.js +3 -1
- package/dist/arena/phases/planning-detail-expansion.d.ts +2 -1
- package/dist/arena/phases/planning-detail-expansion.js +2 -1
- package/dist/arena/planner.d.ts +2 -1
- package/dist/arena/planner.js +2 -1
- package/dist/arena/types.d.ts +5 -1
- package/dist/cc-orchestrator/agent-adapter.d.ts +2 -0
- package/dist/cc-orchestrator/agent-adapter.js +4 -0
- package/dist/cc-orchestrator/codex-session-history.d.ts +14 -1
- package/dist/cc-orchestrator/codex-session-history.js +64 -4
- package/dist/cc-orchestrator/external-agent-changes.js +22 -5
- package/dist/cc-orchestrator/external-agent-driver.d.ts +1 -1
- package/dist/cc-orchestrator/external-agent-driver.js +202 -38
- package/dist/cc-orchestrator/session-history.d.ts +35 -0
- package/dist/cc-orchestrator/session-history.js +96 -13
- package/dist/cli/agent-server-tcp.js +13 -2
- package/dist/context/manager.d.ts +3 -3
- package/dist/context/manager.js +6 -6
- package/dist/context/token-counter.js +5 -3
- package/dist/context/tool-result-storage.d.ts +6 -0
- package/dist/context/tool-result-storage.js +25 -4
- package/dist/credentials/access.d.ts +11 -1
- package/dist/credentials/access.js +77 -1
- package/dist/credentials/index.d.ts +3 -1
- package/dist/credentials/index.js +2 -0
- package/dist/credentials/oauth.d.ts +25 -0
- package/dist/credentials/oauth.js +179 -0
- package/dist/credentials/store.d.ts +2 -1
- package/dist/credentials/store.js +19 -8
- package/dist/credentials/types.d.ts +84 -1
- package/dist/credentials/types.js +16 -1
- package/dist/engine/engine.d.ts +67 -34
- package/dist/engine/engine.js +448 -247
- package/dist/engine/goal.d.ts +19 -0
- package/dist/engine/goal.js +16 -6
- package/dist/engine/input-attachments.js +156 -13
- package/dist/engine/run-image-input.d.ts +22 -0
- package/dist/engine/run-image-input.js +195 -0
- package/dist/engine/session-title.d.ts +2 -1
- package/dist/engine/session-title.js +4 -1
- package/dist/engine/steer-queue.d.ts +3 -1
- package/dist/engine/steer-queue.js +10 -2
- package/dist/engine/turn-loop.d.ts +48 -1
- package/dist/engine/turn-loop.js +307 -37
- package/dist/engine/types.d.ts +6 -2
- package/dist/git/worktree/crud.d.ts +3 -0
- package/dist/git/worktree/crud.js +32 -3
- package/dist/git/worktree/git-exec.d.ts +2 -2
- package/dist/git/worktree/git-exec.js +47 -11
- package/dist/git/worktree/query.d.ts +8 -7
- package/dist/git/worktree/query.js +27 -20
- package/dist/hooks/events.d.ts +3 -0
- package/dist/hooks/events.js +0 -3
- package/dist/hooks/goal-stop-hook.d.ts +44 -2
- package/dist/hooks/goal-stop-hook.js +775 -52
- package/dist/hooks/registry.js +3 -0
- package/dist/hooks/shell-runner.d.ts +12 -1
- package/dist/hooks/shell-runner.js +160 -9
- package/dist/index.d.ts +7 -6
- package/dist/index.js +6 -5
- package/dist/llm/client-base.js +12 -10
- package/dist/llm/types.d.ts +12 -5
- package/dist/plugins/pluginCommandHook.d.ts +4 -4
- package/dist/plugins/pluginCommandHook.js +111 -13
- package/dist/preset/index.js +14 -4
- package/dist/protocol/chat-session-manager.d.ts +13 -2
- package/dist/protocol/chat-session-manager.js +90 -18
- package/dist/protocol/chat-session.d.ts +12 -0
- package/dist/protocol/chat-session.js +30 -5
- package/dist/protocol/client.d.ts +5 -2
- package/dist/protocol/client.js +22 -1
- package/dist/protocol/server.d.ts +25 -11
- package/dist/protocol/server.js +291 -73
- package/dist/protocol/types.d.ts +36 -2
- package/dist/protocol/types.js +2 -0
- package/dist/services/dream-consolidation.d.ts +3 -0
- package/dist/services/dream-consolidation.js +4 -1
- package/dist/services/index.d.ts +1 -1
- package/dist/services/index.js +1 -1
- package/dist/services/oauth.d.ts +34 -10
- package/dist/services/oauth.js +233 -98
- package/dist/session/session-manager.d.ts +35 -6
- package/dist/session/session-manager.js +396 -27
- package/dist/session/transcript.d.ts +30 -1
- package/dist/session/transcript.js +119 -4
- package/dist/tool-system/builtin/agent-notifications.d.ts +11 -4
- package/dist/tool-system/builtin/agent-notifications.js +19 -7
- package/dist/tool-system/builtin/agent.js +5 -1
- package/dist/tool-system/builtin/arena.js +1 -0
- package/dist/tool-system/builtin/background-jobs.d.ts +28 -5
- package/dist/tool-system/builtin/background-jobs.js +109 -7
- package/dist/tool-system/builtin/background-work.d.ts +6 -1
- package/dist/tool-system/builtin/background-work.js +5 -1
- package/dist/tool-system/builtin/bash.d.ts +3 -5
- package/dist/tool-system/builtin/bash.js +10 -5
- package/dist/tool-system/builtin/browser-tools.d.ts +2 -2
- package/dist/tool-system/builtin/cron-list.definition.d.ts +3 -0
- package/dist/tool-system/builtin/cron-list.definition.js +6 -0
- package/dist/tool-system/builtin/cron.d.ts +1 -2
- package/dist/tool-system/builtin/cron.js +9 -7
- package/dist/tool-system/builtin/drive-claude-code.d.ts +7 -0
- package/dist/tool-system/builtin/drive-claude-code.js +307 -20
- package/dist/tool-system/builtin/edit.d.ts +2 -1
- package/dist/tool-system/builtin/edit.js +12 -4
- package/dist/tool-system/builtin/generate-video.d.ts +4 -0
- package/dist/tool-system/builtin/generate-video.js +138 -21
- package/dist/tool-system/builtin/glob.d.ts +2 -1
- package/dist/tool-system/builtin/glob.js +28 -3
- package/dist/tool-system/builtin/grep.d.ts +1 -0
- package/dist/tool-system/builtin/grep.js +82 -17
- package/dist/tool-system/builtin/index.d.ts +25 -11
- package/dist/tool-system/builtin/index.js +60 -5
- package/dist/tool-system/builtin/sleep.d.ts +1 -2
- package/dist/tool-system/builtin/sleep.definition.d.ts +8 -0
- package/dist/tool-system/builtin/sleep.definition.js +28 -0
- package/dist/tool-system/builtin/sleep.js +1 -22
- package/dist/tool-system/builtin/video-providers.d.ts +12 -15
- package/dist/tool-system/builtin/video-providers.js +1 -0
- package/dist/tool-system/builtin/view-image.d.ts +2 -2
- package/dist/tool-system/builtin/web-fetch.js +44 -3
- package/dist/tool-system/builtin/worktree.js +25 -7
- package/dist/tool-system/builtin/write.d.ts +2 -1
- package/dist/tool-system/builtin/write.js +14 -4
- package/dist/tool-system/context.d.ts +35 -5
- package/dist/tool-system/executor.js +24 -8
- package/dist/tool-system/mcp-manager.d.ts +20 -2
- package/dist/tool-system/mcp-manager.js +111 -12
- package/dist/tool-system/path-policy.d.ts +19 -0
- package/dist/tool-system/path-policy.js +62 -1
- package/dist/tool-system/permission.d.ts +43 -3
- package/dist/tool-system/permission.js +383 -30
- package/dist/tool-system/registry.d.ts +3 -2
- package/dist/tool-system/registry.js +52 -34
- package/dist/types.d.ts +38 -7
- package/package.json +1 -1
|
@@ -10,11 +10,15 @@ export class Transcript {
|
|
|
10
10
|
events = [];
|
|
11
11
|
filePath;
|
|
12
12
|
currentTurn = 0;
|
|
13
|
+
writer;
|
|
14
|
+
dirty = false;
|
|
15
|
+
lastFlushFailure;
|
|
13
16
|
getFilePath() {
|
|
14
17
|
return this.filePath;
|
|
15
18
|
}
|
|
16
|
-
constructor(filePath) {
|
|
19
|
+
constructor(filePath, writer = appendFileSync) {
|
|
17
20
|
this.filePath = filePath;
|
|
21
|
+
this.writer = writer;
|
|
18
22
|
mkdirSync(dirname(filePath), { recursive: true });
|
|
19
23
|
if (!existsSync(filePath)) {
|
|
20
24
|
writeFileSync(filePath, "", "utf-8");
|
|
@@ -179,18 +183,65 @@ export class Transcript {
|
|
|
179
183
|
get eventCount() {
|
|
180
184
|
return this.events.length;
|
|
181
185
|
}
|
|
186
|
+
/** True once any event failed both persistence attempts. Sticky by design. */
|
|
187
|
+
flushFailed() {
|
|
188
|
+
return this.dirty;
|
|
189
|
+
}
|
|
190
|
+
/** Structured details for the most recent unrecoverable flush failure. */
|
|
191
|
+
getFlushFailure() {
|
|
192
|
+
return this.lastFlushFailure ? { ...this.lastFlushFailure } : undefined;
|
|
193
|
+
}
|
|
182
194
|
findMessageByClientId(clientMessageId) {
|
|
183
195
|
return this.events.find((event) => event.type === "message" &&
|
|
184
196
|
event.data.clientMessageId === clientMessageId);
|
|
185
197
|
}
|
|
186
198
|
flush(event) {
|
|
199
|
+
const line = JSON.stringify(event) + "\n";
|
|
187
200
|
try {
|
|
188
|
-
|
|
201
|
+
this.writer(this.filePath, line, "utf-8");
|
|
202
|
+
return true;
|
|
189
203
|
}
|
|
190
|
-
catch {
|
|
191
|
-
|
|
204
|
+
catch (firstError) {
|
|
205
|
+
try {
|
|
206
|
+
this.writer(this.filePath, line, "utf-8");
|
|
207
|
+
logger.warn("transcript.flush_retry_recovered", {
|
|
208
|
+
filePath: this.filePath,
|
|
209
|
+
errno: this.errorErrno(firstError),
|
|
210
|
+
message: this.errorMessage(firstError),
|
|
211
|
+
});
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
catch (retryError) {
|
|
215
|
+
const err = retryError;
|
|
216
|
+
const failure = {
|
|
217
|
+
errno: this.errorErrno(retryError),
|
|
218
|
+
...(typeof err?.code === "string" ? { code: err.code } : {}),
|
|
219
|
+
message: this.errorMessage(retryError),
|
|
220
|
+
timestamp: Date.now(),
|
|
221
|
+
attempts: 2,
|
|
222
|
+
recoverable: false,
|
|
223
|
+
filePath: this.filePath,
|
|
224
|
+
};
|
|
225
|
+
// Sticky: a later successful append cannot restore an earlier missing
|
|
226
|
+
// JSONL event, so the transcript remains degraded for this instance.
|
|
227
|
+
this.dirty = true;
|
|
228
|
+
this.lastFlushFailure = failure;
|
|
229
|
+
logger.error("transcript.flush_failed", { ...failure });
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
192
232
|
}
|
|
193
233
|
}
|
|
234
|
+
errorErrno(error) {
|
|
235
|
+
const err = error;
|
|
236
|
+
if (typeof err?.errno === "number" || typeof err?.errno === "string")
|
|
237
|
+
return err.errno;
|
|
238
|
+
if (typeof err?.code === "string")
|
|
239
|
+
return err.code;
|
|
240
|
+
return "UNKNOWN";
|
|
241
|
+
}
|
|
242
|
+
errorMessage(error) {
|
|
243
|
+
return error instanceof Error ? error.message : String(error);
|
|
244
|
+
}
|
|
194
245
|
/**
|
|
195
246
|
* Repair tool_result pairing issues:
|
|
196
247
|
* - Orphaned tool_results (no matching tool_use) get removed
|
|
@@ -227,6 +278,70 @@ export class Transcript {
|
|
|
227
278
|
return true;
|
|
228
279
|
});
|
|
229
280
|
}
|
|
281
|
+
static readEvents(filePath) {
|
|
282
|
+
let raw;
|
|
283
|
+
try {
|
|
284
|
+
raw = readFileSync(filePath, "utf-8");
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
return { events: [], malformedLineCount: 0 };
|
|
288
|
+
}
|
|
289
|
+
const lines = raw.split("\n").filter((line) => line.trim().length > 0);
|
|
290
|
+
const events = [];
|
|
291
|
+
let malformedLineCount = 0;
|
|
292
|
+
for (const line of lines) {
|
|
293
|
+
try {
|
|
294
|
+
events.push(JSON.parse(line));
|
|
295
|
+
}
|
|
296
|
+
catch {
|
|
297
|
+
malformedLineCount++;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return { events, malformedLineCount };
|
|
301
|
+
}
|
|
302
|
+
static eventsToMessages(events) {
|
|
303
|
+
const messages = [];
|
|
304
|
+
for (const event of events) {
|
|
305
|
+
switch (event.type) {
|
|
306
|
+
case "message": {
|
|
307
|
+
const { role, content } = event.data;
|
|
308
|
+
messages.push({ role, content });
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
case "tool_use":
|
|
312
|
+
break;
|
|
313
|
+
case "tool_result": {
|
|
314
|
+
const { toolCallId, result, error, contentBlocks } = event.data;
|
|
315
|
+
const block = {
|
|
316
|
+
type: "tool_result",
|
|
317
|
+
tool_use_id: toolCallId,
|
|
318
|
+
content: error
|
|
319
|
+
? `Error: ${error}`
|
|
320
|
+
: Array.isArray(contentBlocks) && contentBlocks.length > 0
|
|
321
|
+
? contentBlocks
|
|
322
|
+
: (result ?? "(no output)"),
|
|
323
|
+
};
|
|
324
|
+
const last = messages[messages.length - 1];
|
|
325
|
+
if (last?.role === "user" && Array.isArray(last.content)) {
|
|
326
|
+
last.content.push(block);
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
messages.push({ role: "user", content: [block] });
|
|
330
|
+
}
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
case "summary": {
|
|
334
|
+
const { summary } = event.data;
|
|
335
|
+
messages.push({
|
|
336
|
+
role: "user",
|
|
337
|
+
content: `<system-reminder>Previous conversation was summarized:\n${summary}</system-reminder>`,
|
|
338
|
+
});
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return messages;
|
|
344
|
+
}
|
|
230
345
|
static loadFromFile(filePath) {
|
|
231
346
|
const transcript = new Transcript(filePath);
|
|
232
347
|
if (!existsSync(filePath))
|
|
@@ -6,8 +6,9 @@ import type { BackgroundAgentCompletedEvent, StreamEvent } from "../../types.js"
|
|
|
6
6
|
* background sub-agent that finishes (completed | failed) enqueues an item
|
|
7
7
|
* here; the UI layer subscribes and, when the main agent is idle, drains
|
|
8
8
|
* the queue and submits the formatted XML as a new user turn so the LLM
|
|
9
|
-
* sees the result.
|
|
10
|
-
* the agent; no follow-up needed).
|
|
9
|
+
* sees the result. Sub-agent cancellation does NOT enqueue (user explicitly
|
|
10
|
+
* stopped the agent; no follow-up needed). DriveAgent cancellation does enqueue
|
|
11
|
+
* so a detached external CLI job never leaves the waiting session hanging.
|
|
11
12
|
*
|
|
12
13
|
* The result text lives only in this queue + the eventual user message —
|
|
13
14
|
* not in `asyncAgentRegistry`. Registry stays metadata-only.
|
|
@@ -29,7 +30,7 @@ export type NotificationItem = {
|
|
|
29
30
|
agentId: string;
|
|
30
31
|
name?: string;
|
|
31
32
|
description: string;
|
|
32
|
-
status: "completed" | "failed";
|
|
33
|
+
status: "completed" | "failed" | "cancelled";
|
|
33
34
|
/** What kind of background work this was (lets UIs localize the toast). */
|
|
34
35
|
workKind?: "agent" | "shell" | "video" | "cc";
|
|
35
36
|
/** For workKind === "shell": the command that ran. */
|
|
@@ -41,8 +42,14 @@ export type NotificationItem = {
|
|
|
41
42
|
* if this notification is lost, and the user sees a real session id (not just
|
|
42
43
|
* an opaque background jobId). */
|
|
43
44
|
ccSessionId?: string;
|
|
44
|
-
/** Error message (failed only). */
|
|
45
|
+
/** Error message (failed/cancelled only). */
|
|
45
46
|
error?: string;
|
|
47
|
+
/** Files attributed to an external DriveAgent transcript. */
|
|
48
|
+
changedFiles?: string[];
|
|
49
|
+
/** DriveAgent cwd used to canonicalize path aliases in clients. */
|
|
50
|
+
cwd?: string;
|
|
51
|
+
/** Client id of the real user turn that launched this background work. */
|
|
52
|
+
originClientMessageId?: string;
|
|
46
53
|
enqueuedAt: number;
|
|
47
54
|
};
|
|
48
55
|
type Listener = () => void;
|
|
@@ -139,6 +139,15 @@ export function notificationItemToStreamEvent(item) {
|
|
|
139
139
|
event.finalText = item.finalText;
|
|
140
140
|
if (item.error !== undefined)
|
|
141
141
|
event.error = item.error;
|
|
142
|
+
if (item.ccSessionId !== undefined)
|
|
143
|
+
event.ccSessionId = item.ccSessionId;
|
|
144
|
+
if (item.changedFiles !== undefined)
|
|
145
|
+
event.changedFiles = item.changedFiles;
|
|
146
|
+
if (item.cwd !== undefined)
|
|
147
|
+
event.cwd = item.cwd;
|
|
148
|
+
if (item.originClientMessageId !== undefined) {
|
|
149
|
+
event.originClientMessageId = item.originClientMessageId;
|
|
150
|
+
}
|
|
142
151
|
return event;
|
|
143
152
|
}
|
|
144
153
|
/**
|
|
@@ -147,10 +156,7 @@ export function notificationItemToStreamEvent(item) {
|
|
|
147
156
|
* values get the quote escape too; element bodies don't need it.
|
|
148
157
|
*/
|
|
149
158
|
function escapeXmlText(s) {
|
|
150
|
-
return s
|
|
151
|
-
.replace(/&/g, "&")
|
|
152
|
-
.replace(/</g, "<")
|
|
153
|
-
.replace(/>/g, ">");
|
|
159
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
154
160
|
}
|
|
155
161
|
function escapeXmlAttr(s) {
|
|
156
162
|
return escapeXmlText(s).replace(/"/g, """);
|
|
@@ -173,7 +179,9 @@ export function buildNotificationMessage(items) {
|
|
|
173
179
|
const desc = ` <description>${escapeXmlText(item.description)}</description>`;
|
|
174
180
|
const body = item.status === "completed"
|
|
175
181
|
? ` <result>\n${escapeXmlText(item.finalText ?? "")}\n </result>`
|
|
176
|
-
:
|
|
182
|
+
: item.status === "cancelled"
|
|
183
|
+
? ` <cancelled>${escapeXmlText(item.error ?? "cancelled")}</cancelled>`
|
|
184
|
+
: ` <error>${escapeXmlText(item.error ?? "")}</error>`;
|
|
177
185
|
return [opening, desc, body, " </agent>"].join("\n");
|
|
178
186
|
})
|
|
179
187
|
.join("\n");
|
|
@@ -194,9 +202,13 @@ export function buildNotificationMessage(items) {
|
|
|
194
202
|
export function buildNotificationSummary(items) {
|
|
195
203
|
const header = "📨 background agents completed";
|
|
196
204
|
const rows = items.map((item) => {
|
|
197
|
-
const badge = item.status === "completed" ? "✓" : "✗";
|
|
205
|
+
const badge = item.status === "completed" ? "✓" : item.status === "cancelled" ? "cancelled" : "✗";
|
|
198
206
|
const namePart = item.name ? `${item.name} · ` : "";
|
|
199
|
-
const statusPart = item.status === "failed"
|
|
207
|
+
const statusPart = item.status === "failed"
|
|
208
|
+
? ` · failed: ${item.error ?? "unknown"}`
|
|
209
|
+
: item.status === "cancelled"
|
|
210
|
+
? ` · cancelled`
|
|
211
|
+
: "";
|
|
200
212
|
return ` └─ ${namePart}${item.description} · ${badge}${statusPart}`;
|
|
201
213
|
});
|
|
202
214
|
return [header, ...rows].join("\n");
|
|
@@ -269,7 +269,9 @@ streamOverride) {
|
|
|
269
269
|
// that no longer exists. The child Engine is a fresh instance; plan-mode
|
|
270
270
|
// isolation between parent and child is enforced via separate Engine
|
|
271
271
|
// instances (finalized in T6).
|
|
272
|
-
const { text } = await spawner.spawn({ ...opts, streamOverride });
|
|
272
|
+
const { text, usage } = await spawner.spawn({ ...opts, streamOverride });
|
|
273
|
+
if (usage)
|
|
274
|
+
opts.recordBilledUsage?.(usage);
|
|
273
275
|
const finalText = text || `Agent completed but produced no text output.`;
|
|
274
276
|
safeEmit(startEndSink, { type: "agent_end", agentId, name, description, text: finalText, agentType });
|
|
275
277
|
emitSubAgentHook(opts.hooks, "subagent_finish", { agentId, description, text: finalText });
|
|
@@ -372,6 +374,7 @@ export async function agentTool(args, ctx) {
|
|
|
372
374
|
appendSystemPrompt: overrides.appendSystemPrompt,
|
|
373
375
|
readOnlySession: overrides.resolvedType === "researcher" || overrides.resolvedType === "explorer",
|
|
374
376
|
hooks: ctx?.hooks,
|
|
377
|
+
recordBilledUsage: ctx?.recordBilledUsage,
|
|
375
378
|
signal: controller.signal,
|
|
376
379
|
}, parentStream, // uiStream: agent_start/end → main feed
|
|
377
380
|
transcriptSink)
|
|
@@ -581,6 +584,7 @@ async function runSyncSubAgent(args) {
|
|
|
581
584
|
readOnlySession: overrides.resolvedType === "researcher" || overrides.resolvedType === "explorer",
|
|
582
585
|
resumeSessionId,
|
|
583
586
|
hooks: ctx?.hooks,
|
|
587
|
+
recordBilledUsage: ctx?.recordBilledUsage,
|
|
584
588
|
signal: syncController.signal,
|
|
585
589
|
});
|
|
586
590
|
// Sentinel that the timer resolves with, so we can tell "agent finished" from
|
|
@@ -390,6 +390,7 @@ export async function arenaTool(args, ctx) {
|
|
|
390
390
|
signal,
|
|
391
391
|
});
|
|
392
392
|
const result = await arena.run(topic);
|
|
393
|
+
ctx.recordBilledUsage?.(result.usage);
|
|
393
394
|
// Use session renderer (markdown, no ANSI) for tool results.
|
|
394
395
|
// Terminal renderer (chalk) is only for direct stdout printing.
|
|
395
396
|
const formattedResult = formatArenaResultForSession(result);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Registry of NON-agent background jobs (GenerateVideo poll loops, DriveAgent
|
|
3
3
|
* external CLI runs). Mirrors the role `asyncAgentRegistry` plays for background
|
|
4
|
-
* sub-agents, but a job is not an agent — it has no transcript
|
|
5
|
-
*
|
|
4
|
+
* sub-agents, but a job is not an agent — it has no transcript and must NOT
|
|
5
|
+
* show up in AgentStatus.
|
|
6
6
|
*
|
|
7
7
|
* The engine's wait-for-background loop parks the turn until a session has no
|
|
8
8
|
* more RUNNING jobs, so the goal-stop-hook doesn't force the model to busy-loop
|
|
@@ -21,11 +21,15 @@
|
|
|
21
21
|
* loop observe the same instance.
|
|
22
22
|
*/
|
|
23
23
|
type Listener = () => void;
|
|
24
|
-
export type BackgroundJobStatus = "running" | "completed" | "failed";
|
|
24
|
+
export type BackgroundJobStatus = "running" | "cancelling" | "completed" | "failed" | "cancelled";
|
|
25
|
+
export type BackgroundJobKind = "drive-agent" | "video" | "job";
|
|
26
|
+
export type ExternalCliKind = "claude" | "codex";
|
|
25
27
|
/** A background job, running or finished. */
|
|
26
28
|
export interface BackgroundJobEntry {
|
|
27
29
|
jobId: string;
|
|
28
30
|
sessionId: string;
|
|
31
|
+
/** Machine-readable kind for tools that need to target a subset of jobs. */
|
|
32
|
+
kind?: BackgroundJobKind;
|
|
29
33
|
/** Human description (e.g. "Generating video: <prompt>"). Shown to the goal
|
|
30
34
|
* judge (running) and in the background panel (all). */
|
|
31
35
|
description: string;
|
|
@@ -42,16 +46,31 @@ export interface BackgroundJobEntry {
|
|
|
42
46
|
changedFiles?: string[];
|
|
43
47
|
/** Working directory for jobs that operate on the filesystem, e.g. DriveAgent. */
|
|
44
48
|
cwd?: string;
|
|
49
|
+
/** DriveAgent prompt summary, separate from the UI-oriented description. */
|
|
50
|
+
promptSummary?: string;
|
|
51
|
+
/** External CLI kind for DriveAgent jobs. */
|
|
52
|
+
cli?: ExternalCliKind;
|
|
53
|
+
/** Client message that launched this external job. */
|
|
54
|
+
originClientMessageId?: string;
|
|
55
|
+
/** Optional cancellation hook for jobs backed by a live process. It may
|
|
56
|
+
* return a richer terminal outcome when cancellation races with a result
|
|
57
|
+
* that must still be retained (for example, a remotely rendered video). */
|
|
58
|
+
abort?: () => void | BackgroundJobOutcome | Promise<void | BackgroundJobOutcome>;
|
|
45
59
|
}
|
|
46
60
|
/** Outcome passed to finish() to record how a job ended. */
|
|
47
61
|
export interface BackgroundJobOutcome {
|
|
48
|
-
status?: "completed" | "failed";
|
|
62
|
+
status?: "completed" | "failed" | "cancelled";
|
|
49
63
|
finalText?: string;
|
|
50
64
|
ccSessionId?: string;
|
|
51
65
|
changedFiles?: string[];
|
|
52
66
|
}
|
|
53
67
|
export interface BackgroundJobStartOptions {
|
|
68
|
+
kind?: BackgroundJobKind;
|
|
54
69
|
cwd?: string;
|
|
70
|
+
promptSummary?: string;
|
|
71
|
+
cli?: ExternalCliKind;
|
|
72
|
+
originClientMessageId?: string;
|
|
73
|
+
abort?: () => void | BackgroundJobOutcome | Promise<void | BackgroundJobOutcome>;
|
|
55
74
|
}
|
|
56
75
|
declare class BackgroundJobRegistry {
|
|
57
76
|
private jobs;
|
|
@@ -61,7 +80,11 @@ declare class BackgroundJobRegistry {
|
|
|
61
80
|
/** Mark a job terminal (retained, not deleted). Unknown id is a no-op (no
|
|
62
81
|
* notify) so a double-finish or a finish after reset stays quiet. */
|
|
63
82
|
finish(jobId: string, outcome?: BackgroundJobOutcome): void;
|
|
64
|
-
|
|
83
|
+
get(jobId: string): BackgroundJobEntry | undefined;
|
|
84
|
+
/** Persist artifacts discovered while a running job is winding down. */
|
|
85
|
+
recordArtifacts(jobId: string, artifacts: Pick<BackgroundJobOutcome, "ccSessionId" | "changedFiles">): void;
|
|
86
|
+
cancel(jobId: string, outcome?: Omit<BackgroundJobOutcome, "status">): Promise<boolean>;
|
|
87
|
+
/** True while any running/cancelling job spawned by `sessionId` remains. */
|
|
65
88
|
hasRunningForSession(sessionId: string): boolean;
|
|
66
89
|
/** Running jobs spawned by `sessionId`. Feeds the goal judge's task list. */
|
|
67
90
|
listRunningForSession(sessionId: string): BackgroundJobEntry[];
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Registry of NON-agent background jobs (GenerateVideo poll loops, DriveAgent
|
|
3
3
|
* external CLI runs). Mirrors the role `asyncAgentRegistry` plays for background
|
|
4
|
-
* sub-agents, but a job is not an agent — it has no transcript
|
|
5
|
-
*
|
|
4
|
+
* sub-agents, but a job is not an agent — it has no transcript and must NOT
|
|
5
|
+
* show up in AgentStatus.
|
|
6
6
|
*
|
|
7
7
|
* The engine's wait-for-background loop parks the turn until a session has no
|
|
8
8
|
* more RUNNING jobs, so the goal-stop-hook doesn't force the model to busy-loop
|
|
@@ -27,6 +27,31 @@ function isValidSessionId(sid) {
|
|
|
27
27
|
/** Generous per-session cap on retained terminal jobs — a leak backstop, not a
|
|
28
28
|
* UX limit; a human never spawns this many background jobs in one session. */
|
|
29
29
|
const MAX_TERMINAL_JOBS_PER_SESSION = 50;
|
|
30
|
+
const CANCEL_WAIT_TIMEOUT_MS = 5_000;
|
|
31
|
+
function isActiveStatus(status) {
|
|
32
|
+
return status === "running" || status === "cancelling";
|
|
33
|
+
}
|
|
34
|
+
async function waitForAbort(abort) {
|
|
35
|
+
let timer;
|
|
36
|
+
try {
|
|
37
|
+
const result = await Promise.race([
|
|
38
|
+
Promise.resolve().then(abort),
|
|
39
|
+
new Promise((resolve) => {
|
|
40
|
+
timer = setTimeout(() => resolve(undefined), CANCEL_WAIT_TIMEOUT_MS);
|
|
41
|
+
}),
|
|
42
|
+
]);
|
|
43
|
+
return result && typeof result === "object" ? result : undefined;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Cancellation is best-effort; the terminal state still closes after the
|
|
47
|
+
// abort hook settles or the hard deadline expires.
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
if (timer)
|
|
52
|
+
clearTimeout(timer);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
30
55
|
class BackgroundJobRegistry {
|
|
31
56
|
jobs = new Map(); // jobId -> entry (insertion-ordered)
|
|
32
57
|
listeners = new Set();
|
|
@@ -37,10 +62,17 @@ class BackgroundJobRegistry {
|
|
|
37
62
|
this.jobs.set(jobId, {
|
|
38
63
|
jobId,
|
|
39
64
|
sessionId,
|
|
65
|
+
...(options?.kind ? { kind: options.kind } : {}),
|
|
40
66
|
description,
|
|
41
67
|
status: "running",
|
|
42
68
|
startedAt: Date.now(),
|
|
43
69
|
...(options?.cwd ? { cwd: normalizeCwdPath(options.cwd) } : {}),
|
|
70
|
+
...(options?.promptSummary ? { promptSummary: options.promptSummary } : {}),
|
|
71
|
+
...(options?.cli ? { cli: options.cli } : {}),
|
|
72
|
+
...(options?.originClientMessageId
|
|
73
|
+
? { originClientMessageId: options.originClientMessageId }
|
|
74
|
+
: {}),
|
|
75
|
+
...(options?.abort ? { abort: options.abort } : {}),
|
|
44
76
|
});
|
|
45
77
|
this.notify();
|
|
46
78
|
}
|
|
@@ -50,6 +82,8 @@ class BackgroundJobRegistry {
|
|
|
50
82
|
const entry = this.jobs.get(jobId);
|
|
51
83
|
if (!entry)
|
|
52
84
|
return;
|
|
85
|
+
if (entry.status !== "running")
|
|
86
|
+
return;
|
|
53
87
|
entry.status = outcome?.status ?? "completed";
|
|
54
88
|
entry.finishedAt = Date.now();
|
|
55
89
|
if (outcome?.finalText !== undefined)
|
|
@@ -61,22 +95,67 @@ class BackgroundJobRegistry {
|
|
|
61
95
|
this.evictTerminalOverCap(entry.sessionId);
|
|
62
96
|
this.notify();
|
|
63
97
|
}
|
|
64
|
-
|
|
98
|
+
get(jobId) {
|
|
99
|
+
return this.jobs.get(jobId);
|
|
100
|
+
}
|
|
101
|
+
/** Persist artifacts discovered while a running job is winding down. */
|
|
102
|
+
recordArtifacts(jobId, artifacts) {
|
|
103
|
+
const entry = this.jobs.get(jobId);
|
|
104
|
+
if (!entry || !isActiveStatus(entry.status))
|
|
105
|
+
return;
|
|
106
|
+
if (artifacts.ccSessionId !== undefined)
|
|
107
|
+
entry.ccSessionId = artifacts.ccSessionId;
|
|
108
|
+
if (artifacts.changedFiles !== undefined)
|
|
109
|
+
entry.changedFiles = artifacts.changedFiles;
|
|
110
|
+
}
|
|
111
|
+
async cancel(jobId, outcome) {
|
|
112
|
+
const entry = this.jobs.get(jobId);
|
|
113
|
+
if (!entry)
|
|
114
|
+
return false;
|
|
115
|
+
if (entry.status !== "running")
|
|
116
|
+
return false;
|
|
117
|
+
// Publish a non-terminal guard before invoking external code. A completion
|
|
118
|
+
// callback may synchronously re-enter finish(); it must not publish
|
|
119
|
+
// completed/failed, while cwd/session conflict checks must still see the
|
|
120
|
+
// process as active until the abort hook confirms exit.
|
|
121
|
+
entry.status = "cancelling";
|
|
122
|
+
this.notify();
|
|
123
|
+
const abortOutcome = entry.abort ? await waitForAbort(entry.abort) : undefined;
|
|
124
|
+
// Session teardown/reset may have removed the entry while termination was
|
|
125
|
+
// in flight. Do not resurrect or notify for a closed session.
|
|
126
|
+
if (this.jobs.get(jobId) !== entry || entry.status !== "cancelling")
|
|
127
|
+
return false;
|
|
128
|
+
entry.status = abortOutcome?.status ?? "cancelled";
|
|
129
|
+
entry.finishedAt = Date.now();
|
|
130
|
+
const finalText = abortOutcome?.finalText ?? outcome?.finalText;
|
|
131
|
+
const ccSessionId = abortOutcome?.ccSessionId ?? outcome?.ccSessionId;
|
|
132
|
+
const changedFiles = abortOutcome?.changedFiles ?? outcome?.changedFiles;
|
|
133
|
+
if (finalText !== undefined)
|
|
134
|
+
entry.finalText = finalText;
|
|
135
|
+
if (ccSessionId !== undefined)
|
|
136
|
+
entry.ccSessionId = ccSessionId;
|
|
137
|
+
if (changedFiles !== undefined)
|
|
138
|
+
entry.changedFiles = changedFiles;
|
|
139
|
+
this.evictTerminalOverCap(entry.sessionId);
|
|
140
|
+
this.notify();
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
/** True while any running/cancelling job spawned by `sessionId` remains. */
|
|
65
144
|
hasRunningForSession(sessionId) {
|
|
66
145
|
for (const e of this.jobs.values()) {
|
|
67
|
-
if (e.sessionId === sessionId && e.status
|
|
146
|
+
if (e.sessionId === sessionId && isActiveStatus(e.status))
|
|
68
147
|
return true;
|
|
69
148
|
}
|
|
70
149
|
return false;
|
|
71
150
|
}
|
|
72
151
|
/** Running jobs spawned by `sessionId`. Feeds the goal judge's task list. */
|
|
73
152
|
listRunningForSession(sessionId) {
|
|
74
|
-
return [...this.jobs.values()].filter((e) => e.sessionId === sessionId && e.status
|
|
153
|
+
return [...this.jobs.values()].filter((e) => e.sessionId === sessionId && isActiveStatus(e.status));
|
|
75
154
|
}
|
|
76
155
|
/** Running jobs, across all sessions, that are operating in the same cwd. */
|
|
77
156
|
listRunningByCwd(cwd) {
|
|
78
157
|
const normalized = normalizeCwdPath(cwd);
|
|
79
|
-
return [...this.jobs.values()].filter((e) => e.status
|
|
158
|
+
return [...this.jobs.values()].filter((e) => isActiveStatus(e.status) && e.cwd === normalized);
|
|
80
159
|
}
|
|
81
160
|
/** All jobs (running + retained terminal) for `sessionId`. Feeds the panel. */
|
|
82
161
|
listForSession(sessionId) {
|
|
@@ -89,12 +168,26 @@ class BackgroundJobRegistry {
|
|
|
89
168
|
/** Drop every job of a session — called when the session is deleted/closed. */
|
|
90
169
|
dropForSession(sessionId) {
|
|
91
170
|
let removed = false;
|
|
171
|
+
const aborts = [];
|
|
92
172
|
for (const [id, e] of this.jobs) {
|
|
93
173
|
if (e.sessionId === sessionId) {
|
|
174
|
+
// Delete first so a synchronous/queued completion caused by abort sees
|
|
175
|
+
// no live registry entry and cannot publish a late result for a closed
|
|
176
|
+
// session. Terminal jobs have nothing left to stop.
|
|
94
177
|
this.jobs.delete(id);
|
|
178
|
+
if (isActiveStatus(e.status) && e.abort)
|
|
179
|
+
aborts.push(e.abort);
|
|
95
180
|
removed = true;
|
|
96
181
|
}
|
|
97
182
|
}
|
|
183
|
+
for (const abort of aborts) {
|
|
184
|
+
try {
|
|
185
|
+
void Promise.resolve(abort()).catch(() => undefined);
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
// teardown is best-effort across jobs; continue reaping the rest
|
|
189
|
+
}
|
|
190
|
+
}
|
|
98
191
|
if (removed)
|
|
99
192
|
this.notify();
|
|
100
193
|
}
|
|
@@ -106,12 +199,21 @@ class BackgroundJobRegistry {
|
|
|
106
199
|
};
|
|
107
200
|
/** Test helper: drop all tracked jobs. */
|
|
108
201
|
reset() {
|
|
202
|
+
const aborts = [...this.jobs.values()].flatMap((entry) => isActiveStatus(entry.status) && entry.abort ? [entry.abort] : []);
|
|
109
203
|
this.jobs.clear();
|
|
204
|
+
for (const abort of aborts) {
|
|
205
|
+
try {
|
|
206
|
+
void Promise.resolve(abort()).catch(() => undefined);
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
// ignore
|
|
210
|
+
}
|
|
211
|
+
}
|
|
110
212
|
}
|
|
111
213
|
/** Backstop: keep at most MAX_TERMINAL_JOBS_PER_SESSION terminal jobs per
|
|
112
214
|
* session, evicting the oldest (insertion order). Running jobs never evicted. */
|
|
113
215
|
evictTerminalOverCap(sessionId) {
|
|
114
|
-
const terminal = [...this.jobs.values()].filter((e) => e.sessionId === sessionId && e.status
|
|
216
|
+
const terminal = [...this.jobs.values()].filter((e) => e.sessionId === sessionId && !isActiveStatus(e.status));
|
|
115
217
|
const over = terminal.length - MAX_TERMINAL_JOBS_PER_SESSION;
|
|
116
218
|
if (over <= 0)
|
|
117
219
|
return;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AsyncAgentStatus } from "./agent-registry.js";
|
|
2
|
-
import { type BackgroundJobStatus } from "./background-jobs.js";
|
|
2
|
+
import { type BackgroundJobKind, type BackgroundJobStatus, type ExternalCliKind } from "./background-jobs.js";
|
|
3
3
|
import { type BgShell } from "../../runtime/background-shell.js";
|
|
4
4
|
export type BackgroundWorkKind = "subagent" | "job" | "shell";
|
|
5
5
|
export interface BackgroundWorkItem {
|
|
@@ -51,6 +51,11 @@ export type BackgroundWorkEntry = WithSource<{
|
|
|
51
51
|
finalText?: string;
|
|
52
52
|
/** Files an external agent (DriveAgent) changed, parsed from its transcript. */
|
|
53
53
|
changedFiles?: string[];
|
|
54
|
+
/** Machine-readable job kind and external-session linkage for DriveAgent. */
|
|
55
|
+
jobKind?: BackgroundJobKind;
|
|
56
|
+
externalSessionId?: string;
|
|
57
|
+
cli?: ExternalCliKind;
|
|
58
|
+
cwd?: string;
|
|
54
59
|
}>;
|
|
55
60
|
/**
|
|
56
61
|
* Every background-work item spawned by `sessionId`, with per-kind detail, for
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import { readFileSync, statSync } from "node:fs";
|
|
15
15
|
import { join } from "node:path";
|
|
16
16
|
import { asyncAgentRegistry } from "./agent-registry.js";
|
|
17
|
-
import { backgroundJobRegistry } from "./background-jobs.js";
|
|
17
|
+
import { backgroundJobRegistry, } from "./background-jobs.js";
|
|
18
18
|
import { backgroundShellManager } from "../../runtime/background-shell.js";
|
|
19
19
|
import { codeShellHome } from "../../session/session-manager.js";
|
|
20
20
|
/** List every still-running background work item spawned by `sessionId`. */
|
|
@@ -125,6 +125,10 @@ export function listBackgroundWorkForUI(sessionId, opts = {}) {
|
|
|
125
125
|
...(j.finishedAt != null ? { finishedAt: j.finishedAt } : {}),
|
|
126
126
|
...(j.finalText != null ? { finalText: j.finalText } : {}),
|
|
127
127
|
...(j.changedFiles && j.changedFiles.length ? { changedFiles: j.changedFiles } : {}),
|
|
128
|
+
...(j.kind ? { jobKind: j.kind } : {}),
|
|
129
|
+
...(j.ccSessionId ? { externalSessionId: j.ccSessionId } : {}),
|
|
130
|
+
...(j.cli === "claude" || j.cli === "codex" ? { cli: j.cli } : {}),
|
|
131
|
+
...(j.cwd ? { cwd: j.cwd } : {}),
|
|
128
132
|
sourceSession: sourceSession(sessionId, j.sessionId),
|
|
129
133
|
});
|
|
130
134
|
}
|
|
@@ -16,9 +16,7 @@
|
|
|
16
16
|
* Killed by signal, the truncation message, sandbox denial hints).
|
|
17
17
|
*/
|
|
18
18
|
import type { ToolContext } from "../context.js";
|
|
19
|
-
import type { ToolDefinition
|
|
19
|
+
import type { ToolDefinition } from "../../types.js";
|
|
20
|
+
import type { ToolExecutionResult } from "./index.js";
|
|
20
21
|
export declare const bashToolDef: ToolDefinition;
|
|
21
|
-
export declare function bashTool(args: Record<string, unknown>, ctx?: ToolContext): Promise<string |
|
|
22
|
-
result: string;
|
|
23
|
-
sandbox: ToolResult["sandbox"];
|
|
24
|
-
}>;
|
|
22
|
+
export declare function bashTool(args: Record<string, unknown>, ctx?: ToolContext): Promise<string | ToolExecutionResult>;
|
|
@@ -82,7 +82,8 @@ export async function bashTool(args, ctx) {
|
|
|
82
82
|
const sandbox = sandboxMark(backend);
|
|
83
83
|
// Tag every textual return below with the sandbox mark so the UI badge
|
|
84
84
|
// shows up whether the command succeeded, failed, timed out, or aborted.
|
|
85
|
-
const mark = (result) => ({ result, sandbox });
|
|
85
|
+
const mark = (result) => ({ ok: true, result, sandbox });
|
|
86
|
+
const fail = (error, details = {}) => ({ ok: false, error, sandbox, ...details });
|
|
86
87
|
// Project localEnvironment.env (ctx.shellEnv) layers on top of either the
|
|
87
88
|
// full passthrough (off) or the hardened allowlist (sandboxed) — see
|
|
88
89
|
// mergeShellEnv. Background mode merges the same way inside the manager.
|
|
@@ -93,7 +94,8 @@ export async function bashTool(args, ctx) {
|
|
|
93
94
|
// blocked, and Engine.run's wait-for-background loop never waits on it
|
|
94
95
|
// (§难点5: that loop only looks at asyncAgentRegistry).
|
|
95
96
|
if (args.run_in_background === true) {
|
|
96
|
-
|
|
97
|
+
const backgroundResult = runInBackground(command, ctx);
|
|
98
|
+
return backgroundResult.startsWith("Error:") ? fail(backgroundResult) : mark(backgroundResult);
|
|
97
99
|
}
|
|
98
100
|
// A6: lifecycle (spawn, kill cascade, IO drain, byte cap, abort/timeout)
|
|
99
101
|
// is centralized in safeSpawnShell. We still handle Bash's user-facing
|
|
@@ -112,13 +114,13 @@ export async function bashTool(args, ctx) {
|
|
|
112
114
|
signal: ctx?.signal,
|
|
113
115
|
});
|
|
114
116
|
if (result.aborted) {
|
|
115
|
-
return
|
|
117
|
+
return fail(wasAbortedBeforeStart ? "Bash aborted before starting." : "Bash aborted by signal.");
|
|
116
118
|
}
|
|
117
119
|
if (result.timedOut) {
|
|
118
|
-
return
|
|
120
|
+
return fail(`Command timed out after ${timeout}ms`);
|
|
119
121
|
}
|
|
120
122
|
if (result.spawnFailed) {
|
|
121
|
-
return
|
|
123
|
+
return fail(`Failed to spawn command: ${result.error ?? "unknown error"}`);
|
|
122
124
|
}
|
|
123
125
|
let body = "";
|
|
124
126
|
if (result.stdout)
|
|
@@ -150,6 +152,9 @@ export async function bashTool(args, ctx) {
|
|
|
150
152
|
const hint = backend.hintForBlockedOutput?.(result.stderr);
|
|
151
153
|
if (hint)
|
|
152
154
|
output += hint;
|
|
155
|
+
if ((code !== 0 && code !== null) || (code === null && sig)) {
|
|
156
|
+
return fail(output, { exitCode: code, signal: sig, stderr: result.stderr });
|
|
157
|
+
}
|
|
153
158
|
return mark(output);
|
|
154
159
|
}
|
|
155
160
|
/**
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import type { ToolDefinition } from "../../types.js";
|
|
24
24
|
import type { ToolContext } from "../context.js";
|
|
25
|
-
import type {
|
|
25
|
+
import type { BuiltinToolReturn } from "./index.js";
|
|
26
26
|
export declare const browserObserveToolDef: ToolDefinition;
|
|
27
|
-
export declare function browserObserveTool(args: Record<string, unknown>, ctx?: ToolContext): Promise<
|
|
27
|
+
export declare function browserObserveTool(args: Record<string, unknown>, ctx?: ToolContext): Promise<BuiltinToolReturn>;
|
|
28
28
|
export declare const browserActToolDef: ToolDefinition;
|
|
29
29
|
export declare function browserActTool(args: Record<string, unknown>, ctx?: ToolContext): Promise<string>;
|
|
30
30
|
export declare const browserNavigateToolDef: ToolDefinition;
|