@gajae-code/coding-agent 0.16.7 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +180 -1
- package/dist/types/capability/fs.d.ts +37 -4
- package/dist/types/capability/index.d.ts +19 -0
- package/dist/types/capability/types.d.ts +9 -0
- package/dist/types/cli/doctor/args.d.ts +36 -0
- package/dist/types/cli/doctor/checks.d.ts +18 -0
- package/dist/types/cli/doctor/config-repairs.d.ts +38 -0
- package/dist/types/cli/doctor/context.d.ts +89 -0
- package/dist/types/cli/doctor/files.d.ts +27 -0
- package/dist/types/cli/doctor/ids.d.ts +20 -0
- package/dist/types/cli/doctor/install-repairs.d.ts +94 -0
- package/dist/types/cli/doctor/journal.d.ts +43 -0
- package/dist/types/cli/doctor/managed-link.d.ts +39 -0
- package/dist/types/cli/doctor/native-probe.d.ts +2 -0
- package/dist/types/cli/doctor/permission-repairs.d.ts +25 -0
- package/dist/types/cli/doctor/plugin-quarantine.d.ts +77 -0
- package/dist/types/cli/doctor/plugin-restore.d.ts +40 -0
- package/dist/types/cli/doctor/plugin-targets.d.ts +11 -0
- package/dist/types/cli/doctor/probe-client.d.ts +9 -0
- package/dist/types/cli/doctor/probe-types.d.ts +18 -0
- package/dist/types/cli/doctor/probe.d.ts +3 -0
- package/dist/types/cli/doctor/projection-probe.d.ts +2 -0
- package/dist/types/cli/doctor/repairs.d.ts +28 -0
- package/dist/types/cli/doctor/report.d.ts +27 -0
- package/dist/types/cli/doctor/runner.d.ts +4 -0
- package/dist/types/cli/doctor/safety.d.ts +10 -0
- package/dist/types/cli/doctor/service-repairs.d.ts +8 -0
- package/dist/types/cli/doctor/service-targets.d.ts +41 -0
- package/dist/types/cli/doctor/types.d.ts +145 -0
- package/dist/types/cli/doctor-cli.d.ts +7 -0
- package/dist/types/cli/doctor-supervisor.d.ts +121 -0
- package/dist/types/cli/doctor-worker.d.ts +29 -0
- package/dist/types/cli/install-activation.d.ts +158 -0
- package/dist/types/cli/macos-community-app.d.ts +41 -0
- package/dist/types/cli/mcp-cli.d.ts +0 -11
- package/dist/types/cli/update-cli.d.ts +41 -4
- package/dist/types/cli-main.d.ts +174 -0
- package/dist/types/cli.d.ts +1 -171
- package/dist/types/commands/doctor.d.ts +7 -0
- package/dist/types/commands/sdk.d.ts +17 -8
- package/dist/types/config/autorouting-tier-map.d.ts +19 -0
- package/dist/types/config/file-lock.d.ts +27 -3
- package/dist/types/config/model-preset-registry.d.ts +1 -0
- package/dist/types/config/model-registry.d.ts +12 -5
- package/dist/types/config/model-resolver.d.ts +16 -3
- package/dist/types/config/models-config-schema.d.ts +2 -0
- package/dist/types/config/settings-schema.d.ts +1 -1
- package/dist/types/coordinator-mcp/question-gate-codec.d.ts +1 -1
- package/dist/types/coordinator-mcp/session-reaper.d.ts +15 -5
- package/dist/types/cursor.d.ts +9 -9
- package/dist/types/discovery/agents-md.d.ts +6 -1
- package/dist/types/discovery/helpers.d.ts +36 -7
- package/dist/types/discovery/index.d.ts +1 -1
- package/dist/types/discovery/mcp-json.d.ts +2 -0
- package/dist/types/extensibility/extensions/loader.d.ts +22 -1
- package/dist/types/extensibility/gjc-plugins/lifecycle.d.ts +14 -1
- package/dist/types/extensibility/gjc-plugins/paths.d.ts +2 -1
- package/dist/types/extensibility/gjc-plugins/types.d.ts +25 -0
- package/dist/types/extensibility/plugins/manager.d.ts +33 -4
- package/dist/types/extensibility/plugins/marketplace/cache.d.ts +6 -0
- package/dist/types/extensibility/plugins/marketplace/manager.d.ts +186 -0
- package/dist/types/extensibility/plugins/marketplace/registry.d.ts +27 -0
- package/dist/types/extensibility/plugins/marketplace/source-resolver.d.ts +16 -1
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +16 -3
- package/dist/types/gjc-runtime/autoresearch-runtime.d.ts +6 -0
- package/dist/types/gjc-runtime/launch-worktree.d.ts +53 -0
- package/dist/types/gjc-runtime/session-layout.d.ts +8 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +15 -0
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +13 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +6 -0
- package/dist/types/main.d.ts +19 -1
- package/dist/types/modes/acp/acp-agent.d.ts +40 -0
- package/dist/types/modes/components/agent-dashboard.d.ts +1 -0
- package/dist/types/modes/components/model-selector.d.ts +2 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -1
- package/dist/types/runtime-mcp/config-writer.d.ts +47 -0
- package/dist/types/runtime-mcp/policy-json-edit.d.ts +60 -0
- package/dist/types/runtime-mcp/startup-policy.d.ts +4 -0
- package/dist/types/runtime-mcp/types.d.ts +2 -2
- package/dist/types/sdk/acp/adapter.d.ts +5 -1
- package/dist/types/sdk/broker/broker.d.ts +51 -0
- package/dist/types/sdk/broker/daemon-entry.d.ts +39 -0
- package/dist/types/sdk/broker/discovery.d.ts +38 -0
- package/dist/types/sdk/broker/doctor-restart.d.ts +79 -0
- package/dist/types/sdk/broker/endpoint-authority.d.ts +7 -0
- package/dist/types/sdk/broker/ensure.d.ts +14 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +2 -0
- package/dist/types/sdk/broker/process-incarnation.d.ts +50 -0
- package/dist/types/sdk/broker/runtime.d.ts +26 -0
- package/dist/types/sdk/broker/session-index.d.ts +13 -4
- package/dist/types/sdk/broker/worktree-occupancy.d.ts +14 -0
- package/dist/types/sdk/bus/chat-daemon-cli.d.ts +16 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +101 -6
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +16 -0
- package/dist/types/sdk/bus/daemon-startup-exclusion.d.ts +43 -0
- package/dist/types/sdk/bus/doctor-daemon-restart-client.d.ts +51 -0
- package/dist/types/sdk/bus/doctor-daemon-restart.d.ts +39 -0
- package/dist/types/sdk/bus/index.d.ts +2 -0
- package/dist/types/sdk/bus/notification-service.d.ts +3 -0
- package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +11 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
- package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -0
- package/dist/types/sdk/bus/telegram-daemon-owner-registry.d.ts +1 -0
- package/dist/types/sdk/bus/telegram-daemon.d.ts +36 -5
- package/dist/types/sdk/cli/session-cli.d.ts +1 -0
- package/dist/types/sdk/host/session-runtime.d.ts +4 -0
- package/dist/types/sdk/host/surface-policy.d.ts +3 -1
- package/dist/types/sdk/lifecycle/service.d.ts +44 -10
- package/dist/types/sdk/prompt-deadline-lease.d.ts +6 -3
- package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -2
- package/dist/types/sdk/prompt-failure.d.ts +47 -0
- package/dist/types/sdk/prompt-status.d.ts +19 -0
- package/dist/types/sdk/router/session-router.d.ts +3 -4
- package/dist/types/sdk/service-artifact-paths.d.ts +16 -0
- package/dist/types/sdk/session-authority.d.ts +9 -0
- package/dist/types/sdk/session.d.ts +3 -1
- package/dist/types/session/agent-session.d.ts +1 -1
- package/dist/types/session/blob-store.d.ts +1 -1
- package/dist/types/session/fallback-chain-controller.d.ts +3 -6
- package/dist/types/session/internal/managed-session-scope.d.ts +44 -0
- package/dist/types/session/primary-control-surface.d.ts +4 -0
- package/dist/types/session/session-manager.d.ts +6 -0
- package/dist/types/system-prompt.d.ts +6 -0
- package/dist/types/tools/ask.d.ts +25 -0
- package/dist/types/tools/image-gen.d.ts +0 -4
- package/dist/types/tools/skill-discovery.d.ts +7 -5
- package/dist/types/tools/ultragoal-ask-guard.d.ts +7 -4
- package/dist/types/utils/changelog.d.ts +0 -9
- package/dist/types/utils/mupdf-wasm.d.ts +1 -0
- package/package.json +9 -8
- package/scripts/compile-args.ts +8 -4
- package/src/capability/fs.ts +395 -17
- package/src/capability/index.ts +273 -10
- package/src/capability/types.ts +9 -0
- package/src/cli/customize-doctor.ts +18 -12
- package/src/cli/doctor/args.ts +236 -0
- package/src/cli/doctor/checks.ts +431 -0
- package/src/cli/doctor/config-repairs.ts +517 -0
- package/src/cli/doctor/context.ts +149 -0
- package/src/cli/doctor/files.ts +149 -0
- package/src/cli/doctor/ids.ts +115 -0
- package/src/cli/doctor/install-repairs.ts +335 -0
- package/src/cli/doctor/journal.ts +423 -0
- package/src/cli/doctor/managed-link.ts +514 -0
- package/src/cli/doctor/native-probe.ts +13 -0
- package/src/cli/doctor/permission-repairs.ts +242 -0
- package/src/cli/doctor/plugin-quarantine.ts +377 -0
- package/src/cli/doctor/plugin-restore.ts +59 -0
- package/src/cli/doctor/plugin-targets.ts +373 -0
- package/src/cli/doctor/probe-client.ts +195 -0
- package/src/cli/doctor/probe-types.ts +21 -0
- package/src/cli/doctor/probe.ts +42 -0
- package/src/cli/doctor/projection-probe.ts +249 -0
- package/src/cli/doctor/repairs.ts +1157 -0
- package/src/cli/doctor/report.ts +176 -0
- package/src/cli/doctor/runner.ts +342 -0
- package/src/cli/doctor/safety.ts +40 -0
- package/src/cli/doctor/service-repairs.ts +452 -0
- package/src/cli/doctor/service-targets.ts +274 -0
- package/src/cli/doctor/types.ts +164 -0
- package/src/cli/doctor-cli.ts +62 -0
- package/src/cli/doctor-supervisor.ts +571 -0
- package/src/cli/doctor-worker.ts +181 -0
- package/src/cli/install-activation.ts +666 -0
- package/src/cli/macos-community-app.ts +1449 -0
- package/src/cli/mcp-cli.ts +11 -21
- package/src/cli/nofile-limit.ts +5 -1
- package/src/cli/plugin-cli.ts +6 -1
- package/src/cli/update-cli.ts +362 -264
- package/src/cli-main.ts +517 -0
- package/src/cli.ts +64 -433
- package/src/commands/autoresearch.ts +11 -5
- package/src/commands/completion.ts +1 -1
- package/src/commands/doctor.ts +13 -0
- package/src/commands/sdk.ts +58 -10
- package/src/commands/ultragoal.ts +11 -5
- package/src/config/autorouting-tier-map.ts +7 -0
- package/src/config/file-lock.ts +736 -71
- package/src/config/mcp-schema.json +3 -3
- package/src/config/model-preset-registry.ts +1 -0
- package/src/config/model-registry.ts +80 -27
- package/src/config/model-resolver.ts +33 -4
- package/src/config/models-config-schema.ts +2 -0
- package/src/config/settings-schema.ts +6 -2
- package/src/coordinator-mcp/question-gate-codec.ts +1 -0
- package/src/coordinator-mcp/server.ts +534 -51
- package/src/coordinator-mcp/session-reaper.ts +64 -8
- package/src/cursor.ts +223 -53
- package/src/discovery/agents-md.ts +74 -8
- package/src/discovery/agents.ts +32 -7
- package/src/discovery/builtin.ts +184 -75
- package/src/discovery/claude-plugins.ts +85 -17
- package/src/discovery/cline.ts +38 -10
- package/src/discovery/cursor.ts +19 -6
- package/src/discovery/gemini.ts +62 -22
- package/src/discovery/github.ts +13 -3
- package/src/discovery/helpers.ts +483 -89
- package/src/discovery/index.ts +1 -0
- package/src/discovery/mcp-json.ts +36 -10
- package/src/discovery/opencode.ts +36 -19
- package/src/discovery/ssh.ts +11 -8
- package/src/discovery/windsurf.ts +12 -3
- package/src/exec/bash-executor.ts +10 -3
- package/src/exec/isolated-shell.ts +15 -2
- package/src/extensibility/extensions/loader.ts +88 -26
- package/src/extensibility/gjc-plugins/lifecycle.ts +189 -0
- package/src/extensibility/gjc-plugins/paths.ts +4 -1
- package/src/extensibility/gjc-plugins/types.ts +27 -0
- package/src/extensibility/plugins/manager.ts +200 -22
- package/src/extensibility/plugins/marketplace/cache.ts +55 -2
- package/src/extensibility/plugins/marketplace/manager.ts +1262 -162
- package/src/extensibility/plugins/marketplace/registry.ts +153 -0
- package/src/extensibility/plugins/marketplace/source-resolver.ts +116 -10
- package/src/extensibility/runtime-skill-discovery.ts +61 -9
- package/src/extensibility/skill-management.ts +7 -1
- package/src/gjc-runtime/autoresearch-runtime.ts +10 -1
- package/src/gjc-runtime/launch-worktree.test.ts +248 -0
- package/src/gjc-runtime/launch-worktree.ts +275 -38
- package/src/gjc-runtime/repository-binding.ts +26 -1
- package/src/gjc-runtime/session-layout.ts +10 -0
- package/src/gjc-runtime/session-state-lock.ts +330 -23
- package/src/gjc-runtime/session-state-sidecar.ts +328 -80
- package/src/gjc-runtime/ultragoal-runtime.ts +10 -1
- package/src/harness-control-plane/session-lease.ts +2 -2
- package/src/internal-urls/docs-index.generated.ts +28 -26
- package/src/main.ts +150 -29
- package/src/memories/index.ts +24 -2
- package/src/modes/acp/acp-agent.ts +522 -68
- package/src/modes/components/agent-dashboard.ts +3 -1
- package/src/modes/components/model-selector.ts +28 -19
- package/src/modes/components/plugin-settings.ts +2 -1
- package/src/modes/controllers/selector-controller.ts +15 -9
- package/src/modes/interactive-mode.ts +21 -3
- package/src/prompts/system/system-prompt.md +5 -1
- package/src/prompts/tools/browser.md +1 -1
- package/src/prompts/tools/move-session.md +4 -0
- package/src/prompts/tools/read.md +2 -1
- package/src/prompts/tools/skill-discovery.md +6 -5
- package/src/prompts/tools/task.md +1 -0
- package/src/runtime-mcp/config-writer.ts +228 -0
- package/src/runtime-mcp/oauth-discovery.ts +114 -14
- package/src/runtime-mcp/policy-json-edit.ts +129 -0
- package/src/runtime-mcp/startup-policy.ts +14 -0
- package/src/runtime-mcp/types.ts +2 -2
- package/src/sdk/acp/adapter.ts +27 -12
- package/src/sdk/broker/broker.ts +273 -44
- package/src/sdk/broker/daemon-entry.ts +119 -0
- package/src/sdk/broker/discovery.ts +173 -20
- package/src/sdk/broker/doctor-restart.ts +206 -0
- package/src/sdk/broker/endpoint-authority.ts +36 -0
- package/src/sdk/broker/ensure.ts +49 -14
- package/src/sdk/broker/lifecycle.ts +174 -42
- package/src/sdk/broker/process-incarnation.ts +86 -2
- package/src/sdk/broker/runtime.ts +145 -21
- package/src/sdk/broker/session-index.ts +136 -22
- package/src/sdk/broker/spawn-substrate.ts +4 -1
- package/src/sdk/broker/transport.ts +5 -0
- package/src/sdk/broker/worktree-occupancy.ts +21 -1
- package/src/sdk/bus/chat-daemon-cli.ts +63 -0
- package/src/sdk/bus/chat-daemon-control.ts +260 -64
- package/src/sdk/bus/chat-daemon-runtime.ts +141 -25
- package/src/sdk/bus/daemon-startup-exclusion.ts +119 -0
- package/src/sdk/bus/doctor-daemon-restart-client.ts +291 -0
- package/src/sdk/bus/doctor-daemon-restart.ts +92 -0
- package/src/sdk/bus/index.ts +252 -51
- package/src/sdk/bus/kind-aware-reconciliation.ts +76 -41
- package/src/sdk/bus/notification-service.ts +21 -3
- package/src/sdk/bus/reconciliation-store.ts +17 -11
- package/src/sdk/bus/telegram-daemon-cli.ts +91 -5
- package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
- package/src/sdk/bus/telegram-daemon-control.ts +47 -0
- package/src/sdk/bus/telegram-daemon-owner-registry.ts +1 -1
- package/src/sdk/bus/telegram-daemon.ts +178 -13
- package/src/sdk/cli/session-cli.ts +100 -12
- package/src/sdk/host/host.ts +2 -7
- package/src/sdk/host/session-runtime.test.ts +329 -8
- package/src/sdk/host/session-runtime.ts +202 -50
- package/src/sdk/host/surface-policy.ts +8 -1
- package/src/sdk/lifecycle/service.ts +199 -22
- package/src/sdk/mcp/server.ts +1 -1
- package/src/sdk/prompt-deadline-lease.ts +12 -4
- package/src/sdk/prompt-deadline-manager.ts +5 -7
- package/src/sdk/prompt-failure.ts +148 -0
- package/src/sdk/prompt-status.ts +30 -1
- package/src/sdk/prompt-watchdog.ts +11 -3
- package/src/sdk/protocol/operation-inventory.generated.json +18 -0
- package/src/sdk/protocol/operation-registry.ts +20 -8
- package/src/sdk/router/session-router.ts +8 -3
- package/src/sdk/service-artifact-paths.ts +26 -0
- package/src/sdk/session-authority.ts +10 -0
- package/src/sdk/session.ts +361 -194
- package/src/session/agent-session.ts +398 -93
- package/src/session/blob-store.ts +9 -9
- package/src/session/fallback-chain-controller.ts +3 -7
- package/src/session/internal/managed-session-scope.ts +445 -29
- package/src/session/internal/managed-session-storage.ts +38 -6
- package/src/session/primary-control-surface.ts +12 -0
- package/src/session/session-manager.ts +11 -3
- package/src/session/session-storage.ts +1 -1
- package/src/session-import/codex.ts +6 -1
- package/src/session-import/redact.ts +7 -0
- package/src/skill-state/workflow-mutation-guard.ts +645 -39
- package/src/slash-commands/builtin-registry.ts +7 -3
- package/src/system-prompt.ts +8 -0
- package/src/task/executor.ts +36 -3
- package/src/task/types.ts +27 -5
- package/src/tools/ask.ts +114 -34
- package/src/tools/browser/actions.ts +2 -2
- package/src/tools/browser/tab-worker.ts +11 -8
- package/src/tools/descriptors.ts +6 -1
- package/src/tools/fetch.ts +7 -2
- package/src/tools/image-gen.ts +33 -34
- package/src/tools/read.ts +78 -32
- package/src/tools/skill-discovery.ts +17 -7
- package/src/tools/tool-catalog.generated.ts +8 -8
- package/src/tools/ultragoal-ask-guard.ts +16 -26
- package/src/utils/changelog.ts +7 -1
- package/src/utils/git.ts +3 -1
- package/src/utils/markit.ts +24 -2
- package/src/utils/mupdf-wasm.ts +26 -0
- package/src/web/search/providers/openai-compatible.ts +191 -14
|
@@ -436,7 +436,11 @@ async function resolveModelCommandSelection(
|
|
|
436
436
|
const providerRef = parseProviderQualifiedSelector(selector);
|
|
437
437
|
const discoverableProviders = runtime.session.modelRegistry?.getDiscoverableProviders?.() ?? [];
|
|
438
438
|
if (providerRef && discoverableProviders.includes(providerRef.provider)) {
|
|
439
|
-
await runtime.session.modelRegistry.refreshProvider?.(
|
|
439
|
+
await runtime.session.modelRegistry.refreshProvider?.(
|
|
440
|
+
providerRef.provider,
|
|
441
|
+
"online",
|
|
442
|
+
runtime.session.credentialSessionId,
|
|
443
|
+
);
|
|
440
444
|
availableModels = runtime.session.getAvailableModels?.() ?? [];
|
|
441
445
|
const refreshedSelection = resolveModelCommandSelectionFromAvailable(
|
|
442
446
|
runtime,
|
|
@@ -1699,7 +1703,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1699
1703
|
models: parsed.models,
|
|
1700
1704
|
force: parsed.force,
|
|
1701
1705
|
});
|
|
1702
|
-
await runtime.session.modelRegistry.refresh("offline");
|
|
1706
|
+
await runtime.session.modelRegistry.refresh("offline", runtime.session.credentialSessionId);
|
|
1703
1707
|
await runtime.output(formatProviderSetupResult(result));
|
|
1704
1708
|
await runtime.notifyConfigChanged?.();
|
|
1705
1709
|
return commandConsumed();
|
|
@@ -1740,7 +1744,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1740
1744
|
models: parsed.models,
|
|
1741
1745
|
force: parsed.force,
|
|
1742
1746
|
});
|
|
1743
|
-
await runtime.ctx.session.modelRegistry.refresh("offline");
|
|
1747
|
+
await runtime.ctx.session.modelRegistry.refresh("offline", runtime.ctx.session.credentialSessionId);
|
|
1744
1748
|
runtime.ctx.showStatus(formatProviderSetupResult(result));
|
|
1745
1749
|
} catch (err) {
|
|
1746
1750
|
runtime.ctx.showError(`Provider setup failed: ${errorMessage(err)}`);
|
package/src/system-prompt.ts
CHANGED
|
@@ -406,6 +406,12 @@ export interface BuildSystemPromptOptions {
|
|
|
406
406
|
alwaysApplyRules?: AlwaysApplyRule[];
|
|
407
407
|
/** Whether secret obfuscation is active. When true, explains the redaction format in the prompt. */
|
|
408
408
|
secretsEnabled?: boolean;
|
|
409
|
+
/**
|
|
410
|
+
* Whether the `task` tool advertises the `isolated` parameter in this session
|
|
411
|
+
* (`task.isolation.mode !== "none"`). Worktree routing guidance depends on it:
|
|
412
|
+
* without isolation the agent must use a plain `git worktree`, never refuse.
|
|
413
|
+
*/
|
|
414
|
+
taskIsolationEnabled?: boolean;
|
|
409
415
|
/** Pre-loaded workspace tree (skips discovery if provided). May be a Promise to allow early kick-off. */
|
|
410
416
|
workspaceTree?: WorkspaceTree | Promise<WorkspaceTree>;
|
|
411
417
|
/**
|
|
@@ -570,6 +576,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
570
576
|
toolDiscoveryActive = false,
|
|
571
577
|
eagerTasks = false,
|
|
572
578
|
secretsEnabled = false,
|
|
579
|
+
taskIsolationEnabled = false,
|
|
573
580
|
workspaceTree: providedWorkspaceTree,
|
|
574
581
|
subagent = false,
|
|
575
582
|
} = options;
|
|
@@ -749,6 +756,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
749
756
|
toolDiscoveryActive: toolDiscoveryActive && hasHiddenToolDiscoveryTool,
|
|
750
757
|
eagerTasks,
|
|
751
758
|
secretsEnabled,
|
|
759
|
+
taskIsolationEnabled,
|
|
752
760
|
subagent,
|
|
753
761
|
};
|
|
754
762
|
const rendered = prompt.render(resolvedCustomPrompt ? customSystemPromptTemplate : systemPromptTemplate, data);
|
package/src/task/executor.ts
CHANGED
|
@@ -34,7 +34,11 @@ import { logger, prompt, untilAborted } from "@gajae-code/utils";
|
|
|
34
34
|
import { AsyncJobManager } from "../async";
|
|
35
35
|
import { AUTOROUTING_SELECTOR_MAX_LENGTH, type AutoroutingReasonCode } from "../config/autorouting-contract";
|
|
36
36
|
import { ModelRegistry } from "../config/model-registry";
|
|
37
|
-
import {
|
|
37
|
+
import {
|
|
38
|
+
formatModelString,
|
|
39
|
+
isExplicitProviderModelOverride,
|
|
40
|
+
resolveModelOverrideWithAuthFallback,
|
|
41
|
+
} from "../config/model-resolver";
|
|
38
42
|
import type { PromptTemplate } from "../config/prompt-templates";
|
|
39
43
|
import { Settings } from "../config/settings";
|
|
40
44
|
import { SETTINGS_SCHEMA, type SettingPath } from "../config/settings-schema";
|
|
@@ -921,7 +925,13 @@ function formatExecutionError(error: unknown): string {
|
|
|
921
925
|
const causes = error.errors.map(cause => (cause instanceof Error ? cause.message : String(cause))).join("; ");
|
|
922
926
|
return causes ? `${error.message}: ${causes}` : error.message;
|
|
923
927
|
}
|
|
924
|
-
|
|
928
|
+
if (!(error instanceof Error)) return String(error);
|
|
929
|
+
const message = error.message;
|
|
930
|
+
const stack = error.stack;
|
|
931
|
+
if (!stack) return message;
|
|
932
|
+
const stackHeadline = stack.split(/\r?\n/u, 1)[0] ?? "";
|
|
933
|
+
if (!message || stackHeadline.includes(message)) return stack;
|
|
934
|
+
return `${error.name || "Error"}: ${message}\n${stack}`;
|
|
925
935
|
}
|
|
926
936
|
|
|
927
937
|
function transportFactsFromError(error: unknown): TransportFailureFacts | undefined {
|
|
@@ -1730,7 +1740,12 @@ export async function runSubprocessOnce(options: ExecutorOptions): Promise<Singl
|
|
|
1730
1740
|
}
|
|
1731
1741
|
checkAbort();
|
|
1732
1742
|
if (!registryFromParent) {
|
|
1733
|
-
await awaitAbortable(
|
|
1743
|
+
await awaitAbortable(
|
|
1744
|
+
modelRegistry.refresh(
|
|
1745
|
+
"online-if-uncached",
|
|
1746
|
+
options.parentCredentialSessionId ?? options.parentSessionId,
|
|
1747
|
+
),
|
|
1748
|
+
);
|
|
1734
1749
|
} else {
|
|
1735
1750
|
logger.debug("runSubagent: reusing parent modelRegistry; skipping refresh");
|
|
1736
1751
|
}
|
|
@@ -1763,6 +1778,24 @@ export async function runSubprocessOnce(options: ExecutorOptions): Promise<Singl
|
|
|
1763
1778
|
canonicalChildScope,
|
|
1764
1779
|
),
|
|
1765
1780
|
);
|
|
1781
|
+
if (!model && isExplicitProviderModelOverride(modelPatterns)) {
|
|
1782
|
+
const skipReasons = [...new Set(skips.map(skip => skip.reason))];
|
|
1783
|
+
if ((options.preflightProbe || options.preflightDurable) && skipReasons.includes("unauthenticated")) {
|
|
1784
|
+
preflightOperation = "auth_resolve";
|
|
1785
|
+
throw Object.assign(new Error("autorouting credential unavailable"), {
|
|
1786
|
+
transient: false,
|
|
1787
|
+
credentialMissing: true,
|
|
1788
|
+
});
|
|
1789
|
+
}
|
|
1790
|
+
const reason = skipReasons.includes("unauthenticated")
|
|
1791
|
+
? "authentication failed"
|
|
1792
|
+
: skipReasons.includes("unknown_model")
|
|
1793
|
+
? "is missing from the catalog"
|
|
1794
|
+
: "could not be resolved";
|
|
1795
|
+
throw new Error(
|
|
1796
|
+
`Requested model ${modelPatterns.join(", ")} ${reason}. Explicit provider/model selectors fail closed and do not fall back to the parent session model.`,
|
|
1797
|
+
);
|
|
1798
|
+
}
|
|
1766
1799
|
if (
|
|
1767
1800
|
(options.preflightProbe || options.preflightDurable) &&
|
|
1768
1801
|
model &&
|
package/src/task/types.ts
CHANGED
|
@@ -92,7 +92,9 @@ const repositoryBindingSchema = z
|
|
|
92
92
|
branch: z.string().min(1).optional(),
|
|
93
93
|
})
|
|
94
94
|
.strict()
|
|
95
|
-
.describe(
|
|
95
|
+
.describe(
|
|
96
|
+
"authoritative repository identity for multi-repo fail-closed spawn; must match the active session worktree. To target another approved repository, start a new session there (for example, gjc --cwd <approved-worktree>); changing tasks[].repositoryBinding cannot authorize a foreign repository.",
|
|
97
|
+
);
|
|
96
98
|
|
|
97
99
|
const createTaskItemSchema = (_contextEnabled: boolean) =>
|
|
98
100
|
z.object({
|
|
@@ -118,7 +120,7 @@ const createTaskItemSchema = (_contextEnabled: boolean) =>
|
|
|
118
120
|
repositoryBinding: repositoryBindingSchema
|
|
119
121
|
.optional()
|
|
120
122
|
.describe(
|
|
121
|
-
"authoritative repository identity; omitted items are stamped from session cwd before discovery/spawn and still fail closed on sibling drift",
|
|
123
|
+
"authoritative repository identity; omitted items are stamped from session cwd before discovery/spawn and still fail closed on sibling drift. A declared binding must match the active session; use a new session rooted in the approved worktree (for example, gjc --cwd <approved-worktree>) for another repository.",
|
|
122
124
|
),
|
|
123
125
|
duplicate_policy: z.enum(["warn", "supersede"]).optional().describe("duplicate launch policy; defaults to warn"),
|
|
124
126
|
});
|
|
@@ -352,12 +354,31 @@ const SETUP_FAILURE_SUMMARY_MAX_CHARS = 280;
|
|
|
352
354
|
const SETUP_FAILURE_SUMMARY_MAX_BYTES = 1_024;
|
|
353
355
|
const AUTHORIZATION_HEADER_VALUE_PATTERN = /(["']?(?:Proxy-)?Authorization\b["']?\s*:\s*)[^\r\n]*/gi;
|
|
354
356
|
const COOKIE_HEADER_VALUE_PATTERN = /(["']?(?:Set-)?Cookie\b["']?\s*:\s*)[^\r\n]*/gi;
|
|
355
|
-
|
|
357
|
+
// The scheme-character run is boundary anchored, so the unbounded suffix is
|
|
358
|
+
// attempted once per maximal run instead of once at every prefix. Keeping the
|
|
359
|
+
// leading non-letter characters in the capture preserves redaction for URLs
|
|
360
|
+
// embedded after digits or scheme punctuation without imposing an arbitrary
|
|
361
|
+
// scheme-length cap.
|
|
362
|
+
const URL_CREDENTIAL_PATTERN = /(?<![A-Za-z0-9+.-])([0-9+.-]*[a-z][a-z0-9+.-]*:\/\/)[^/?#\s:@]+:[^@/?#\s]+@/gi;
|
|
356
363
|
const API_KEY_LABEL_VALUE_PATTERN = /(["']?api\s+key["']?\s*:\s*)(?:"[^"]*"|'[^']*'|[^\s&]+)/gi;
|
|
364
|
+
// Both name-prefix repetitions are bounded. `(?:[A-Za-z][A-Za-z0-9]*[_.-])*?`
|
|
365
|
+
// nests an unbounded quantifier inside an unbounded quantified group, so at every
|
|
366
|
+
// offset of a long identifier run the inner class scans to the end of the run
|
|
367
|
+
// before backtracking to look for a separator. That is quadratic in the length of
|
|
368
|
+
// the failure text: 50 KB cost ~1.7s. Sixteen segments of at most 64 characters is
|
|
369
|
+
// far past any real credential name, and the suffix group below still absorbs
|
|
370
|
+
// arbitrary trailing segments.
|
|
357
371
|
const SENSITIVE_SETUP_FAILURE_VALUE_PATTERN =
|
|
358
|
-
/(["']?(?:
|
|
372
|
+
/(["']?(?:[A-Za-z][A-Za-z0-9]{0,63}[_.-]){0,16}?(?:access[_-]?token|refresh[_-]?token|session[_-]?token|id[_-]?token|api[_-]?key|client[_-]?secret|private[_-]?key|signing[_-]?key|secret|password|passwd|pwd|authorization|credential|token)(?:[_.-][A-Za-z0-9]+)*["']?)(\s*[=:]\s*)(?:"[^"]*"|'[^']*'|[^\s&]+)/gi;
|
|
373
|
+
// `AKIA` is only one of four AWS access-key id prefixes, and the npm, GitLab,
|
|
374
|
+
// Stripe and Hugging Face shapes are the ones `crash/upstream/envelope.ts`
|
|
375
|
+
// already classifies as credential-like. Stripe and Hugging Face separate with
|
|
376
|
+
// `_`, so the `sk-` alternatives above never matched them.
|
|
359
377
|
const BARE_PROVIDER_TOKEN_PATTERN =
|
|
360
|
-
/\b(?:gh[pousr]_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,}|xox[baprs]-[A-Za-z0-9-]{20,}|sk-(?:proj-)?[A-Za-z0-9_-]{20,}|sk-ant-[A-Za-z0-9_-]{20,}|AIza[A-Za-z0-9_-]{35}|AKIA[A-Z0-9]{16})\b/g;
|
|
378
|
+
/\b(?:gh[pousr]_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,}|xox[baprs]-[A-Za-z0-9-]{20,}|sk-(?:proj-)?[A-Za-z0-9_-]{20,}|sk-ant-[A-Za-z0-9_-]{20,}|AIza[A-Za-z0-9_-]{35}|(?:AKIA|ASIA|ABIA|ACCA)[A-Z0-9]{16}|npm_[A-Za-z0-9]{20,}|glpat-[A-Za-z0-9_-]{20,}|(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}|hf_[A-Za-z0-9]{20,})\b/g;
|
|
379
|
+
// A PEM block carries the key material itself and is redacted whole, before the
|
|
380
|
+
// narrower rules can consume its base64 body and leave a truncated key behind.
|
|
381
|
+
const PEM_PRIVATE_KEY_PATTERN = /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z0-9 ]*PRIVATE KEY-----/g;
|
|
361
382
|
const LOCAL_ABSOLUTE_PATH_PATTERN = /(^|[\s("'`=])((?:\/(?!\/)[^\s/:?"'`()[\]{},;<>]+){2,})/g;
|
|
362
383
|
const WINDOWS_ABSOLUTE_PATH_PATTERN =
|
|
363
384
|
/(^|[\s("'`=])([A-Za-z]:\\(?:[^\\/:?"'`()[\]{},;<>\s]+\\)+[^\\/:?"'`()[\]{},;<>\s]+)/g;
|
|
@@ -417,6 +438,7 @@ function capSetupFailureSummary(value: string): string {
|
|
|
417
438
|
export function createSetupFailureSummary(error: unknown): SetupFailureSummary {
|
|
418
439
|
const message = normalizeSetupFailureText(error instanceof Error ? error.message : String(error));
|
|
419
440
|
const summary = message
|
|
441
|
+
.replace(PEM_PRIVATE_KEY_PATTERN, "[redacted]")
|
|
420
442
|
.replace(AUTHORIZATION_HEADER_VALUE_PATTERN, "$1[redacted]")
|
|
421
443
|
.replace(COOKIE_HEADER_VALUE_PATTERN, "$1[redacted]")
|
|
422
444
|
.replace(URL_CREDENTIAL_PATTERN, "$1[redacted]@")
|
package/src/tools/ask.ts
CHANGED
|
@@ -109,6 +109,37 @@ export interface AskToolDetails {
|
|
|
109
109
|
export const OTHER_OPTION = "Other (type your own)";
|
|
110
110
|
export const ASK_CLARIFICATION_OPTION = "Ask about these choices";
|
|
111
111
|
export const RECOMMENDED_SUFFIX = " (Recommended)";
|
|
112
|
+
/**
|
|
113
|
+
* Environment override bounding how long a headless ask waits for its answer, in
|
|
114
|
+
* milliseconds. Unset, empty, non-positive, non-integer, or larger than
|
|
115
|
+
* {@link MAX_ASK_ANSWER_DEADLINE_MS} disables the bound.
|
|
116
|
+
*/
|
|
117
|
+
export const GJC_ASK_ANSWER_DEADLINE_MS_ENV = "GJC_ASK_ANSWER_DEADLINE_MS";
|
|
118
|
+
/**
|
|
119
|
+
* Largest deadline the runtime timer can represent. `setTimeout` silently clamps
|
|
120
|
+
* a longer delay to one millisecond, which would invert the contract and abort a
|
|
121
|
+
* headless ask almost immediately, so a value beyond this is not a usable bound
|
|
122
|
+
* and is treated as disabled exactly like any other invalid value.
|
|
123
|
+
*/
|
|
124
|
+
export const MAX_ASK_ANSWER_DEADLINE_MS = 2_147_483_647;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* How long a headless ask may wait for an answer before it gives up.
|
|
128
|
+
*
|
|
129
|
+
* A headless ask has no local selector, so it is answered only by a remote
|
|
130
|
+
* answer source or by a workflow gate waiting for a remote responder. Neither
|
|
131
|
+
* channel reports that the question was taken up, so the only observable event
|
|
132
|
+
* is the answer itself: this is an answer deadline, not an acknowledgement.
|
|
133
|
+
* `GJC_ASK_ANSWER_DEADLINE_MS` arms it. It stays disabled by default: an
|
|
134
|
+
* attended remote responder may legitimately answer arbitrarily late, and
|
|
135
|
+
* silently cutting such a wait short would corrupt a real answer into an abort.
|
|
136
|
+
*/
|
|
137
|
+
export function resolveAskAnswerDeadlineMs(env: Record<string, string | undefined> = process.env): number | null {
|
|
138
|
+
const raw = env[GJC_ASK_ANSWER_DEADLINE_MS_ENV];
|
|
139
|
+
if (raw === undefined) return null;
|
|
140
|
+
const parsed = Number(raw);
|
|
141
|
+
return Number.isSafeInteger(parsed) && parsed > 0 && parsed <= MAX_ASK_ANSWER_DEADLINE_MS ? parsed : null;
|
|
142
|
+
}
|
|
112
143
|
const REMOTE_NAVIGATION_FORWARD = "\u0000ask-navigation-forward";
|
|
113
144
|
const HEADLESS_CHECKBOX_CHECKED = "[x]";
|
|
114
145
|
const HEADLESS_CHECKBOX_UNCHECKED = "[ ]";
|
|
@@ -825,10 +856,7 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
825
856
|
): Promise<AgentToolResult<AskToolDetails>> {
|
|
826
857
|
await assertUltragoalAskAllowed(
|
|
827
858
|
this.session.cwd,
|
|
828
|
-
{
|
|
829
|
-
activeSkillState: this.session.getActiveSkillState?.(),
|
|
830
|
-
sessionId: this.session.getSessionId?.() ?? null,
|
|
831
|
-
},
|
|
859
|
+
{ sessionId: this.session.getSessionId?.() ?? null },
|
|
832
860
|
this.session.getSessionAgentDir?.() ?? this.session.settings.getAgentDir(),
|
|
833
861
|
);
|
|
834
862
|
assertDeepInterviewStructuredResponseWithinLimit(params);
|
|
@@ -871,7 +899,55 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
871
899
|
throw new ToolAbortError("Ask tool requires interactive mode");
|
|
872
900
|
}
|
|
873
901
|
|
|
902
|
+
// Only a headless ask needs an answer deadline: an interactive UI answers
|
|
903
|
+
// locally, and its own `ask.timeout` already applies there.
|
|
904
|
+
const askAnswerDeadlineMs = hasInteractiveUi ? null : resolveAskAnswerDeadlineMs();
|
|
905
|
+
const askDeadlineExpired = Symbol("ask-deadline-expired");
|
|
906
|
+
/**
|
|
907
|
+
* Bound one headless answer wait. No answer source reports that it has taken
|
|
908
|
+
* the question, so the only observable event is the answer itself; on expiry
|
|
909
|
+
* the ask must fail with a distinguishable origin instead of degrading into
|
|
910
|
+
* the empty answer the model would otherwise read as "the user declined",
|
|
911
|
+
* and the enclosing turn is aborted so the stall cannot outlive the call.
|
|
912
|
+
*/
|
|
913
|
+
const awaitAskAnswer = async <T>(work: () => Promise<T>): Promise<T> => {
|
|
914
|
+
if (askAnswerDeadlineMs === null) return await work();
|
|
915
|
+
const expired = Promise.withResolvers<typeof askDeadlineExpired>();
|
|
916
|
+
const timer = setTimeout(() => expired.resolve(askDeadlineExpired), askAnswerDeadlineMs);
|
|
917
|
+
try {
|
|
918
|
+
const settled = await Promise.race([work(), expired.promise]);
|
|
919
|
+
if (settled !== askDeadlineExpired) return settled;
|
|
920
|
+
logger.warn("ask_answer_deadline_exceeded", {
|
|
921
|
+
sessionId: this.session.getSessionId?.() ?? null,
|
|
922
|
+
// Re-derive the routed channel at expiry: the answer source can be
|
|
923
|
+
// registered or removed after the ask entered `execute`.
|
|
924
|
+
answerSource:
|
|
925
|
+
!this.session.getAskAnswerSource?.() && gateEmitter?.supportsRemoteGateAnswers() === true
|
|
926
|
+
? "workflow_gate"
|
|
927
|
+
: "remote",
|
|
928
|
+
deadlineMs: askAnswerDeadlineMs,
|
|
929
|
+
});
|
|
930
|
+
await settleActiveRemote({ kind: "resolve_without_commit", reason: "aborted" });
|
|
931
|
+
activeRemoteRequest = undefined;
|
|
932
|
+
context?.abort();
|
|
933
|
+
throw new ToolAbortError(
|
|
934
|
+
`Ask was aborted: no answer was received within ${askAnswerDeadlineMs / 1000}s of the headless ask answer deadline`,
|
|
935
|
+
);
|
|
936
|
+
} finally {
|
|
937
|
+
clearTimeout(timer);
|
|
938
|
+
}
|
|
939
|
+
};
|
|
940
|
+
|
|
874
941
|
const extensionUi = context?.ui;
|
|
942
|
+
const throwRemoteCancellation = (remoteSignal: AbortSignal, toolSignal?: AbortSignal): never => {
|
|
943
|
+
// A headless remote answer source owns the only interactive surface. When it
|
|
944
|
+
// explicitly closes the ask, abort the enclosing turn as well; otherwise the
|
|
945
|
+
// tool throws while the foreground prompt keeps running and the next client
|
|
946
|
+
// prompt is rejected as already active. An abort already initiated by the
|
|
947
|
+
// caller must not be reported as a second, remote cancellation.
|
|
948
|
+
if (!extensionUi && !remoteSignal.aborted && !toolSignal?.aborted) context?.abort();
|
|
949
|
+
throw new ToolAbortError("Ask was cancelled by the remote client");
|
|
950
|
+
};
|
|
875
951
|
const ui: UIContext = {
|
|
876
952
|
select: (prompt, options, dialogOptions) => {
|
|
877
953
|
const source = this.session.getAskAnswerSource?.();
|
|
@@ -908,7 +984,7 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
908
984
|
)
|
|
909
985
|
.then((answer): RemoteRaceResult | Promise<RemoteRaceResult> => {
|
|
910
986
|
if (answer === undefined) {
|
|
911
|
-
if (!extensionUi)
|
|
987
|
+
if (!extensionUi) return throwRemoteCancellation(remoteController.signal, toolSignal);
|
|
912
988
|
return new Promise<never>(() => {});
|
|
913
989
|
}
|
|
914
990
|
const receipt = typeof answer === "string" ? legacyAskReceipt(answer) : answer;
|
|
@@ -989,20 +1065,22 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
989
1065
|
// The losing selector may reject when aborted after the race already settled;
|
|
990
1066
|
// swallow that so it is not an unhandled rejection (the race result is unaffected).
|
|
991
1067
|
void local.catch(() => undefined);
|
|
992
|
-
return
|
|
993
|
-
.
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1068
|
+
return awaitAskAnswer(() =>
|
|
1069
|
+
Promise.race([local, remote])
|
|
1070
|
+
.then(async result => {
|
|
1071
|
+
if (result.winner === "remote") {
|
|
1072
|
+
localController.abort();
|
|
1073
|
+
if (result.settlement) await result.receipt.settle(result.settlement);
|
|
1074
|
+
else activeRemoteReceipt = result.receipt;
|
|
1075
|
+
} else {
|
|
1076
|
+
void remote.then(remoteResult =>
|
|
1077
|
+
remoteResult.receipt.settle({ kind: "resolve_without_commit", reason: "aborted" }),
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
return result.value;
|
|
1081
|
+
})
|
|
1082
|
+
.finally(() => toolSignal?.removeEventListener("abort", abortRace)),
|
|
1083
|
+
);
|
|
1006
1084
|
},
|
|
1007
1085
|
editor: (title, prefill, dialogOptions, editorOptions) => {
|
|
1008
1086
|
const source = this.session.getAskAnswerSource?.();
|
|
@@ -1041,7 +1119,7 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
1041
1119
|
)
|
|
1042
1120
|
.then((answer): RemoteRaceResult | Promise<RemoteRaceResult> => {
|
|
1043
1121
|
if (answer === undefined) {
|
|
1044
|
-
if (!extensionUi)
|
|
1122
|
+
if (!extensionUi) return throwRemoteCancellation(remoteController.signal, toolSignal);
|
|
1045
1123
|
return new Promise<never>(() => {});
|
|
1046
1124
|
}
|
|
1047
1125
|
const receipt = typeof answer === "string" ? legacyAskReceipt(answer) : answer;
|
|
@@ -1078,19 +1156,21 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
1078
1156
|
})
|
|
1079
1157
|
: new Promise<never>(() => {});
|
|
1080
1158
|
void local.catch(() => undefined);
|
|
1081
|
-
return
|
|
1082
|
-
.
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1159
|
+
return awaitAskAnswer(() =>
|
|
1160
|
+
Promise.race([local, remote])
|
|
1161
|
+
.then(result => {
|
|
1162
|
+
if (result.winner === "remote") {
|
|
1163
|
+
activeRemoteReceipt = result.receipt;
|
|
1164
|
+
localController.abort();
|
|
1165
|
+
} else {
|
|
1166
|
+
void remote.then(remoteResult =>
|
|
1167
|
+
remoteResult.receipt.settle({ kind: "resolve_without_commit", reason: "aborted" }),
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
return result.value;
|
|
1171
|
+
})
|
|
1172
|
+
.finally(() => toolSignal?.removeEventListener("abort", abortRace)),
|
|
1173
|
+
);
|
|
1094
1174
|
},
|
|
1095
1175
|
};
|
|
1096
1176
|
|
|
@@ -1137,7 +1217,7 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
1137
1217
|
allowEmpty: q.multi === true && params.questions.length > 1,
|
|
1138
1218
|
navigationLabel: questionIndex === params.questions.length - 1 ? "Done" : "Next",
|
|
1139
1219
|
};
|
|
1140
|
-
const answer = await gateEmitter.emitGate(questionToGate(gateQuestion));
|
|
1220
|
+
const answer = await awaitAskAnswer(() => gateEmitter.emitGate(questionToGate(gateQuestion)));
|
|
1141
1221
|
const decoded = gateAnswerToResult(gateQuestion, answer);
|
|
1142
1222
|
return {
|
|
1143
1223
|
optionLabels: rawOptionLabels,
|
|
@@ -154,7 +154,7 @@ for (const s of __steps) {
|
|
|
154
154
|
__results.push({ verb: "select", selected: await tab.select(s.selector, ...(s.values || [])) });
|
|
155
155
|
break;
|
|
156
156
|
case "press":
|
|
157
|
-
await tab.press(s.key, s.selector ? { selector: s.selector } : undefined);
|
|
157
|
+
await tab.press(s.key, s.selector !== undefined ? { selector: s.selector } : undefined);
|
|
158
158
|
__results.push({ verb: "press", key: s.key });
|
|
159
159
|
break;
|
|
160
160
|
case "scroll":
|
|
@@ -166,7 +166,7 @@ for (const s of __steps) {
|
|
|
166
166
|
__results.push({ verb: "back" });
|
|
167
167
|
break;
|
|
168
168
|
case "wait":
|
|
169
|
-
if (s.selector) { await tab.waitFor(s.selector); }
|
|
169
|
+
if (s.selector !== undefined) { await tab.waitFor(s.selector); }
|
|
170
170
|
else { await new Promise(r => setTimeout(r, s.ms)); }
|
|
171
171
|
__results.push({ verb: "wait", selector: s.selector ?? null, ms: s.ms ?? null });
|
|
172
172
|
break;
|
|
@@ -137,8 +137,12 @@ interface TabApi {
|
|
|
137
137
|
id(n: number): Promise<ElementHandle>;
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
function normalizeSelector(selector:
|
|
141
|
-
if (
|
|
140
|
+
function normalizeSelector(selector: unknown): string {
|
|
141
|
+
if (typeof selector !== "string" || selector.trim().length === 0) {
|
|
142
|
+
throw new ToolError(
|
|
143
|
+
"Selector must be a non-empty string (CSS or a Puppeteer query handler). For numeric observation ids, use (await tab.id(id)).click() instead of tab.click(id).",
|
|
144
|
+
);
|
|
145
|
+
}
|
|
142
146
|
if (selector.startsWith("p-") && !LEGACY_SELECTOR_PREFIXES.some(prefix => selector.startsWith(prefix))) {
|
|
143
147
|
throw new ToolError(
|
|
144
148
|
`Unsupported selector prefix. Use CSS or puppeteer query handlers (aria/, text/, xpath/, pierce/). Got: ${selector}`,
|
|
@@ -735,7 +739,7 @@ export class WorkerCore {
|
|
|
735
739
|
},
|
|
736
740
|
press: async (key, opts) => {
|
|
737
741
|
const selector = opts?.selector;
|
|
738
|
-
if (selector) await untilAborted(signal, () => page.focus(normalizeSelector(selector)));
|
|
742
|
+
if (selector !== undefined) await untilAborted(signal, () => page.focus(normalizeSelector(selector)));
|
|
739
743
|
await untilAborted(signal, () => page.keyboard.press(key));
|
|
740
744
|
},
|
|
741
745
|
scroll: async (deltaX, deltaY) => {
|
|
@@ -829,12 +833,11 @@ export class WorkerCore {
|
|
|
829
833
|
opts: ScreenshotOptions = {},
|
|
830
834
|
): Promise<ScreenshotResult> {
|
|
831
835
|
const page = this.#requirePage();
|
|
832
|
-
const
|
|
836
|
+
const selector = opts.selector === undefined ? undefined : normalizeSelector(opts.selector);
|
|
837
|
+
const fullPage = selector !== undefined ? false : (opts.fullPage ?? false);
|
|
833
838
|
let buffer: Buffer;
|
|
834
|
-
if (
|
|
835
|
-
const handle = (await untilAborted(signal, () =>
|
|
836
|
-
page.$(normalizeSelector(opts.selector!)),
|
|
837
|
-
)) as ElementHandle | null;
|
|
839
|
+
if (selector !== undefined) {
|
|
840
|
+
const handle = (await untilAborted(signal, () => page.$(selector))) as ElementHandle | null;
|
|
838
841
|
if (!handle) throw new ToolError("Screenshot selector did not resolve to an element");
|
|
839
842
|
try {
|
|
840
843
|
buffer = (await untilAborted(signal, () => handle.screenshot({ type: "png" }))) as Buffer;
|
package/src/tools/descriptors.ts
CHANGED
|
@@ -452,7 +452,12 @@ const names: Array<[name: string, label: string, summary: string | undefined, lo
|
|
|
452
452
|
["todo_write", "Todo Write", "Write a structured todo list", "discoverable"],
|
|
453
453
|
["web_search", "Web Search", "Search the web for up-to-date information", "discoverable"],
|
|
454
454
|
["search_tool_bm25", "SearchTools", undefined, "essential"],
|
|
455
|
-
[
|
|
455
|
+
[
|
|
456
|
+
"skill_discovery",
|
|
457
|
+
"SkillDiscovery",
|
|
458
|
+
"Discover bundled GJC workflow, project, and user runtime skills by thin metadata",
|
|
459
|
+
"essential",
|
|
460
|
+
],
|
|
456
461
|
["telegram_send", "TelegramSend", "Send a workspace file to Telegram", "discoverable"],
|
|
457
462
|
["write", "Write", "Write content to a file", "discoverable"],
|
|
458
463
|
["skill", "Skill", "Chain into another available skill", "essential"],
|
package/src/tools/fetch.ts
CHANGED
|
@@ -764,7 +764,9 @@ async function renderUrl(
|
|
|
764
764
|
let convertedText: string | null = null;
|
|
765
765
|
const converted = await convertWithMarkit(binary.buffer, conversionExtension, timeout, signal);
|
|
766
766
|
if (converted.ok) {
|
|
767
|
-
|
|
767
|
+
// See the non-empty conversion note near the document path
|
|
768
|
+
// below (#5433): short conversions still beat raw fallback.
|
|
769
|
+
if (converted.content.trim().length > 0) {
|
|
768
770
|
notes.push("Converted with markit");
|
|
769
771
|
convertedText = converted.content;
|
|
770
772
|
} else {
|
|
@@ -872,7 +874,10 @@ async function renderUrl(
|
|
|
872
874
|
const ext = getExtensionHint(finalUrl, binary.contentDisposition) || extHint;
|
|
873
875
|
const converted = await convertWithMarkit(binary.buffer, ext, timeout, signal);
|
|
874
876
|
if (converted.ok) {
|
|
875
|
-
|
|
877
|
+
// Any non-empty markit conversion is preferable to a raw-bytes
|
|
878
|
+
// fallback (#5433): short documents like the W3C dummy.pdf
|
|
879
|
+
// ("Dummy PDF file", 14 chars) are legitimately converted text.
|
|
880
|
+
if (converted.content.trim().length > 0) {
|
|
876
881
|
notes.push("Converted with markit");
|
|
877
882
|
const output = finalizeOutput(converted.content);
|
|
878
883
|
return {
|
package/src/tools/image-gen.ts
CHANGED
|
@@ -31,7 +31,6 @@ import imageGenDescription from "../prompts/tools/image-gen.md" with { type: "te
|
|
|
31
31
|
import { isPrivateOrSpecialAddress, validatePublicHttpUrl } from "../web/insane/url-guard";
|
|
32
32
|
import { resolveReadPath } from "./path-utils";
|
|
33
33
|
|
|
34
|
-
const DEFAULT_MODEL = "gemini-3-pro-image-preview";
|
|
35
34
|
const IMAGE_TIMEOUT = 3 * 60 * 1000; // 3 minutes
|
|
36
35
|
const MAX_IMAGE_SIZE = 35 * 1024 * 1024;
|
|
37
36
|
const MAX_IMAGE_REDIRECTS = 5;
|
|
@@ -70,7 +69,35 @@ export function redactImageProviderText(value: unknown, activeApiKey?: string):
|
|
|
70
69
|
/(?:\b|["'])(?:authorization|api[-_ ]?key|access[-_ ]?token|refresh[-_ ]?token|token)["']?\s*[:=]\s*(?:"[^"]*"|'[^']*'|[^\s,;}]+)/gi,
|
|
71
70
|
match => match.replace(/([:=]\s*).*/, `$1${REDACTED_PROVIDER_SECRET}`),
|
|
72
71
|
)
|
|
73
|
-
|
|
72
|
+
// A PEM block is redacted whole and runs before the narrower rules, which
|
|
73
|
+
// would otherwise consume its base64 body and leave a truncated key behind.
|
|
74
|
+
.replace(
|
|
75
|
+
/-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z0-9 ]*PRIVATE KEY-----/g,
|
|
76
|
+
REDACTED_PROVIDER_SECRET,
|
|
77
|
+
)
|
|
78
|
+
.replace(/\b(?:sk|rk|pk|sess|xox[baprs])-[-A-Za-z0-9._]{8,}\b/gi, REDACTED_PROVIDER_SECRET)
|
|
79
|
+
// GitHub tokens separate with `_`, not the `-` the rule above requires, so
|
|
80
|
+
// listing `ghp`/`gho`/`github_pat` there never matched one. Short tokens
|
|
81
|
+
// then fell through the 40-character catch-all below entirely.
|
|
82
|
+
.replace(/\b(?:gh[opsur]_[A-Za-z0-9_]{12,}|github_pat_[A-Za-z0-9_]{12,})\b/g, REDACTED_PROVIDER_SECRET)
|
|
83
|
+
// The shapes `crash/upstream/envelope.ts` classifies as credential-like.
|
|
84
|
+
// A GitLab PAT and a Hugging Face token are both shorter than the 40-character
|
|
85
|
+
// catch-all below, and Stripe separates with `_` so the `sk|rk|pk`-hyphen rule
|
|
86
|
+
// above never matched one.
|
|
87
|
+
.replace(
|
|
88
|
+
/\b(?:npm_[A-Za-z0-9]{20,}|glpat-[A-Za-z0-9_-]{20,}|(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}|hf_[A-Za-z0-9]{20,})\b/g,
|
|
89
|
+
REDACTED_PROVIDER_SECRET,
|
|
90
|
+
)
|
|
91
|
+
// AWS access-key ids are 20 characters, so the catch-all never reached them.
|
|
92
|
+
.replace(/\b(?:AKIA|ASIA|ABIA|ACCA)[0-9A-Z]{16}\b/g, REDACTED_PROVIDER_SECRET)
|
|
93
|
+
// A Google API key is exactly 39 characters — one short of the catch-all.
|
|
94
|
+
.replace(/\bAIza[0-9A-Za-z_-]{35}\b/g, REDACTED_PROVIDER_SECRET)
|
|
95
|
+
// Basic-auth credentials in a URL. The scheme repetition is bounded so a
|
|
96
|
+
// long alphabetic run cannot be re-tried at every prefix (#5346).
|
|
97
|
+
.replace(
|
|
98
|
+
/(?<![A-Za-z0-9+.-])([a-z][a-z0-9+.-]{0,15}:\/\/)[^/\s:@]{1,256}:[^/\s@]{1,256}@/gi,
|
|
99
|
+
`$1${REDACTED_PROVIDER_SECRET}@`,
|
|
100
|
+
)
|
|
74
101
|
.replace(/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g, REDACTED_PROVIDER_SECRET)
|
|
75
102
|
.replace(/\b[A-Za-z0-9+/_-]{40,}={0,2}\b/g, REDACTED_PROVIDER_SECRET);
|
|
76
103
|
return text.length > MAX_PROVIDER_TEXT_LENGTH ? `${text.slice(0, MAX_PROVIDER_TEXT_LENGTH - 1)}…` : text;
|
|
@@ -815,16 +842,6 @@ function extractOpenRouterImageUrls(message: OpenRouterMessage | undefined): str
|
|
|
815
842
|
return urls;
|
|
816
843
|
}
|
|
817
844
|
|
|
818
|
-
/** Provider → default image model mapping for fallbacks */
|
|
819
|
-
export const IMAGE_PROVIDER_DEFAULTS: Record<string, string> = {
|
|
820
|
-
openai: "gpt-image-2",
|
|
821
|
-
alibaba: "wan2.7-image",
|
|
822
|
-
"openai-codex": "gpt-image-2",
|
|
823
|
-
antigravity: "gemini-3-pro-image",
|
|
824
|
-
gemini: "gemini-3-pro-image-preview",
|
|
825
|
-
openrouter: "google/gemini-3-pro-image-preview",
|
|
826
|
-
};
|
|
827
|
-
|
|
828
845
|
/**
|
|
829
846
|
* Resolve the image-generation model from the `modelRoles.image` settings entry.
|
|
830
847
|
* Returns the resolved Model (with provider identity) or undefined when no
|
|
@@ -847,12 +864,6 @@ export function resolveImageRoleModel(
|
|
|
847
864
|
return resolved.model;
|
|
848
865
|
}
|
|
849
866
|
|
|
850
|
-
/** Resolve the effective image model for a configured provider */
|
|
851
|
-
export function resolveImageModel(provider: string, modelOverride: string | null): string {
|
|
852
|
-
if (modelOverride) return modelOverride;
|
|
853
|
-
return IMAGE_PROVIDER_DEFAULTS[provider] ?? DEFAULT_MODEL;
|
|
854
|
-
}
|
|
855
|
-
|
|
856
867
|
interface ParsedAntigravityCredentials {
|
|
857
868
|
accessToken: string;
|
|
858
869
|
projectId?: string;
|
|
@@ -941,10 +952,11 @@ async function findImageApiKey(
|
|
|
941
952
|
settings: ModelRoleSettings,
|
|
942
953
|
sessionId?: string,
|
|
943
954
|
credentialSessionId?: string,
|
|
944
|
-
): Promise<ImageApiKey | null> {
|
|
955
|
+
): Promise<(ImageApiKey & { model: Model }) | null> {
|
|
945
956
|
const imageModel = resolveImageRoleModel(settings, modelRegistry, { sessionId, credentialSessionId });
|
|
946
957
|
if (!imageModel) return null;
|
|
947
|
-
|
|
958
|
+
const credentials = await resolveCredentialsForImageModel(imageModel, modelRegistry, sessionId);
|
|
959
|
+
return credentials ? { ...credentials, model: imageModel } : null;
|
|
948
960
|
}
|
|
949
961
|
|
|
950
962
|
/**
|
|
@@ -1492,16 +1504,7 @@ export const imageGenTool: CustomTool<typeof imageGenSchema, ImageGenToolDetails
|
|
|
1492
1504
|
}
|
|
1493
1505
|
|
|
1494
1506
|
const provider = apiKey.provider;
|
|
1495
|
-
const
|
|
1496
|
-
const model = imageModel
|
|
1497
|
-
? imageModel.id
|
|
1498
|
-
: provider === "antigravity"
|
|
1499
|
-
? resolveImageModel("antigravity", null)
|
|
1500
|
-
: provider === "alibaba"
|
|
1501
|
-
? resolveImageModel("alibaba", null)
|
|
1502
|
-
: provider === "openrouter"
|
|
1503
|
-
? resolveImageModel("openrouter", null)
|
|
1504
|
-
: resolveImageModel("gemini", null);
|
|
1507
|
+
const model = apiKey.model.id;
|
|
1505
1508
|
const resolvedModel = provider === "openrouter" ? resolveOpenRouterModel(model) : model;
|
|
1506
1509
|
const cwd = ctx.sessionManager.getCwd();
|
|
1507
1510
|
|
|
@@ -1515,10 +1518,6 @@ export const imageGenTool: CustomTool<typeof imageGenSchema, ImageGenToolDetails
|
|
|
1515
1518
|
const requestSignal = ptree.combineSignals(signal, IMAGE_TIMEOUT);
|
|
1516
1519
|
|
|
1517
1520
|
if (provider === "openai" || provider === "openai-codex") {
|
|
1518
|
-
if (!apiKey.model) {
|
|
1519
|
-
throw new Error("Missing active GPT model for OpenAI image generation");
|
|
1520
|
-
}
|
|
1521
|
-
|
|
1522
1521
|
const parsed = await generateOpenAIHostedImage(
|
|
1523
1522
|
apiKey.apiKey,
|
|
1524
1523
|
apiKey.model,
|