@sayknow-cli/coding-agent 0.3.7 → 0.3.8
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/types/append-only-mode.d.ts +31 -0
- package/dist/types/capability/rule.d.ts +8 -0
- package/dist/types/cli/completion-cli.d.ts +41 -0
- package/dist/types/cli/config-cli.d.ts +1 -0
- package/dist/types/cli/fixture-report.d.ts +20 -0
- package/dist/types/cli/fixture-report.test.d.ts +1 -0
- package/dist/types/cli/nofile-limit.d.ts +11 -0
- package/dist/types/cli/notify-cli.d.ts +7 -0
- package/dist/types/cli/update-cli.d.ts +22 -0
- package/dist/types/cli.d.ts +124 -1
- package/dist/types/commands/completion.d.ts +29 -0
- package/dist/types/commands/config.d.ts +3 -0
- package/dist/types/commands/deep-interview.d.ts +3 -0
- package/dist/types/config/file-lock.d.ts +7 -6
- package/dist/types/config/keybindings.d.ts +2 -2
- package/dist/types/config/model-profile-activation.d.ts +6 -0
- package/dist/types/config/model-profiles.d.ts +1 -0
- package/dist/types/config/settings-schema.d.ts +105 -1
- package/dist/types/config/settings.d.ts +8 -0
- package/dist/types/discovery/index.d.ts +1 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/export/ttsr.d.ts +14 -3
- package/dist/types/extensibility/extensions/runner.d.ts +2 -1
- package/dist/types/extensibility/extensions/types.d.ts +8 -0
- package/dist/types/goals/runtime.d.ts +1 -0
- package/dist/types/hooks/native-skill-hook.d.ts +12 -0
- package/dist/types/main.d.ts +4 -1
- package/dist/types/modes/components/assistant-message.d.ts +5 -1
- package/dist/types/modes/components/model-selector.d.ts +4 -0
- package/dist/types/modes/components/queued-message-selector.d.ts +12 -0
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/status-line.d.ts +9 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/event-controller.d.ts +5 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -3
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/print-mode.d.ts +13 -1
- package/dist/types/modes/prompt-action-autocomplete.d.ts +1 -1
- package/dist/types/modes/rpc/rpc-types.d.ts +14 -0
- package/dist/types/modes/shared/agent-wire/event-contract.d.ts +19 -0
- package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +23 -1
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +1 -0
- package/dist/types/notifications/config.d.ts +14 -1
- package/dist/types/notifications/daemon-paths.d.ts +1 -0
- package/dist/types/notifications/index.d.ts +6 -3
- package/dist/types/notifications/lifecycle-commands.d.ts +11 -3
- package/dist/types/notifications/telegram-daemon.d.ts +23 -3
- package/dist/types/notifications/telegram-reference.d.ts +0 -5
- package/dist/types/notifications/threaded-render.d.ts +6 -0
- package/dist/types/runtime-api-key.d.ts +7 -0
- package/dist/types/sdk.d.ts +3 -0
- package/dist/types/session/agent-session.d.ts +61 -6
- package/dist/types/session/agent-storage.d.ts +1 -0
- package/dist/types/session/file-mention-pruning.d.ts +8 -0
- package/dist/types/session/messages.d.ts +4 -0
- package/dist/types/session/session-manager.d.ts +17 -1
- package/dist/types/skc-runtime/deep-interview-ambiguity.d.ts +39 -0
- package/dist/types/skc-runtime/deep-interview-recorder.d.ts +10 -1
- package/dist/types/skc-runtime/deep-interview-state.d.ts +17 -2
- package/dist/types/skc-runtime/session-state-sidecar.d.ts +7 -0
- package/dist/types/skc-runtime/state-writer.d.ts +3 -0
- package/dist/types/skc-runtime/ultragoal-receipt-freshness.d.ts +42 -0
- package/dist/types/skc-runtime/ultragoal-runtime.d.ts +120 -13
- package/dist/types/skill-state/active-state.d.ts +18 -1
- package/dist/types/system-prompt.d.ts +18 -2
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/task/fork-context-budget.d.ts +7 -0
- package/dist/types/task/token-log.d.ts +30 -0
- package/dist/types/task/token-log.test.d.ts +1 -0
- package/dist/types/tools/bisect.d.ts +81 -0
- package/dist/types/tools/computer.d.ts +2 -2
- package/dist/types/tools/index.d.ts +1 -0
- package/dist/types/tools/search.d.ts +2 -2
- package/dist/types/tools/ultragoal-ask-guard.d.ts +9 -2
- package/dist/types/utils/file-mentions.d.ts +14 -0
- package/dist/types/utils/git.d.ts +32 -0
- package/dist/types/web/scrapers/youtube.d.ts +8 -0
- package/dist/types/workflow/workflow-intent-diff.d.ts +8 -2
- package/dist/types/workflow/workflow-intent-report-consumer.d.ts +50 -0
- package/dist/types/workflow/workflow-intent-report-schema.d.ts +2 -0
- package/dist/types/workflow/workflow-intent-report.d.ts +48 -0
- package/package.json +8 -8
- package/src/append-only-mode.ts +58 -0
- package/src/autoresearch/prompt.md +1 -1
- package/src/autoresearch/tools/log-experiment.ts +1 -1
- package/src/capability/rule.ts +9 -0
- package/src/cli/completion-cli.ts +234 -0
- package/src/cli/config-cli.ts +61 -9
- package/src/cli/fast-help.ts +1 -0
- package/src/cli/fixture-report.test.ts +230 -0
- package/src/cli/fixture-report.ts +171 -0
- package/src/cli/nofile-limit.ts +48 -0
- package/src/cli/notify-cli.ts +92 -8
- package/src/cli/telegram-cli.ts +1 -1
- package/src/cli/update-cli.ts +85 -9
- package/src/cli.ts +29 -1
- package/src/commands/completion.ts +84 -0
- package/src/commands/config.ts +2 -0
- package/src/commands/contribution-prep.ts +1 -1
- package/src/commands/deep-interview.ts +2 -1
- package/src/config/file-lock.ts +73 -34
- package/src/config/keybindings.ts +2 -2
- package/src/config/model-profile-activation.ts +65 -0
- package/src/config/model-profiles.ts +31 -2
- package/src/config/settings-schema.ts +68 -1
- package/src/config/settings.ts +22 -0
- package/src/defaults/skc/skills/team/SKILL.md +17 -14
- package/src/defaults/skc/skills/ultragoal/SKILL.md +59 -4
- package/src/defaults/skc/skills/ultragoal/pipeline-validation-contracts.md +30 -0
- package/src/defaults/skc-defaults.ts +9 -0
- package/src/discovery/helpers.ts +10 -0
- package/src/discovery/index.ts +1 -0
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +60 -2
- package/src/export/ttsr.ts +96 -18
- package/src/extensibility/extensions/runner.ts +183 -219
- package/src/extensibility/extensions/types.ts +9 -0
- package/src/extensibility/skc-plugins/runtime-adapters.ts +156 -44
- package/src/goals/runtime.ts +10 -2
- package/src/hindsight/backend.ts +1 -3
- package/src/hooks/native-skill-hook.ts +124 -4
- package/src/internal-urls/docs-index.generated.ts +14 -5
- package/src/main.ts +95 -43
- package/src/memories/index.ts +12 -1
- package/src/modes/components/assistant-message.ts +241 -82
- package/src/modes/components/custom-editor.ts +1 -0
- package/src/modes/components/model-selector.ts +54 -11
- package/src/modes/components/queued-message-selector.ts +92 -0
- package/src/modes/components/settings-selector.ts +4 -0
- package/src/modes/components/status-line.ts +167 -54
- package/src/modes/components/tool-execution.ts +44 -19
- package/src/modes/components/welcome.ts +181 -44
- package/src/modes/controllers/command-controller.ts +40 -4
- package/src/modes/controllers/event-controller.ts +66 -47
- package/src/modes/controllers/extension-ui-controller.ts +4 -10
- package/src/modes/controllers/input-controller.ts +180 -18
- package/src/modes/controllers/selector-controller.ts +127 -24
- package/src/modes/interactive-mode.ts +51 -16
- package/src/modes/print-mode.ts +43 -4
- package/src/modes/prompt-action-autocomplete.ts +21 -20
- package/src/modes/rpc/rpc-mode.ts +29 -3
- package/src/modes/rpc/rpc-types.ts +9 -0
- package/src/modes/shared/agent-wire/command-validation.ts +4 -0
- package/src/modes/shared/agent-wire/event-contract.ts +22 -0
- package/src/modes/shared/agent-wire/event-envelope.ts +82 -0
- package/src/modes/shared/agent-wire/scopes.ts +1 -0
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +20 -7
- package/src/notifications/config.ts +25 -5
- package/src/notifications/daemon-paths.ts +2 -0
- package/src/notifications/index.ts +117 -14
- package/src/notifications/lifecycle-commands.ts +37 -7
- package/src/notifications/lifecycle-orchestrator.ts +19 -6
- package/src/notifications/telegram-daemon-control.ts +5 -3
- package/src/notifications/telegram-daemon.ts +302 -51
- package/src/notifications/telegram-reference.ts +0 -16
- package/src/notifications/threaded-render.ts +17 -1
- package/src/prompts/agents/architect.md +19 -10
- package/src/prompts/agents/critic.md +16 -18
- package/src/prompts/agents/executor.md +14 -11
- package/src/prompts/agents/planner.md +18 -13
- package/src/prompts/ci-green-request.md +3 -8
- package/src/prompts/goals/goal-continuation.md +0 -5
- package/src/prompts/goals/goal-mode-active.md +0 -5
- package/src/prompts/system/agent-creation-architect.md +2 -2
- package/src/prompts/system/plan-mode-active.md +1 -1
- package/src/prompts/system/plan-mode-subagent.md +3 -3
- package/src/prompts/system/project-prompt.md +0 -13
- package/src/prompts/system/subagent-system-prompt.md +3 -2
- package/src/prompts/system/system-prompt.md +29 -17
- package/src/prompts/system/volatile-project-context.md +10 -0
- package/src/prompts/system/web-search.md +4 -7
- package/src/prompts/tools/apply-patch.md +6 -0
- package/src/prompts/tools/ast-edit.md +1 -0
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/bisect.md +22 -0
- package/src/prompts/tools/browser.md +1 -1
- package/src/prompts/tools/computer.md +2 -2
- package/src/prompts/tools/cron.md +0 -2
- package/src/prompts/tools/eval.md +2 -2
- package/src/prompts/tools/find.md +1 -1
- package/src/prompts/tools/github.md +6 -6
- package/src/prompts/tools/goal.md +1 -3
- package/src/prompts/tools/image-gen.md +12 -5
- package/src/prompts/tools/irc.md +5 -4
- package/src/prompts/tools/job.md +2 -2
- package/src/prompts/tools/lsp.md +1 -1
- package/src/prompts/tools/monitor.md +2 -2
- package/src/prompts/tools/patch.md +2 -2
- package/src/prompts/tools/replace.md +2 -21
- package/src/prompts/tools/rewind.md +1 -1
- package/src/prompts/tools/search-tool-bm25.md +1 -11
- package/src/prompts/tools/search.md +1 -1
- package/src/prompts/tools/skill.md +3 -2
- package/src/prompts/tools/ssh.md +2 -1
- package/src/prompts/tools/subagent.md +2 -2
- package/src/prompts/tools/task.md +4 -5
- package/src/prompts/tools/todo-write.md +1 -1
- package/src/prompts/tools/vim.md +1 -0
- package/src/prompts/tools/web-search.md +2 -0
- package/src/prompts/tools/write.md +8 -2
- package/src/runtime-api-key.ts +16 -0
- package/src/sdk.ts +52 -28
- package/src/session/agent-session.ts +1011 -141
- package/src/session/agent-storage.ts +4 -0
- package/src/session/file-mention-pruning.ts +64 -0
- package/src/session/messages.ts +4 -0
- package/src/session/session-manager.ts +103 -4
- package/src/skc-runtime/deep-interview-ambiguity.ts +206 -0
- package/src/skc-runtime/deep-interview-recorder.ts +62 -8
- package/src/skc-runtime/deep-interview-state.ts +44 -2
- package/src/skc-runtime/session-state-sidecar.ts +263 -20
- package/src/skc-runtime/state-runtime.ts +12 -2
- package/src/skc-runtime/state-writer.ts +15 -0
- package/src/skc-runtime/ultragoal-guard.ts +55 -66
- package/src/skc-runtime/ultragoal-receipt-freshness.ts +315 -0
- package/src/skc-runtime/ultragoal-runtime.ts +1663 -159
- package/src/skc-runtime/workflow-manifest.generated.json +113 -2
- package/src/skc-runtime/workflow-manifest.ts +74 -1
- package/src/skill-state/active-state.ts +141 -5
- package/src/skill-state/deep-interview-mutation-guard.ts +67 -3
- package/src/slash-commands/builtin-registry.ts +218 -71
- package/src/system-prompt.ts +57 -16
- package/src/task/executor.ts +132 -24
- package/src/task/fork-context-advisory.ts +2 -7
- package/src/task/fork-context-budget.ts +9 -0
- package/src/task/index.ts +13 -6
- package/src/task/output-manager.ts +20 -8
- package/src/task/receipt.ts +26 -7
- package/src/task/token-log.test.ts +188 -0
- package/src/task/token-log.ts +179 -0
- package/src/tools/bisect.ts +367 -0
- package/src/tools/computer.ts +246 -43
- package/src/tools/index.ts +3 -0
- package/src/tools/output-meta.ts +116 -4
- package/src/tools/search.ts +8 -2
- package/src/tools/skill.ts +15 -0
- package/src/tools/telegram-send.ts +6 -0
- package/src/tools/ultragoal-ask-guard.ts +39 -5
- package/src/utils/file-mentions.ts +39 -7
- package/src/utils/git.ts +73 -0
- package/src/web/scrapers/youtube.ts +24 -12
- package/src/workflow/workflow-intent-diff.ts +29 -6
- package/src/workflow/workflow-intent-report-consumer.ts +221 -0
- package/src/workflow/workflow-intent-report-schema.ts +129 -0
- package/src/workflow/workflow-intent-report.ts +135 -0
package/src/tools/computer.ts
CHANGED
|
@@ -98,7 +98,7 @@ export interface ComputerScreenshotDetails {
|
|
|
98
98
|
scaleY?: number;
|
|
99
99
|
originX?: number;
|
|
100
100
|
originY?: number;
|
|
101
|
-
displayEpoch?:
|
|
101
|
+
displayEpoch?: number;
|
|
102
102
|
captureId?: string;
|
|
103
103
|
pngBytes?: number;
|
|
104
104
|
path?: string;
|
|
@@ -144,7 +144,7 @@ type NativeScreenshot = {
|
|
|
144
144
|
scaleY?: number;
|
|
145
145
|
originX?: number;
|
|
146
146
|
originY?: number;
|
|
147
|
-
displayEpoch?:
|
|
147
|
+
displayEpoch?: number;
|
|
148
148
|
captureId?: string;
|
|
149
149
|
};
|
|
150
150
|
|
|
@@ -175,6 +175,7 @@ let controllerFactory: ComputerControllerFactory = createNativeComputerControlle
|
|
|
175
175
|
let platformOverrideForTests: NodeJS.Platform | undefined;
|
|
176
176
|
let archOverrideForTests: NodeJS.Architecture | undefined;
|
|
177
177
|
const screenshotFallbackDirs = new WeakMap<ToolSession, Promise<string>>();
|
|
178
|
+
const latestScreenshotContexts = new WeakMap<ToolSession, ScreenshotContext>();
|
|
178
179
|
|
|
179
180
|
const COMPUTER_INLINE_SCREENSHOT_MAX_WIDTH = 1568;
|
|
180
181
|
const COMPUTER_INLINE_SCREENSHOT_MAX_HEIGHT = 1568;
|
|
@@ -275,12 +276,24 @@ export class ComputerTool implements AgentTool<typeof computerSchema, ComputerTo
|
|
|
275
276
|
const timeoutSeconds = clampTimeout("computer", params.timeout);
|
|
276
277
|
const timeoutMs = timeoutSeconds > 0 ? timeoutSeconds * 1000 : undefined;
|
|
277
278
|
const controller = controllerFactory();
|
|
278
|
-
|
|
279
|
-
// so cancellation is honored before dispatch and wait() is bounded by timeoutMs.
|
|
279
|
+
const deadline = createComputerDeadline(timeoutMs);
|
|
280
280
|
if (params.action === "batch") {
|
|
281
|
-
const batchResult = await dispatchBatchComputerActions(
|
|
281
|
+
const batchResult = await dispatchBatchComputerActions(
|
|
282
|
+
controller,
|
|
283
|
+
params.actions,
|
|
284
|
+
timeoutMs,
|
|
285
|
+
hotkey,
|
|
286
|
+
latestScreenshotContexts.get(this.session),
|
|
287
|
+
shouldCapturePostActionScreenshot(params, this.session),
|
|
288
|
+
Boolean(this.session.settings.get("computer.autoScreenshot")),
|
|
289
|
+
signal,
|
|
290
|
+
deadline,
|
|
291
|
+
);
|
|
282
292
|
details.steps = batchResult.steps;
|
|
283
|
-
if (batchResult.screenshot)
|
|
293
|
+
if (batchResult.screenshot) {
|
|
294
|
+
details.screenshot = batchResult.screenshot;
|
|
295
|
+
rememberLatestScreenshot(this.session, batchResult.screenshot);
|
|
296
|
+
}
|
|
284
297
|
details.status = batchResult.failedStep ? "error" : "success";
|
|
285
298
|
if (batchResult.failedStep) {
|
|
286
299
|
details.code = batchResult.failedStep.code;
|
|
@@ -307,9 +320,21 @@ export class ComputerTool implements AgentTool<typeof computerSchema, ComputerTo
|
|
|
307
320
|
.done()
|
|
308
321
|
: toolResult(details).text(details.message).done();
|
|
309
322
|
}
|
|
310
|
-
|
|
323
|
+
let result = await dispatchComputerAction(
|
|
324
|
+
controller,
|
|
325
|
+
params,
|
|
326
|
+
deadline,
|
|
327
|
+
latestScreenshotContexts.get(this.session),
|
|
328
|
+
signal,
|
|
329
|
+
);
|
|
330
|
+
if (shouldCapturePostActionScreenshot(params, this.session)) {
|
|
331
|
+
result = await captureScreenshot(controller, deadline, signal);
|
|
332
|
+
}
|
|
311
333
|
const screenshot = normalizeScreenshot(result);
|
|
312
|
-
if (screenshot)
|
|
334
|
+
if (screenshot) {
|
|
335
|
+
details.screenshot = screenshot;
|
|
336
|
+
rememberLatestScreenshot(this.session, screenshot);
|
|
337
|
+
}
|
|
313
338
|
details.status = "success";
|
|
314
339
|
details.message = describeComputerSuccess(details);
|
|
315
340
|
if (screenshot) {
|
|
@@ -342,6 +367,10 @@ interface CoordinateBounds {
|
|
|
342
367
|
originY?: number;
|
|
343
368
|
}
|
|
344
369
|
|
|
370
|
+
interface ScreenshotContext extends CoordinateBounds {
|
|
371
|
+
displayEpoch?: number;
|
|
372
|
+
}
|
|
373
|
+
|
|
345
374
|
function validatePointerCoordinates(action: string, x: number, y: number, bounds: CoordinateBounds | undefined): void {
|
|
346
375
|
if (!bounds) return;
|
|
347
376
|
const minX = bounds.originX ?? 0;
|
|
@@ -355,42 +384,189 @@ function validatePointerCoordinates(action: string, x: number, y: number, bounds
|
|
|
355
384
|
}
|
|
356
385
|
}
|
|
357
386
|
|
|
358
|
-
function
|
|
387
|
+
function expectedEpochFromContext(context: ScreenshotContext | undefined): number | undefined {
|
|
388
|
+
return typeof context?.displayEpoch === "number" &&
|
|
389
|
+
Number.isFinite(context.displayEpoch) &&
|
|
390
|
+
context.displayEpoch >= 0
|
|
391
|
+
? context.displayEpoch
|
|
392
|
+
: undefined;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function rememberLatestScreenshot(session: ToolSession, screenshot: ComputerScreenshotDetails): void {
|
|
396
|
+
latestScreenshotContexts.set(session, {
|
|
397
|
+
widthPx: screenshot.widthPx,
|
|
398
|
+
heightPx: screenshot.heightPx,
|
|
399
|
+
originX: screenshot.originX,
|
|
400
|
+
originY: screenshot.originY,
|
|
401
|
+
displayEpoch: screenshot.displayEpoch,
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function captureScreenshot(
|
|
359
406
|
controller: NativeController,
|
|
360
|
-
|
|
407
|
+
deadline: ComputerDeadline | undefined,
|
|
408
|
+
signal?: AbortSignal,
|
|
409
|
+
): Promise<unknown> {
|
|
410
|
+
return runComputerOperation(
|
|
411
|
+
() => {
|
|
412
|
+
if (!controller.screenshot) missingNativeMethod("screenshot", "screenshot");
|
|
413
|
+
return controller.screenshot();
|
|
414
|
+
},
|
|
415
|
+
deadline,
|
|
416
|
+
signal,
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function missingNativeMethod(action: string, method: string): never {
|
|
421
|
+
throw new ToolError(`COMPUTER_UNAVAILABLE: Native ComputerController.${method} is unavailable for ${action}.`, {
|
|
422
|
+
code: "COMPUTER_UNAVAILABLE",
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function shouldCapturePostActionScreenshot(
|
|
427
|
+
params: Pick<ComputerParams, "action" | "include_screenshot">,
|
|
428
|
+
session: Pick<ToolSession, "settings">,
|
|
429
|
+
): boolean {
|
|
430
|
+
return (
|
|
431
|
+
params.action !== "screenshot" &&
|
|
432
|
+
(params.include_screenshot === true || Boolean(session.settings.get("computer.autoScreenshot")))
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
interface ComputerDeadline {
|
|
437
|
+
expiresAtMs: number;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
class ComputerTimeoutError extends Error {
|
|
441
|
+
constructor() {
|
|
442
|
+
super("Computer action timed out.");
|
|
443
|
+
this.name = "TimeoutError";
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function createComputerDeadline(
|
|
361
448
|
timeoutMs: number | undefined,
|
|
362
|
-
|
|
363
|
-
):
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
449
|
+
parent?: ComputerDeadline,
|
|
450
|
+
): ComputerDeadline | undefined {
|
|
451
|
+
const localExpiresAt = timeoutMs && timeoutMs > 0 ? performance.now() + timeoutMs : undefined;
|
|
452
|
+
const parentExpiresAt = parent?.expiresAtMs;
|
|
453
|
+
const expiresAtMs =
|
|
454
|
+
localExpiresAt === undefined
|
|
455
|
+
? parentExpiresAt
|
|
456
|
+
: parentExpiresAt === undefined
|
|
457
|
+
? localExpiresAt
|
|
458
|
+
: Math.min(localExpiresAt, parentExpiresAt);
|
|
459
|
+
return expiresAtMs === undefined ? undefined : { expiresAtMs };
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function remainingComputerTimeoutMs(deadline: ComputerDeadline | undefined): number | undefined {
|
|
463
|
+
if (!deadline) return undefined;
|
|
464
|
+
const remaining = Math.ceil(deadline.expiresAtMs - performance.now());
|
|
465
|
+
if (remaining <= 0) throw new ComputerTimeoutError();
|
|
466
|
+
return remaining;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function assertComputerDeadline(deadline: ComputerDeadline | undefined): void {
|
|
470
|
+
remainingComputerTimeoutMs(deadline);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
async function runComputerOperation<T>(
|
|
474
|
+
operation: () => Promise<T> | T,
|
|
475
|
+
deadline: ComputerDeadline | undefined,
|
|
476
|
+
signal?: AbortSignal,
|
|
477
|
+
): Promise<T> {
|
|
478
|
+
throwIfAborted(signal);
|
|
479
|
+
const timeoutMs = remainingComputerTimeoutMs(deadline);
|
|
480
|
+
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
481
|
+
let removeAbortListener: (() => void) | undefined;
|
|
482
|
+
const operationPromise = Promise.resolve().then(operation);
|
|
483
|
+
operationPromise.catch(() => undefined);
|
|
484
|
+
const guards: Array<Promise<never>> = [];
|
|
485
|
+
if (timeoutMs !== undefined) {
|
|
486
|
+
guards.push(
|
|
487
|
+
new Promise((_, reject) => {
|
|
488
|
+
timeout = setTimeout(() => reject(new ComputerTimeoutError()), timeoutMs);
|
|
489
|
+
}),
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
if (signal) {
|
|
493
|
+
guards.push(
|
|
494
|
+
new Promise((_, reject) => {
|
|
495
|
+
const onAbort = () => reject(new ToolAbortError());
|
|
496
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
497
|
+
removeAbortListener = () => signal.removeEventListener("abort", onAbort);
|
|
498
|
+
}),
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
try {
|
|
502
|
+
const result = await (guards.length > 0 ? Promise.race([operationPromise, ...guards]) : operationPromise);
|
|
503
|
+
throwIfAborted(signal);
|
|
504
|
+
assertComputerDeadline(deadline);
|
|
505
|
+
return result;
|
|
506
|
+
} finally {
|
|
507
|
+
if (timeout) clearTimeout(timeout);
|
|
508
|
+
removeAbortListener?.();
|
|
391
509
|
}
|
|
392
510
|
}
|
|
393
511
|
|
|
512
|
+
function dispatchComputerAction(
|
|
513
|
+
controller: NativeController,
|
|
514
|
+
params: SingleComputerParams,
|
|
515
|
+
deadline: ComputerDeadline | undefined,
|
|
516
|
+
context?: ScreenshotContext,
|
|
517
|
+
signal?: AbortSignal,
|
|
518
|
+
): Promise<unknown> {
|
|
519
|
+
const expectedEpoch = expectedEpochFromContext(context);
|
|
520
|
+
return runComputerOperation(
|
|
521
|
+
() => {
|
|
522
|
+
switch (params.action) {
|
|
523
|
+
case "screenshot":
|
|
524
|
+
if (!controller.screenshot) missingNativeMethod("screenshot", "screenshot");
|
|
525
|
+
return controller.screenshot();
|
|
526
|
+
case "click":
|
|
527
|
+
validatePointerCoordinates("click", params.x, params.y, context);
|
|
528
|
+
if (!controller.click) missingNativeMethod("click", "click");
|
|
529
|
+
return controller.click(expectedEpoch, params.x, params.y, params.button ?? "left");
|
|
530
|
+
case "double_click":
|
|
531
|
+
validatePointerCoordinates("double_click", params.x, params.y, context);
|
|
532
|
+
if (!controller.doubleClick) missingNativeMethod("double_click", "doubleClick");
|
|
533
|
+
return controller.doubleClick(expectedEpoch, params.x, params.y, params.button ?? "left");
|
|
534
|
+
case "move":
|
|
535
|
+
validatePointerCoordinates("move", params.x, params.y, context);
|
|
536
|
+
if (!controller.move) missingNativeMethod("move", "move");
|
|
537
|
+
return controller.move(expectedEpoch, params.x, params.y);
|
|
538
|
+
case "drag":
|
|
539
|
+
validatePointerCoordinates("drag start", params.x, params.y, context);
|
|
540
|
+
validatePointerCoordinates("drag end", params.to_x, params.to_y, context);
|
|
541
|
+
if (!controller.drag) missingNativeMethod("drag", "drag");
|
|
542
|
+
return controller.drag(
|
|
543
|
+
expectedEpoch,
|
|
544
|
+
params.x,
|
|
545
|
+
params.y,
|
|
546
|
+
params.to_x,
|
|
547
|
+
params.to_y,
|
|
548
|
+
params.button ?? "left",
|
|
549
|
+
);
|
|
550
|
+
case "scroll":
|
|
551
|
+
validatePointerCoordinates("scroll", params.x, params.y, context);
|
|
552
|
+
if (!controller.scroll) missingNativeMethod("scroll", "scroll");
|
|
553
|
+
return controller.scroll(expectedEpoch, params.x, params.y, params.scroll_x, params.scroll_y);
|
|
554
|
+
case "type":
|
|
555
|
+
if (!controller.type) missingNativeMethod("type", "type");
|
|
556
|
+
return controller.type(undefined, params.text);
|
|
557
|
+
case "keypress":
|
|
558
|
+
if (!controller.keypress) missingNativeMethod("keypress", "keypress");
|
|
559
|
+
return controller.keypress(undefined, params.keys);
|
|
560
|
+
case "wait":
|
|
561
|
+
if (!controller.wait) missingNativeMethod("wait", "wait");
|
|
562
|
+
return controller.wait(undefined, capWaitMs(params.ms, remainingComputerTimeoutMs(deadline)));
|
|
563
|
+
}
|
|
564
|
+
},
|
|
565
|
+
deadline,
|
|
566
|
+
signal,
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
|
|
394
570
|
interface BatchDispatchResult {
|
|
395
571
|
steps: ComputerToolDetails[];
|
|
396
572
|
screenshot?: ComputerScreenshotDetails;
|
|
@@ -403,21 +579,34 @@ async function dispatchBatchComputerActions(
|
|
|
403
579
|
actions: readonly SingleComputerParams[],
|
|
404
580
|
timeoutMs: number | undefined,
|
|
405
581
|
hotkey?: string,
|
|
582
|
+
initialContext?: ScreenshotContext,
|
|
583
|
+
includeBatchScreenshot = false,
|
|
584
|
+
autoScreenshot = false,
|
|
585
|
+
signal?: AbortSignal,
|
|
586
|
+
deadline?: ComputerDeadline,
|
|
406
587
|
): Promise<BatchDispatchResult> {
|
|
407
588
|
const steps: ComputerToolDetails[] = [];
|
|
408
589
|
let lastScreenshot: ComputerScreenshotDetails | undefined;
|
|
409
590
|
let lastScreenshotSource: unknown;
|
|
410
|
-
let
|
|
591
|
+
let context = initialContext;
|
|
411
592
|
for (const single of actions) {
|
|
412
593
|
const stepDetails = detailsFromParams(single);
|
|
413
594
|
try {
|
|
414
|
-
|
|
595
|
+
throwIfAborted(signal);
|
|
596
|
+
assertComputerDeadline(deadline);
|
|
597
|
+
const stepTimeoutMs = stepTimeoutFromParams(single, timeoutMs);
|
|
598
|
+
const stepDeadline = createComputerDeadline(stepTimeoutMs, deadline);
|
|
599
|
+
let result = await dispatchComputerAction(controller, single, stepDeadline, context, signal);
|
|
600
|
+
if (single.action !== "screenshot" && (single.include_screenshot === true || autoScreenshot)) {
|
|
601
|
+
result = await captureScreenshot(controller, stepDeadline, signal);
|
|
602
|
+
}
|
|
603
|
+
|
|
415
604
|
const screenshot = normalizeScreenshot(result);
|
|
416
605
|
if (screenshot) {
|
|
417
606
|
stepDetails.screenshot = screenshot;
|
|
418
607
|
lastScreenshot = screenshot;
|
|
419
608
|
lastScreenshotSource = result;
|
|
420
|
-
|
|
609
|
+
context = screenshot;
|
|
421
610
|
}
|
|
422
611
|
stepDetails.status = "success";
|
|
423
612
|
stepDetails.message = describeComputerSuccess(stepDetails);
|
|
@@ -437,9 +626,19 @@ async function dispatchBatchComputerActions(
|
|
|
437
626
|
}
|
|
438
627
|
steps.push(stepDetails);
|
|
439
628
|
}
|
|
629
|
+
if (includeBatchScreenshot) {
|
|
630
|
+
lastScreenshotSource = await captureScreenshot(controller, deadline, signal);
|
|
631
|
+
lastScreenshot = normalizeScreenshot(lastScreenshotSource) ?? lastScreenshot;
|
|
632
|
+
}
|
|
440
633
|
return { steps, screenshot: lastScreenshot, screenshotSource: lastScreenshotSource };
|
|
441
634
|
}
|
|
442
635
|
|
|
636
|
+
function stepTimeoutFromParams(params: SingleComputerParams, batchTimeoutMs: number | undefined): number | undefined {
|
|
637
|
+
if (params.timeout === undefined) return batchTimeoutMs;
|
|
638
|
+
const timeoutSeconds = clampTimeout("computer", params.timeout);
|
|
639
|
+
return timeoutSeconds > 0 ? timeoutSeconds * 1000 : undefined;
|
|
640
|
+
}
|
|
641
|
+
|
|
443
642
|
function detailsFromParams(params: ComputerParams): ComputerToolDetails {
|
|
444
643
|
const details: ComputerToolDetails = { action: params.action, status: "success" };
|
|
445
644
|
if ("x" in params) details.x = params.x;
|
|
@@ -476,12 +675,16 @@ function normalizeScreenshot(value: unknown): ComputerScreenshotDetails | undefi
|
|
|
476
675
|
scaleY: shot.scaleY,
|
|
477
676
|
originX: shot.originX,
|
|
478
677
|
originY: shot.originY,
|
|
479
|
-
displayEpoch: shot.displayEpoch,
|
|
678
|
+
displayEpoch: normalizeDisplayEpoch(shot.displayEpoch),
|
|
480
679
|
captureId: shot.captureId,
|
|
481
680
|
pngBytes: getPngByteLength(shot.png),
|
|
482
681
|
};
|
|
483
682
|
}
|
|
484
683
|
|
|
684
|
+
function normalizeDisplayEpoch(value: unknown): number | undefined {
|
|
685
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
686
|
+
}
|
|
687
|
+
|
|
485
688
|
function fullResolutionImageContentFromNativeResult(value: unknown): ImageContent | undefined {
|
|
486
689
|
const candidate =
|
|
487
690
|
value && typeof value === "object" && "screenshot" in value
|
package/src/tools/index.ts
CHANGED
|
@@ -34,6 +34,7 @@ import { AstEditTool } from "./ast-edit";
|
|
|
34
34
|
import { AstGrepTool } from "./ast-grep";
|
|
35
35
|
import { BashTool } from "./bash";
|
|
36
36
|
import type { BashRestrictionProfile } from "./bash-allowed-prefixes";
|
|
37
|
+
import { BisectTool } from "./bisect";
|
|
37
38
|
import { BrowserTool } from "./browser";
|
|
38
39
|
import { CalculatorTool } from "./calculator";
|
|
39
40
|
import { type CheckpointState, CheckpointTool, RewindTool } from "./checkpoint";
|
|
@@ -72,6 +73,7 @@ export * from "./ask";
|
|
|
72
73
|
export * from "./ast-edit";
|
|
73
74
|
export * from "./ast-grep";
|
|
74
75
|
export * from "./bash";
|
|
76
|
+
export * from "./bisect";
|
|
75
77
|
export * from "./browser";
|
|
76
78
|
export * from "./calculator";
|
|
77
79
|
export * from "./checkpoint";
|
|
@@ -387,6 +389,7 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
|
|
|
387
389
|
render_mermaid: s => new RenderMermaidTool(s),
|
|
388
390
|
ask: AskTool.createIf,
|
|
389
391
|
debug: DebugTool.createIf,
|
|
392
|
+
bisect: s => new BisectTool(s),
|
|
390
393
|
eval: s => new EvalTool(s),
|
|
391
394
|
calc: s => new CalculatorTool(s),
|
|
392
395
|
ssh: loadSshTool,
|
package/src/tools/output-meta.ts
CHANGED
|
@@ -554,13 +554,17 @@ function getSpillConfig(s: Settings | undefined) {
|
|
|
554
554
|
| "tools.artifactSpillThreshold"
|
|
555
555
|
| "tools.artifactTailBytes"
|
|
556
556
|
| "tools.artifactTailLines"
|
|
557
|
-
| "tools.artifactHeadBytes"
|
|
557
|
+
| "tools.artifactHeadBytes"
|
|
558
|
+
| "tools.maxInlineResultBytes"
|
|
559
|
+
| "tools.readArtifactSpillThreshold";
|
|
558
560
|
const get = <P extends Path>(path: P) => s?.get(path) ?? getDefault(path);
|
|
559
561
|
return {
|
|
560
562
|
threshold: get("tools.artifactSpillThreshold") * 1024,
|
|
563
|
+
readThreshold: get("tools.readArtifactSpillThreshold") * 1024,
|
|
561
564
|
tailBytes: get("tools.artifactTailBytes") * 1024,
|
|
562
565
|
tailLines: get("tools.artifactTailLines"),
|
|
563
566
|
headBytes: get("tools.artifactHeadBytes") * 1024,
|
|
567
|
+
maxInlineBytes: get("tools.maxInlineResultBytes") * 1024,
|
|
564
568
|
};
|
|
565
569
|
}
|
|
566
570
|
|
|
@@ -596,8 +600,12 @@ async function spillLargeResultToArtifact(
|
|
|
596
600
|
): Promise<AgentToolResult> {
|
|
597
601
|
const sessionManager = context?.sessionManager;
|
|
598
602
|
if (!sessionManager) return result;
|
|
599
|
-
|
|
600
|
-
|
|
603
|
+
const { threshold, readThreshold, tailBytes, tailLines, headBytes } = getSpillConfig(context?.settings);
|
|
604
|
+
// `read` manages its own per-range truncation, but the combined multi-range
|
|
605
|
+
// output has no cap — enforce a read-specific (higher) combined threshold
|
|
606
|
+
// instead of exempting read entirely. 0 disables read spill (backstop only).
|
|
607
|
+
const effectiveThreshold = toolName === "read" ? readThreshold : threshold;
|
|
608
|
+
if (effectiveThreshold <= 0) return result;
|
|
601
609
|
|
|
602
610
|
// Skip if tool already saved an artifact
|
|
603
611
|
const existingMeta: OutputMeta | undefined = result.details?.meta;
|
|
@@ -614,7 +622,7 @@ async function spillLargeResultToArtifact(
|
|
|
614
622
|
|
|
615
623
|
const fullText = textParts.length === 1 ? textParts[0] : textParts.join("\n");
|
|
616
624
|
const totalBytes = Buffer.byteLength(fullText, "utf-8");
|
|
617
|
-
if (totalBytes <=
|
|
625
|
+
if (totalBytes <= effectiveThreshold) return result;
|
|
618
626
|
|
|
619
627
|
// Save full output as artifact
|
|
620
628
|
const artifactId = await sessionManager.saveArtifact(fullText, toolName);
|
|
@@ -691,6 +699,106 @@ async function spillLargeResultToArtifact(
|
|
|
691
699
|
return { ...result, content: newContent, details: newDetails };
|
|
692
700
|
}
|
|
693
701
|
|
|
702
|
+
/**
|
|
703
|
+
* Absolute inline-size backstop enforced after {@link spillLargeResultToArtifact}.
|
|
704
|
+
*
|
|
705
|
+
* The threshold-based spill above has escape hatches: it early-returns for the
|
|
706
|
+
* `read` tool and for results that already carry an `artifactId` (a tool may set
|
|
707
|
+
* partial truncation meta yet still emit oversized inline text). This backstop
|
|
708
|
+
* closes those gaps: when `tools.maxInlineResultBytes` is configured (> 0), any
|
|
709
|
+
* final result whose inline text exceeds the cap is force-saved to an artifact
|
|
710
|
+
* (reusing an existing artifactId to avoid double-artifacting) and truncated to a
|
|
711
|
+
* head+tail view that fits the cap. Disabled by default (opt-in pending
|
|
712
|
+
* measurement); a 0 cap returns the result untouched.
|
|
713
|
+
*/
|
|
714
|
+
async function enforceInlineResultBackstop(
|
|
715
|
+
result: AgentToolResult,
|
|
716
|
+
toolName: string,
|
|
717
|
+
context: AgentToolContext | undefined,
|
|
718
|
+
): Promise<AgentToolResult> {
|
|
719
|
+
const { maxInlineBytes, tailLines, headBytes } = getSpillConfig(context?.settings);
|
|
720
|
+
if (maxInlineBytes <= 0) return result;
|
|
721
|
+
|
|
722
|
+
const textParts: string[] = [];
|
|
723
|
+
for (const block of result.content) {
|
|
724
|
+
if (block.type === "text" && block.text) {
|
|
725
|
+
textParts.push(block.text);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
if (textParts.length === 0) return result;
|
|
729
|
+
|
|
730
|
+
const fullText = textParts.length === 1 ? textParts[0] : textParts.join("\n");
|
|
731
|
+
const totalBytes = Buffer.byteLength(fullText, "utf-8");
|
|
732
|
+
if (totalBytes <= maxInlineBytes) return result;
|
|
733
|
+
|
|
734
|
+
// Reuse an existing artifact (avoid double-artifacting); otherwise save the
|
|
735
|
+
// full output so the truncated view keeps a reference to the complete text.
|
|
736
|
+
const existingMeta: OutputMeta | undefined = result.details?.meta;
|
|
737
|
+
let artifactId = existingMeta?.truncation?.artifactId;
|
|
738
|
+
if (!artifactId) {
|
|
739
|
+
artifactId = (await context?.sessionManager?.saveArtifact(fullText, toolName)) ?? undefined;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// Budget head+tail below the cap, reserving room for the elision marker so the
|
|
743
|
+
// composed `<head>\n<marker>\n<tail>` view never exceeds the configured cap.
|
|
744
|
+
const MARKER_RESERVE = 256;
|
|
745
|
+
const budget = maxInlineBytes - MARKER_RESERVE;
|
|
746
|
+
const useMiddle = headBytes > 0 && budget > 0;
|
|
747
|
+
let truncated = useMiddle
|
|
748
|
+
? truncateMiddle(fullText, {
|
|
749
|
+
maxBytes: budget,
|
|
750
|
+
maxLines: tailLines * 2,
|
|
751
|
+
maxHeadBytes: Math.min(headBytes, Math.floor(budget / 2)),
|
|
752
|
+
maxHeadLines: tailLines,
|
|
753
|
+
})
|
|
754
|
+
: truncateTail(fullText, { maxBytes: maxInlineBytes, maxLines: tailLines });
|
|
755
|
+
|
|
756
|
+
// Defensive clamp: guarantee the contract even for pathological marker sizes.
|
|
757
|
+
if (Buffer.byteLength(truncated.content, "utf-8") > maxInlineBytes) {
|
|
758
|
+
truncated = truncateTail(fullText, { maxBytes: maxInlineBytes, maxLines: tailLines });
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
const newContent: (TextContent | ImageContent)[] = [];
|
|
762
|
+
for (const block of result.content) {
|
|
763
|
+
if (block.type !== "text") {
|
|
764
|
+
newContent.push(block);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
newContent.push({ type: "text", text: truncated.content });
|
|
768
|
+
|
|
769
|
+
const outputLines = truncated.outputLines ?? truncated.totalLines;
|
|
770
|
+
const outputBytes = truncated.outputBytes ?? truncated.totalBytes;
|
|
771
|
+
const truncationMeta: TruncationMeta =
|
|
772
|
+
truncated.truncatedBy === "middle"
|
|
773
|
+
? {
|
|
774
|
+
direction: "middle",
|
|
775
|
+
truncatedBy: "middle",
|
|
776
|
+
totalLines: truncated.totalLines,
|
|
777
|
+
totalBytes: truncated.totalBytes,
|
|
778
|
+
outputLines,
|
|
779
|
+
outputBytes,
|
|
780
|
+
maxBytes: maxInlineBytes,
|
|
781
|
+
elidedLines: truncated.elidedLines ?? Math.max(0, truncated.totalLines - outputLines),
|
|
782
|
+
elidedBytes: truncated.elidedBytes ?? Math.max(0, truncated.totalBytes - outputBytes),
|
|
783
|
+
artifactId,
|
|
784
|
+
}
|
|
785
|
+
: {
|
|
786
|
+
direction: "tail",
|
|
787
|
+
truncatedBy: truncated.truncatedBy ?? "bytes",
|
|
788
|
+
totalLines: truncated.totalLines,
|
|
789
|
+
totalBytes: truncated.totalBytes,
|
|
790
|
+
outputLines,
|
|
791
|
+
outputBytes,
|
|
792
|
+
maxBytes: maxInlineBytes,
|
|
793
|
+
shownRange: { start: truncated.totalLines - outputLines + 1, end: truncated.totalLines },
|
|
794
|
+
artifactId,
|
|
795
|
+
};
|
|
796
|
+
|
|
797
|
+
const newMeta: OutputMeta = { ...(existingMeta ?? {}), truncation: truncationMeta };
|
|
798
|
+
const newDetails = { ...(result.details ?? {}), meta: newMeta };
|
|
799
|
+
return { ...result, content: newContent, details: newDetails };
|
|
800
|
+
}
|
|
801
|
+
|
|
694
802
|
// =============================================================================
|
|
695
803
|
// Tool wrapper
|
|
696
804
|
// =============================================================================
|
|
@@ -711,6 +819,10 @@ async function wrappedExecute(
|
|
|
711
819
|
// Spill large results to artifact, truncate to tail
|
|
712
820
|
result = await spillLargeResultToArtifact(result, this.name, context);
|
|
713
821
|
|
|
822
|
+
// Absolute inline-size backstop: catches oversized text the threshold spill
|
|
823
|
+
// skipped (read exemption, tools with pre-existing partial artifact meta).
|
|
824
|
+
result = await enforceInlineResultBackstop(result, this.name, context);
|
|
825
|
+
|
|
714
826
|
// Append notices from meta
|
|
715
827
|
const meta: OutputMeta | undefined = result.details?.meta;
|
|
716
828
|
if (meta) {
|
package/src/tools/search.ts
CHANGED
|
@@ -44,7 +44,10 @@ const searchSchema = z
|
|
|
44
44
|
paths: z
|
|
45
45
|
.array(z.string().describe("file, directory, glob, or internal URL to search"))
|
|
46
46
|
.min(1)
|
|
47
|
-
.
|
|
47
|
+
.optional()
|
|
48
|
+
.describe(
|
|
49
|
+
"files, directories, globs, or internal URLs to search (defaults to the working directory when omitted)",
|
|
50
|
+
),
|
|
48
51
|
i: z.boolean().optional().describe("case-insensitive search"),
|
|
49
52
|
gitignore: z.boolean().optional().describe("respect gitignore"),
|
|
50
53
|
skip: z
|
|
@@ -236,7 +239,10 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
236
239
|
_onUpdate?: AgentToolUpdateCallback<SearchToolDetails>,
|
|
237
240
|
_toolContext?: AgentToolContext,
|
|
238
241
|
): Promise<AgentToolResult<SearchToolDetails>> {
|
|
239
|
-
const { pattern,
|
|
242
|
+
const { pattern, i, gitignore, skip } = params;
|
|
243
|
+
// Recurring real-world failure mode: models omit `paths` for repo-wide
|
|
244
|
+
// searches. Default to the working directory instead of failing the call.
|
|
245
|
+
const paths = params.paths ?? ["."];
|
|
240
246
|
|
|
241
247
|
return untilAborted(signal, async () => {
|
|
242
248
|
const normalizedPattern = pattern.trim();
|
package/src/tools/skill.ts
CHANGED
|
@@ -37,6 +37,8 @@ function normalizeSkillName(name: string | undefined): string {
|
|
|
37
37
|
return (name ?? "").trim();
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
const SKILL_NAME_GLOB_PATTERN = /[*?[\]{}]/;
|
|
41
|
+
|
|
40
42
|
type SkillToolInput = z.infer<typeof skillSchema>;
|
|
41
43
|
|
|
42
44
|
export interface SkillToolDetails {
|
|
@@ -87,6 +89,19 @@ export class SkillTool implements AgentTool<typeof skillSchema, SkillToolDetails
|
|
|
87
89
|
if (!requestedName) {
|
|
88
90
|
throw new ToolError("skill tool: `name` is required");
|
|
89
91
|
}
|
|
92
|
+
// Fail fast on glob/wildcard names (e.g. `*`). A model that sees the
|
|
93
|
+
// `--skills '*'` launch filter may echo the glob into the skill tool.
|
|
94
|
+
// Without this guard `*` slips through to the generic unknown-skill
|
|
95
|
+
// path only because no skill is literally named `*`, so a future
|
|
96
|
+
// odd skill name could get dispatched — and the actionable signal
|
|
97
|
+
// that names are concrete (not globs) is lost. Reject before any
|
|
98
|
+
// dispatch or handoff state mutation so the call ends immediately
|
|
99
|
+
// instead of spawning skill work that burns the whole turn budget.
|
|
100
|
+
if (SKILL_NAME_GLOB_PATTERN.test(requestedName)) {
|
|
101
|
+
throw new ToolError(
|
|
102
|
+
`skill tool: "${requestedName}" is not a valid skill name. The name must be a single concrete skill (e.g. "ralplan"), not a glob or wildcard pattern. Pass one exact skill name as shown in /skill:<name>.`,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
90
105
|
const activeState = this.#session.getActiveSkillState?.();
|
|
91
106
|
const activeSkill = normalizeSkillName(activeState?.skill);
|
|
92
107
|
if (activeSkill && requestedName === activeSkill) {
|
|
@@ -6,6 +6,9 @@ import { getTelegramFileSink } from "../notifications/attachment-registry";
|
|
|
6
6
|
import { getNotificationConfig, isGloballyConfigured } from "../notifications/config";
|
|
7
7
|
import type { ToolSession } from "./index";
|
|
8
8
|
|
|
9
|
+
const TELEGRAM_SEND_MAX_FILE_BYTES = 50 * 1024 * 1024;
|
|
10
|
+
const TELEGRAM_SEND_MAX_FILE_MIB = TELEGRAM_SEND_MAX_FILE_BYTES / (1024 * 1024);
|
|
11
|
+
|
|
9
12
|
const telegramSendSchema = z.object({
|
|
10
13
|
path: z
|
|
11
14
|
.string()
|
|
@@ -75,6 +78,9 @@ export class TelegramSendTool implements AgentTool<typeof telegramSendSchema, Te
|
|
|
75
78
|
if (!stat.isFile()) {
|
|
76
79
|
return { ok: false, error: "not a regular file" };
|
|
77
80
|
}
|
|
81
|
+
if (stat.size > TELEGRAM_SEND_MAX_FILE_BYTES) {
|
|
82
|
+
return { ok: false, error: `file exceeds Telegram document limit (${TELEGRAM_SEND_MAX_FILE_MIB} MiB)` };
|
|
83
|
+
}
|
|
78
84
|
return { ok: true, path: real };
|
|
79
85
|
}
|
|
80
86
|
|