@gajae-code/coding-agent 0.18.6 → 0.18.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/CHANGELOG.md +318 -1
- package/dist/types/async/job-manager.d.ts +1 -1
- package/dist/types/capability/mcp.d.ts +2 -0
- package/dist/types/capability/skill.d.ts +2 -0
- package/dist/types/capability/types.d.ts +6 -0
- package/dist/types/cli/public-command-errors.d.ts +3 -1
- package/dist/types/cli/setup-cli.d.ts +1 -1
- package/dist/types/commands/deep-interview.d.ts +3 -0
- package/dist/types/config/autorouting-tier-map.d.ts +348 -9
- package/dist/types/config/file-lock.d.ts +1 -0
- package/dist/types/config/model-bindings-applier.d.ts +2 -0
- package/dist/types/config/model-profile-activation.d.ts +11 -3
- package/dist/types/config/model-registry.d.ts +4 -9
- package/dist/types/config/settings.d.ts +4 -0
- package/dist/types/config/skill-settings-defaults.d.ts +2 -0
- package/dist/types/cursor-delete-path.d.ts +1 -0
- package/dist/types/cursor-mcp-name.d.ts +8 -0
- package/dist/types/customization/inventory.d.ts +2 -0
- package/dist/types/debug/report-bundle-parent.d.ts +2 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.d.ts +1 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.d.ts +31 -0
- package/dist/types/defaults/gjc-skills.generated.d.ts +5 -6
- package/dist/types/discovery/helpers.d.ts +8 -3
- package/dist/types/export/html/css-color.d.ts +2 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/extensions/types.d.ts +7 -0
- package/dist/types/extensibility/gjc-plugins/runtime-adapters.d.ts +14 -1
- package/dist/types/extensibility/plugins/marketplace/cache.d.ts +2 -0
- package/dist/types/extensibility/plugins/plugin-link-path.d.ts +2 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +3 -1
- package/dist/types/extensibility/skill-management.d.ts +3 -1
- package/dist/types/extensibility/skills.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-crystallize.d.ts +74 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +11 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +5 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +0 -5
- package/dist/types/gjc-runtime/state-argv.d.ts +1 -1
- package/dist/types/gjc-runtime/state-migrations.d.ts +8 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +95 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +11 -1
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -0
- package/dist/types/internal-urls/skill-path.d.ts +1 -0
- package/dist/types/lsp/workspace-path.d.ts +1 -0
- package/dist/types/modes/acp/acp-agent.d.ts +7 -0
- package/dist/types/modes/components/custom-editor.d.ts +6 -0
- package/dist/types/modes/components/customization/customization-dashboard.d.ts +1 -0
- package/dist/types/modes/components/extensions/state-manager.d.ts +4 -3
- package/dist/types/modes/controllers/input-controller.d.ts +9 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/utils/interrupt-ownership.d.ts +3 -0
- package/dist/types/modes/utils/keybinding-matchers.d.ts +1 -0
- package/dist/types/modes/utils/macos-option-forwarding.d.ts +17 -0
- package/dist/types/progress/collect-project-progress.d.ts +38 -0
- package/dist/types/progress/progress-contract.d.ts +154 -0
- package/dist/types/progress/progress-snapshot.d.ts +11 -0
- package/dist/types/progress/project-progress.d.ts +141 -0
- package/dist/types/progress/render-progress.d.ts +28 -0
- package/dist/types/runtime-mcp/elicitation.d.ts +12 -0
- package/dist/types/runtime-mcp/oauth-public-url.d.ts +1 -0
- package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +1 -0
- package/dist/types/runtime-mcp/smithery-registry.d.ts +24 -0
- package/dist/types/runtime-mcp/types.d.ts +6 -0
- package/dist/types/sdk/acp/adapter.d.ts +1 -1
- package/dist/types/sdk/broker/broker-exit.d.ts +6 -2
- package/dist/types/sdk/broker/broker.d.ts +18 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +9 -0
- package/dist/types/sdk/broker/session-index.d.ts +12 -6
- package/dist/types/sdk/broker/task-artifact-owner-validation.d.ts +55 -0
- package/dist/types/sdk/bus/index.d.ts +12 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +3 -2
- package/dist/types/sdk/client/client.d.ts +2 -0
- package/dist/types/sdk/host/host.d.ts +6 -0
- package/dist/types/sdk/host/query/handlers.d.ts +3 -0
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/router/session-router.d.ts +9 -0
- package/dist/types/sdk/session-list.d.ts +1 -1
- package/dist/types/sdk/session.d.ts +8 -0
- package/dist/types/session/agent-session.d.ts +38 -9
- package/dist/types/session/artifacts.d.ts +2 -2
- package/dist/types/session/internal/managed-gc-retirement-codec.d.ts +32 -0
- package/dist/types/session/internal/managed-session-scope.d.ts +79 -2
- package/dist/types/session/internal/managed-session-storage.d.ts +36 -1
- package/dist/types/session/internal/managed-task-owner-cleanup.d.ts +62 -0
- package/dist/types/session/internal/task-artifact-owner-access.d.ts +19 -0
- package/dist/types/session/internal/task-artifact-owner-transcript.d.ts +11 -0
- package/dist/types/session/session-manager.d.ts +31 -5
- package/dist/types/session/session-retirement.d.ts +71 -0
- package/dist/types/session/session-storage.d.ts +62 -48
- package/dist/types/session/session-transcript-header.d.ts +2 -0
- package/dist/types/session/task-artifact-owner-codec.d.ts +109 -0
- package/dist/types/session/task-artifact-owner-retirement.d.ts +7 -0
- package/dist/types/session/task-artifact-owner.d.ts +10 -0
- package/dist/types/setup/credential-auto-import.d.ts +1 -1
- package/dist/types/setup/credential-import.d.ts +6 -4
- package/dist/types/skill-state/active-state.d.ts +38 -0
- package/dist/types/slash-commands/helpers/progress-report.d.ts +8 -0
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/tools/bash-skill-urls.d.ts +1 -1
- package/dist/types/tools/index.d.ts +3 -0
- package/dist/types/tools/python.d.ts +9 -1
- package/dist/types/tools/recipe/recipe-guard.d.ts +7 -0
- package/dist/types/tools/restricted-cwd.d.ts +1 -0
- package/dist/types/tools/todo-write.d.ts +6 -0
- package/dist/types/utils/tools-checksum.d.ts +7 -0
- package/package.json +7 -7
- package/scripts/generate-sdk-operation-inventory.ts +21 -0
- package/scripts/resident-memory-bench.ts +174 -20
- package/scripts/verify-managed-empty-stop.ts +28 -0
- package/src/async/job-manager.ts +228 -122
- package/src/capability/index.ts +18 -0
- package/src/capability/mcp.ts +2 -0
- package/src/capability/skill.ts +2 -0
- package/src/capability/types.ts +6 -0
- package/src/cli/auth-broker-cli.ts +1 -1
- package/src/cli/config-cli.ts +8 -18
- package/src/cli/customize-doctor.ts +15 -2
- package/src/cli/public-command-errors.ts +21 -1
- package/src/cli/setup-cli.ts +4 -4
- package/src/cli/skills-cli.ts +5 -0
- package/src/cli-main.ts +7 -8
- package/src/commands/deep-interview.ts +47 -8
- package/src/commands/sdk.ts +140 -29
- package/src/config/autorouting-tier-map.ts +117 -7
- package/src/config/file-lock.ts +307 -68
- package/src/config/mcp-schema.json +4 -0
- package/src/config/model-bindings-applier.ts +20 -0
- package/src/config/model-profile-activation.ts +240 -46
- package/src/config/model-registry.ts +47 -153
- package/src/config/settings.ts +44 -4
- package/src/config/skill-settings-defaults.ts +7 -0
- package/src/coordinator-mcp/policy.ts +3 -2
- package/src/cursor-delete-path.ts +10 -0
- package/src/cursor-mcp-name.ts +15 -0
- package/src/cursor.ts +13 -2
- package/src/customization/inventory.ts +3 -0
- package/src/debug/report-bundle-parent.ts +22 -0
- package/src/debug/report-bundle.ts +3 -0
- package/src/debug/system-info.ts +13 -1
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.ts +67 -4
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.ts +80 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +105 -10
- package/src/defaults/gjc/skills/ralplan/SKILL.md +6 -4
- package/src/defaults/gjc-defaults.test.ts +18 -1
- package/src/defaults/gjc-defaults.ts +3 -0
- package/src/defaults/gjc-skills.generated.ts +58 -36
- package/src/discovery/agents.ts +6 -1
- package/src/discovery/builtin.ts +10 -0
- package/src/discovery/helpers.ts +168 -23
- package/src/discovery/mcp-json.ts +6 -0
- package/src/discovery/ssh.ts +9 -4
- package/src/discovery/substitute-plugin-root.ts +3 -1
- package/src/edit/file-read-cache.ts +1 -1
- package/src/eval/py/executor.ts +380 -120
- package/src/export/html/css-color.ts +9 -0
- package/src/export/html/index.ts +5 -4
- package/src/export/html/template.generated.ts +1 -1
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +7 -0
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +73 -8
- package/src/extensibility/plugins/manager.ts +3 -2
- package/src/extensibility/plugins/marketplace/cache.ts +15 -0
- package/src/extensibility/plugins/marketplace/manager.ts +11 -3
- package/src/extensibility/plugins/plugin-link-path.ts +12 -0
- package/src/extensibility/runtime-skill-discovery.ts +20 -2
- package/src/extensibility/skill-management.ts +6 -2
- package/src/extensibility/skills.ts +6 -1
- package/src/gjc-runtime/deep-interview-crystallize.ts +1814 -0
- package/src/gjc-runtime/deep-interview-recorder.ts +20 -2
- package/src/gjc-runtime/deep-interview-runtime.ts +1415 -13
- package/src/gjc-runtime/deep-interview-stage.ts +98 -21
- package/src/gjc-runtime/gc-runtime.ts +206 -8
- package/src/gjc-runtime/launch-worktree.ts +14 -4
- package/src/gjc-runtime/ralplan-runtime.ts +447 -52
- package/src/gjc-runtime/state-argv.ts +2 -0
- package/src/gjc-runtime/state-migrations.ts +91 -29
- package/src/gjc-runtime/state-runtime.ts +3716 -135
- package/src/gjc-runtime/state-writer.ts +83 -8
- package/src/gjc-runtime/ultragoal-runtime.ts +16 -4
- package/src/gjc-runtime/workflow-manifest.generated.json +70 -42
- package/src/gjc-runtime/workflow-manifest.ts +54 -21
- package/src/harness-control-plane/operate.ts +12 -4
- package/src/harness-control-plane/owner.ts +5 -1
- package/src/hashline/parser.ts +48 -9
- package/src/hashline/recovery.ts +13 -4
- package/src/hindsight/config.ts +9 -3
- package/src/internal-urls/docs-index.generated.ts +19 -18
- package/src/internal-urls/skill-path.ts +19 -0
- package/src/internal-urls/skill-protocol.ts +2 -0
- package/src/lsp/edits.ts +6 -0
- package/src/lsp/workspace-path.ts +51 -0
- package/src/main.ts +195 -31
- package/src/modes/acp/acp-agent.ts +163 -7
- package/src/modes/components/custom-editor.ts +14 -0
- package/src/modes/components/customization/customization-dashboard.ts +12 -0
- package/src/modes/components/extensions/extension-dashboard.ts +2 -2
- package/src/modes/components/extensions/state-manager.ts +15 -5
- package/src/modes/components/model-selector.ts +1 -4
- package/src/modes/components/provider-onboarding-selector.ts +1 -1
- package/src/modes/components/status-line/segments.ts +4 -0
- package/src/modes/controllers/event-controller.ts +11 -4
- package/src/modes/controllers/input-controller.ts +74 -29
- package/src/modes/controllers/runtime-mcp-command-controller.ts +8 -3
- package/src/modes/controllers/selector-controller.ts +28 -15
- package/src/modes/interactive-mode.ts +4 -0
- package/src/modes/runtime-init.ts +4 -0
- package/src/modes/utils/abort-message.ts +3 -1
- package/src/modes/utils/interrupt-ownership.ts +12 -0
- package/src/modes/utils/keybinding-matchers.ts +4 -0
- package/src/modes/utils/macos-option-forwarding.ts +80 -0
- package/src/progress/collect-project-progress.ts +153 -0
- package/src/progress/progress-contract.ts +172 -0
- package/src/progress/progress-snapshot.ts +140 -0
- package/src/progress/project-progress.ts +446 -0
- package/src/progress/render-progress.ts +209 -0
- package/src/prompts/system/master-mode.md +1 -0
- package/src/prompts/tools/skill-discovery.md +1 -1
- package/src/runtime-mcp/client.ts +13 -4
- package/src/runtime-mcp/config-writer.ts +10 -6
- package/src/runtime-mcp/config.ts +1 -0
- package/src/runtime-mcp/elicitation.ts +142 -0
- package/src/runtime-mcp/oauth-flow.ts +5 -0
- package/src/runtime-mcp/oauth-public-url.ts +8 -0
- package/src/runtime-mcp/plugin-network-boundary.ts +4 -0
- package/src/runtime-mcp/smithery-registry.ts +22 -3
- package/src/runtime-mcp/transports/http.ts +2 -2
- package/src/runtime-mcp/types.ts +6 -0
- package/src/sdk/acp/adapter.ts +93 -8
- package/src/sdk/broker/broker-exit.ts +52 -23
- package/src/sdk/broker/broker.ts +83 -7
- package/src/sdk/broker/ensure.ts +2 -0
- package/src/sdk/broker/lifecycle.ts +820 -73
- package/src/sdk/broker/session-index.ts +191 -81
- package/src/sdk/broker/task-artifact-owner-validation.ts +259 -0
- package/src/sdk/bus/chat-daemon-cli.ts +38 -1
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +3 -0
- package/src/sdk/bus/index.ts +241 -46
- package/src/sdk/bus/telegram-daemon-contract.ts +6 -1
- package/src/sdk/bus/telegram-daemon.ts +4 -2
- package/src/sdk/cli/session-cli.ts +17 -3
- package/src/sdk/client/client.ts +28 -8
- package/src/sdk/host/host.ts +6 -0
- package/src/sdk/host/query/handlers.ts +9 -1
- package/src/sdk/host/session-runtime.test.ts +2805 -411
- package/src/sdk/host/session-runtime.ts +428 -111
- package/src/sdk/host/surface-policy.ts +1 -0
- package/src/sdk/index.ts +8 -0
- package/src/sdk/lifecycle-session.ts +3 -1
- package/src/sdk/mcp/server.ts +15 -3
- package/src/sdk/prompt-deadline-manager.ts +15 -0
- package/src/sdk/protocol/operation-inventory.generated.json +146 -0
- package/src/sdk/protocol/operation-registry.ts +9 -2
- package/src/sdk/router/session-router.ts +94 -14
- package/src/sdk/session-list.ts +1 -0
- package/src/sdk/session.ts +81 -38
- package/src/session/agent-session.ts +674 -224
- package/src/session/artifacts.ts +174 -70
- package/src/session/internal/managed-gc-retirement-codec.ts +319 -0
- package/src/session/internal/managed-session-scope.ts +3457 -462
- package/src/session/internal/managed-session-storage.ts +482 -61
- package/src/session/internal/managed-task-owner-cleanup.ts +473 -0
- package/src/session/internal/task-artifact-owner-access.ts +245 -0
- package/src/session/internal/task-artifact-owner-transcript.ts +614 -0
- package/src/session/session-manager.ts +939 -133
- package/src/session/session-retirement.ts +714 -0
- package/src/session/session-storage.ts +418 -174
- package/src/session/session-transcript-header.ts +17 -0
- package/src/session/task-artifact-owner-codec.ts +641 -0
- package/src/session/task-artifact-owner-retirement.ts +477 -0
- package/src/session/task-artifact-owner.ts +289 -0
- package/src/setup/credential-auto-import.ts +12 -3
- package/src/setup/credential-import.ts +176 -5
- package/src/setup/paseo/announce.ts +6 -1
- package/src/skill-state/active-state.ts +132 -2
- package/src/slash-commands/builtin-registry.ts +39 -9
- package/src/slash-commands/helpers/progress-report.ts +23 -0
- package/src/stt/setup.ts +3 -5
- package/src/task/commands.ts +3 -1
- package/src/task/executor.ts +4 -0
- package/src/task/index.ts +6 -0
- package/src/tools/ask.ts +205 -6
- package/src/tools/ast-edit.ts +4 -0
- package/src/tools/bash-allowed-prefixes.ts +15 -0
- package/src/tools/bash-skill-urls.ts +56 -3
- package/src/tools/bash.ts +48 -79
- package/src/tools/descriptors.ts +5 -1
- package/src/tools/eval.ts +56 -36
- package/src/tools/image-gen.ts +1 -1
- package/src/tools/index.ts +3 -0
- package/src/tools/python.ts +187 -66
- package/src/tools/recipe/index.ts +9 -0
- package/src/tools/recipe/recipe-guard.ts +19 -0
- package/src/tools/restricted-cwd.ts +10 -0
- package/src/tools/skill-discovery.ts +5 -0
- package/src/tools/skill.ts +28 -1
- package/src/tools/todo-write.ts +10 -1
- package/src/tools/tool-catalog.generated.ts +1 -1
- package/src/tools/write.ts +1 -0
- package/src/utils/tools-checksum.ts +34 -0
- package/src/utils/tools-manager.ts +24 -6
- package/src/web/search/providers/codex.ts +3 -2
package/src/tools/ask.ts
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
* - Questions may time out and auto-select the recommended option (configurable, disabled in plan mode)
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
import { randomUUID } from "node:crypto";
|
|
18
19
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@gajae-code/agent-core";
|
|
19
20
|
import type { RawArgumentValidationResult } from "@gajae-code/ai/types";
|
|
20
21
|
import { validateToolArguments } from "@gajae-code/ai/utils/validation";
|
|
@@ -36,12 +37,25 @@ import {
|
|
|
36
37
|
} from "../deep-interview/render-middleware";
|
|
37
38
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
38
39
|
import { appendOrMergeDeepInterviewRound, syncDeepInterviewRecorderHud } from "../gjc-runtime/deep-interview-recorder";
|
|
39
|
-
import {
|
|
40
|
+
import {
|
|
41
|
+
assertDeepInterviewCrystalCoversLiveTranscript,
|
|
42
|
+
authoritativeConversationSnapshot,
|
|
43
|
+
deepInterviewStatePath,
|
|
44
|
+
} from "../gjc-runtime/deep-interview-runtime";
|
|
40
45
|
import {
|
|
41
46
|
assertDeepInterviewInputWithinLimit,
|
|
42
47
|
assertDeepInterviewStructuredResponseWithinLimit,
|
|
43
48
|
MAX_USER_RESPONSE_LENGTH,
|
|
44
49
|
} from "../gjc-runtime/deep-interview-state";
|
|
50
|
+
import type { ExecutionApprovalPresentation } from "../gjc-runtime/state-runtime";
|
|
51
|
+
import {
|
|
52
|
+
captureExecutionApprovalPresentation,
|
|
53
|
+
executionApprovalLineage,
|
|
54
|
+
recordDeepInterviewExecutionApproval,
|
|
55
|
+
recordNonCrystalExecutionApproval,
|
|
56
|
+
revokeDeepInterviewExecutionApproval,
|
|
57
|
+
revokeNonCrystalExecutionApproval,
|
|
58
|
+
} from "../gjc-runtime/state-runtime";
|
|
45
59
|
import {
|
|
46
60
|
type AskGateQuestion,
|
|
47
61
|
gateAnswerToResult,
|
|
@@ -75,7 +89,7 @@ import {
|
|
|
75
89
|
export { askSchema } from "./ask-contract";
|
|
76
90
|
|
|
77
91
|
import { formatErrorMessage, formatMeta, formatTitle } from "./render-utils";
|
|
78
|
-
import { ToolAbortError } from "./tool-errors";
|
|
92
|
+
import { ToolAbortError, ToolError } from "./tool-errors";
|
|
79
93
|
import { assertUltragoalAskAllowed } from "./ultragoal-ask-guard";
|
|
80
94
|
|
|
81
95
|
// =============================================================================
|
|
@@ -158,6 +172,23 @@ function nonEmptyCustomInput(value: string | undefined): string | undefined {
|
|
|
158
172
|
return value !== undefined && value.trim().length > 0 ? value : undefined;
|
|
159
173
|
}
|
|
160
174
|
|
|
175
|
+
function deepInterviewExecutionTarget(selectedOptions: readonly string[]): "ultragoal" | undefined {
|
|
176
|
+
if (selectedOptions.length !== 1) return undefined;
|
|
177
|
+
const normalized = selectedOptions[0]
|
|
178
|
+
?.trim()
|
|
179
|
+
.replace(/\s*\(Recommended\)\s*$/i, "")
|
|
180
|
+
.toLowerCase();
|
|
181
|
+
return normalized === "approve execution via ultragoal" ||
|
|
182
|
+
/^execute with ultragoal(?: \(only when spec is already implementation-ready and really simple\))?$/.test(
|
|
183
|
+
normalized ?? "",
|
|
184
|
+
) ||
|
|
185
|
+
normalized === "ultragoal" ||
|
|
186
|
+
normalized === "/skill:ultragoal" ||
|
|
187
|
+
normalized === "gjc ultragoal"
|
|
188
|
+
? "ultragoal"
|
|
189
|
+
: undefined;
|
|
190
|
+
}
|
|
191
|
+
|
|
161
192
|
function isAskTimeoutError(error: unknown): boolean {
|
|
162
193
|
return typeof error === "object" && error !== null && (error as { code?: unknown }).code === GJC_ASK_TIMEOUT_CODE;
|
|
163
194
|
}
|
|
@@ -320,6 +351,8 @@ interface SelectionResult {
|
|
|
320
351
|
selectedOptions: string[];
|
|
321
352
|
customInput?: string;
|
|
322
353
|
clarificationQuestion?: string;
|
|
354
|
+
executionGateId?: string;
|
|
355
|
+
executionPresentation?: ExecutionApprovalPresentation;
|
|
323
356
|
timedOut: boolean;
|
|
324
357
|
navigation?: "back" | "forward";
|
|
325
358
|
cancelled?: boolean;
|
|
@@ -340,6 +373,7 @@ interface AskSingleQuestionOptions {
|
|
|
340
373
|
otherOptionLabel?: string;
|
|
341
374
|
clarificationOptionLabel?: string;
|
|
342
375
|
autoSelectOnTimeout?: boolean;
|
|
376
|
+
executionPresentation?: ExecutionApprovalPresentation;
|
|
343
377
|
onRemoteState?: (state: {
|
|
344
378
|
interaction: "selector" | "custom_editor" | "clarification_editor";
|
|
345
379
|
selectedCount: number;
|
|
@@ -849,6 +883,65 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
849
883
|
);
|
|
850
884
|
}
|
|
851
885
|
|
|
886
|
+
/**
|
|
887
|
+
* The Deep Interview execution choice is the only ask answer that can mint
|
|
888
|
+
* execution authority. The runtime records only the redacted answer hash;
|
|
889
|
+
* the subsequent CLI action must consume that record before it can approve
|
|
890
|
+
* the canonical Crystal.
|
|
891
|
+
*/
|
|
892
|
+
async #recordDeepInterviewExecutionApproval(
|
|
893
|
+
q: AskParams["questions"][number],
|
|
894
|
+
selectedOptions: string[],
|
|
895
|
+
customInput: string | undefined,
|
|
896
|
+
toolCallId: string,
|
|
897
|
+
executionGateId?: string,
|
|
898
|
+
executionPresentation?: ExecutionApprovalPresentation,
|
|
899
|
+
): Promise<void> {
|
|
900
|
+
const deepInterviewExecution = q.workflowGate?.stage === "deep-interview" && q.workflowGate.kind === "execution";
|
|
901
|
+
const ralplanApproval = q.workflowGate?.stage === "ralplan" && q.workflowGate.kind === "approval";
|
|
902
|
+
if (!deepInterviewExecution && !ralplanApproval) return;
|
|
903
|
+
const target = deepInterviewExecutionTarget(selectedOptions);
|
|
904
|
+
const sessionId = this.session.getSessionId?.();
|
|
905
|
+
if (!target || customInput !== undefined) {
|
|
906
|
+
if (sessionId) {
|
|
907
|
+
await revokeDeepInterviewExecutionApproval(this.session.cwd, sessionId);
|
|
908
|
+
await revokeNonCrystalExecutionApproval(
|
|
909
|
+
this.session.cwd,
|
|
910
|
+
sessionId,
|
|
911
|
+
ralplanApproval ? "ralplan" : "deep-interview",
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
if (!sessionId) throw new ToolAbortError("Deep Interview execution approval requires a session");
|
|
917
|
+
if (!executionPresentation)
|
|
918
|
+
throw new ToolAbortError("Execution approval publication could not be authenticated before consent");
|
|
919
|
+
const approvalStage = ralplanApproval ? "ralplan" : "deep-interview";
|
|
920
|
+
const lineage = await executionApprovalLineage(this.session.cwd, sessionId, approvalStage);
|
|
921
|
+
const transcriptEvidence =
|
|
922
|
+
lineage === "crystal"
|
|
923
|
+
? await assertDeepInterviewCrystalCoversLiveTranscript(this.session.cwd, sessionId, deepInterviewExecution)
|
|
924
|
+
: await authoritativeConversationSnapshot(this.session.cwd, sessionId);
|
|
925
|
+
const recordApproval =
|
|
926
|
+
lineage === "crystal" ? recordDeepInterviewExecutionApproval : recordNonCrystalExecutionApproval;
|
|
927
|
+
await recordApproval({
|
|
928
|
+
cwd: this.session.cwd,
|
|
929
|
+
sessionId,
|
|
930
|
+
questionId: q.id,
|
|
931
|
+
gateId: executionGateId ?? q.id,
|
|
932
|
+
toolCallId,
|
|
933
|
+
target,
|
|
934
|
+
selectedOptions,
|
|
935
|
+
transcriptPath: transcriptEvidence.transcriptPath,
|
|
936
|
+
transcriptSha256: transcriptEvidence.transcriptSha256,
|
|
937
|
+
approvalStage,
|
|
938
|
+
presentation: executionPresentation,
|
|
939
|
+
});
|
|
940
|
+
// Ralplan approval is consumed by the subsequent handoff, after the agent
|
|
941
|
+
// persists this Ask tool result. Consuming here would validate the boundary
|
|
942
|
+
// before the matching provider toolResult exists in the transcript.
|
|
943
|
+
}
|
|
944
|
+
|
|
852
945
|
async execute(
|
|
853
946
|
_toolCallId: string,
|
|
854
947
|
params: AskParams,
|
|
@@ -1200,6 +1293,14 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
1200
1293
|
// Send notification if waiting and not suppressed
|
|
1201
1294
|
this.#sendAskNotification();
|
|
1202
1295
|
|
|
1296
|
+
if (
|
|
1297
|
+
params.questions.filter(
|
|
1298
|
+
question =>
|
|
1299
|
+
(question.workflowGate?.stage === "deep-interview" && question.workflowGate.kind === "execution") ||
|
|
1300
|
+
(question.workflowGate?.stage === "ralplan" && question.workflowGate.kind === "approval"),
|
|
1301
|
+
).length > 1
|
|
1302
|
+
)
|
|
1303
|
+
throw new ToolError("Ask accepts at most one execution-authorizing workflow gate per invocation");
|
|
1203
1304
|
const askQuestion = async (
|
|
1204
1305
|
q: AskParams["questions"][number],
|
|
1205
1306
|
options?: {
|
|
@@ -1212,6 +1313,9 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
1212
1313
|
const recommended = validRecommendedIndex(q.recommended, rawOptionLabels.length);
|
|
1213
1314
|
const questionIndex = params.questions.indexOf(q);
|
|
1214
1315
|
const emptyCustomAttempt = options?.emptyCustomAttempt ?? 0;
|
|
1316
|
+
const isExecutionApprovalQuestion =
|
|
1317
|
+
(q.workflowGate?.stage === "deep-interview" && q.workflowGate.kind === "execution") ||
|
|
1318
|
+
(q.workflowGate?.stage === "ralplan" && q.workflowGate.kind === "approval");
|
|
1215
1319
|
// Route headless asks through the SDK workflow-gate emitter; a connected
|
|
1216
1320
|
// SDK responder supplies the durable answer instead of an interactive UI.
|
|
1217
1321
|
if (gateEmitter && canUseWorkflowGate) {
|
|
@@ -1226,19 +1330,79 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
1226
1330
|
allowEmpty: q.multi === true && params.questions.length > 1,
|
|
1227
1331
|
navigationLabel: questionIndex === params.questions.length - 1 ? "Done" : "Next",
|
|
1228
1332
|
};
|
|
1229
|
-
|
|
1333
|
+
let executionGateId: string | undefined = isExecutionApprovalQuestion
|
|
1334
|
+
? `interactive-${randomUUID()}`
|
|
1335
|
+
: undefined;
|
|
1336
|
+
let executionPresentation: ExecutionApprovalPresentation | undefined;
|
|
1337
|
+
if (isExecutionApprovalQuestion) {
|
|
1338
|
+
const sessionId = this.session.getSessionId?.();
|
|
1339
|
+
if (!sessionId) throw new ToolAbortError("Execution approval requires a session");
|
|
1340
|
+
try {
|
|
1341
|
+
executionPresentation = await captureExecutionApprovalPresentation(
|
|
1342
|
+
this.session.cwd,
|
|
1343
|
+
sessionId,
|
|
1344
|
+
q.workflowGate?.stage === "ralplan" ? "ralplan" : "deep-interview",
|
|
1345
|
+
);
|
|
1346
|
+
} catch (error) {
|
|
1347
|
+
throw new ToolAbortError(
|
|
1348
|
+
error instanceof Error
|
|
1349
|
+
? `Execution approval publication could not be authenticated: ${error.message}`
|
|
1350
|
+
: "Execution approval publication could not be authenticated",
|
|
1351
|
+
);
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
const stopGateObservation = gateEmitter.onGateEmitted?.(gate => {
|
|
1355
|
+
const stageState = gate.context?.stage_state;
|
|
1356
|
+
if (
|
|
1357
|
+
gate.stage === q.workflowGate?.stage &&
|
|
1358
|
+
gate.kind === q.workflowGate?.kind &&
|
|
1359
|
+
((gate.stage === "deep-interview" && gate.kind === "execution") ||
|
|
1360
|
+
(gate.stage === "ralplan" && gate.kind === "approval")) &&
|
|
1361
|
+
stageState?.question_id === q.id
|
|
1362
|
+
)
|
|
1363
|
+
executionGateId = gate.gate_id;
|
|
1364
|
+
});
|
|
1365
|
+
let answer: unknown;
|
|
1366
|
+
try {
|
|
1367
|
+
answer = await gateEmitter.emitGate(questionToGate(gateQuestion));
|
|
1368
|
+
} finally {
|
|
1369
|
+
stopGateObservation?.();
|
|
1370
|
+
}
|
|
1230
1371
|
const decoded = gateAnswerToResult(gateQuestion, answer);
|
|
1231
1372
|
return {
|
|
1232
1373
|
optionLabels: rawOptionLabels,
|
|
1233
1374
|
selectedOptions: decoded.selectedOptions,
|
|
1234
1375
|
customInput: decoded.customInput,
|
|
1235
1376
|
clarificationQuestion: decoded.clarificationQuestion,
|
|
1377
|
+
executionGateId,
|
|
1378
|
+
executionPresentation,
|
|
1236
1379
|
navigation: undefined as NavigationControls | undefined,
|
|
1237
1380
|
cancelled: false,
|
|
1238
1381
|
timedOut: false,
|
|
1239
1382
|
};
|
|
1240
1383
|
}
|
|
1241
1384
|
try {
|
|
1385
|
+
let executionPresentation: ExecutionApprovalPresentation | undefined;
|
|
1386
|
+
if (isExecutionApprovalQuestion) {
|
|
1387
|
+
const sessionId = this.session.getSessionId?.();
|
|
1388
|
+
if (!sessionId) throw new ToolAbortError("Execution approval requires a session");
|
|
1389
|
+
try {
|
|
1390
|
+
executionPresentation = await captureExecutionApprovalPresentation(
|
|
1391
|
+
this.session.cwd,
|
|
1392
|
+
sessionId,
|
|
1393
|
+
q.workflowGate?.stage === "ralplan" ? "ralplan" : "deep-interview",
|
|
1394
|
+
);
|
|
1395
|
+
} catch (error) {
|
|
1396
|
+
throw new ToolAbortError(
|
|
1397
|
+
error instanceof Error
|
|
1398
|
+
? `Execution approval publication could not be authenticated: ${error.message}`
|
|
1399
|
+
: "Execution approval publication could not be authenticated",
|
|
1400
|
+
);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
const executionGateId: string | undefined = isExecutionApprovalQuestion
|
|
1404
|
+
? `interactive-${randomUUID()}`
|
|
1405
|
+
: undefined;
|
|
1242
1406
|
const deepInterviewPrompt = formatDeepInterviewSelectorPrompt(q.question);
|
|
1243
1407
|
const isDeepInterviewQuestion = deepInterviewPrompt !== null || q.deepInterview !== undefined;
|
|
1244
1408
|
const baseDisplayQuestion = deepInterviewPrompt ?? q.question;
|
|
@@ -1307,6 +1471,7 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
1307
1471
|
!intentReview(q.deepInterview) &&
|
|
1308
1472
|
(q.workflowGate === undefined || q.workflowGate.kind === "question"),
|
|
1309
1473
|
clarificationOptionLabel,
|
|
1474
|
+
executionPresentation,
|
|
1310
1475
|
onRemoteState: state => {
|
|
1311
1476
|
activeRemoteRequest = {
|
|
1312
1477
|
question: displayQuestion,
|
|
@@ -1395,6 +1560,8 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
1395
1560
|
selectedOptions,
|
|
1396
1561
|
customInput,
|
|
1397
1562
|
clarificationQuestion,
|
|
1563
|
+
executionGateId,
|
|
1564
|
+
executionPresentation,
|
|
1398
1565
|
navigation,
|
|
1399
1566
|
cancelled,
|
|
1400
1567
|
timedOut,
|
|
@@ -1418,8 +1585,16 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
1418
1585
|
|
|
1419
1586
|
if (params.questions.length === 1) {
|
|
1420
1587
|
const [q] = params.questions;
|
|
1421
|
-
const {
|
|
1422
|
-
|
|
1588
|
+
const {
|
|
1589
|
+
optionLabels,
|
|
1590
|
+
selectedOptions,
|
|
1591
|
+
customInput,
|
|
1592
|
+
clarificationQuestion,
|
|
1593
|
+
executionGateId,
|
|
1594
|
+
executionPresentation,
|
|
1595
|
+
cancelled,
|
|
1596
|
+
timedOut,
|
|
1597
|
+
} = await askQuestion(q);
|
|
1423
1598
|
|
|
1424
1599
|
if (
|
|
1425
1600
|
!timedOut &&
|
|
@@ -1435,6 +1610,14 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
1435
1610
|
) {
|
|
1436
1611
|
await this.#recordDeepInterviewRound(q, selectedOptions, customInput);
|
|
1437
1612
|
}
|
|
1613
|
+
await this.#recordDeepInterviewExecutionApproval(
|
|
1614
|
+
q,
|
|
1615
|
+
selectedOptions,
|
|
1616
|
+
customInput,
|
|
1617
|
+
_toolCallId,
|
|
1618
|
+
executionGateId,
|
|
1619
|
+
executionPresentation,
|
|
1620
|
+
);
|
|
1438
1621
|
const details: AskToolDetails = {
|
|
1439
1622
|
question: q.question,
|
|
1440
1623
|
options: optionLabels,
|
|
@@ -1476,6 +1659,10 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
1476
1659
|
}
|
|
1477
1660
|
|
|
1478
1661
|
const resultsByIndex: Array<QuestionResult | undefined> = Array.from({ length: params.questions.length });
|
|
1662
|
+
const executionGateIdsByIndex: Array<string | undefined> = Array.from({ length: params.questions.length });
|
|
1663
|
+
const executionPresentationsByIndex: Array<ExecutionApprovalPresentation | undefined> = Array.from({
|
|
1664
|
+
length: params.questions.length,
|
|
1665
|
+
});
|
|
1479
1666
|
let questionIndex = 0;
|
|
1480
1667
|
while (questionIndex < params.questions.length) {
|
|
1481
1668
|
const q = params.questions[questionIndex]!;
|
|
@@ -1490,6 +1677,8 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
1490
1677
|
selectedOptions,
|
|
1491
1678
|
customInput,
|
|
1492
1679
|
clarificationQuestion,
|
|
1680
|
+
executionGateId,
|
|
1681
|
+
executionPresentation,
|
|
1493
1682
|
navigation: navAction,
|
|
1494
1683
|
cancelled,
|
|
1495
1684
|
timedOut,
|
|
@@ -1509,6 +1698,8 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
1509
1698
|
customInput,
|
|
1510
1699
|
clarificationQuestion,
|
|
1511
1700
|
};
|
|
1701
|
+
executionGateIdsByIndex[questionIndex] = executionGateId;
|
|
1702
|
+
executionPresentationsByIndex[questionIndex] = executionPresentation;
|
|
1512
1703
|
|
|
1513
1704
|
if (
|
|
1514
1705
|
clarificationQuestion === undefined &&
|
|
@@ -1521,7 +1712,6 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
1521
1712
|
questionIndex = Math.max(0, questionIndex - 1);
|
|
1522
1713
|
continue;
|
|
1523
1714
|
}
|
|
1524
|
-
|
|
1525
1715
|
questionIndex += 1;
|
|
1526
1716
|
}
|
|
1527
1717
|
|
|
@@ -1538,6 +1728,15 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
|
1538
1728
|
});
|
|
1539
1729
|
|
|
1540
1730
|
const details: AskToolDetails = { results };
|
|
1731
|
+
for (const [index, result] of results.entries())
|
|
1732
|
+
await this.#recordDeepInterviewExecutionApproval(
|
|
1733
|
+
params.questions[index]!,
|
|
1734
|
+
result.selectedOptions,
|
|
1735
|
+
result.customInput,
|
|
1736
|
+
_toolCallId,
|
|
1737
|
+
executionGateIdsByIndex[index],
|
|
1738
|
+
executionPresentationsByIndex[index],
|
|
1739
|
+
);
|
|
1541
1740
|
const responseLines = results.map(formatQuestionResult);
|
|
1542
1741
|
const responseText = `User answers:\n${responseLines.join("\n")}`;
|
|
1543
1742
|
|
package/src/tools/ast-edit.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { createFileRecorder, formatResultPath } from "./file-recorder";
|
|
|
17
17
|
import { formatGroupedFiles } from "./grouped-file-output";
|
|
18
18
|
import type { OutputMeta } from "./output-meta";
|
|
19
19
|
import { resolveToolSearchScope } from "./path-utils";
|
|
20
|
+
import { enforcePlanModeWrite } from "./plan-mode-guard";
|
|
20
21
|
import {
|
|
21
22
|
appendParseErrorsBulletList,
|
|
22
23
|
capParseErrors,
|
|
@@ -337,6 +338,9 @@ export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolD
|
|
|
337
338
|
label: `AST Edit: ${result.totalReplacements} replacement${previewReplacementPlural} in ${result.filesTouched} file${previewFilePlural}`,
|
|
338
339
|
sourceToolName: this.name,
|
|
339
340
|
apply: async (_reason: string) => {
|
|
341
|
+
for (const filePath of previewedFiles) {
|
|
342
|
+
enforcePlanModeWrite(this.session, filePath, { op: "update" });
|
|
343
|
+
}
|
|
340
344
|
await assertWorkflowMutationRawPathsAllowed({
|
|
341
345
|
cwd: this.session.cwd,
|
|
342
346
|
sessionId: this.session.getSessionId?.() ?? undefined,
|
|
@@ -268,10 +268,25 @@ function validateReadOnlyCommand(words: readonly string[]): BashAllowedPrefixesC
|
|
|
268
268
|
}
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
+
const gitWrite = rejectGitHostAccess(words);
|
|
272
|
+
if (gitWrite) return gitWrite;
|
|
273
|
+
|
|
271
274
|
return { allowed: true };
|
|
272
275
|
}
|
|
273
276
|
|
|
277
|
+
function rejectGitHostAccess(words: readonly string[]): BashAllowedPrefixesCheck | undefined {
|
|
278
|
+
if (words[0] !== "git") return undefined;
|
|
279
|
+
for (const option of optionWords(words)) {
|
|
280
|
+
if (isLongOption(option, "--output") || isLongOption(option, "--no-index")) {
|
|
281
|
+
return { allowed: false, reason: "restricted bash does not allow git --output or --no-index" };
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
|
|
274
287
|
function validateMatchedGjcCommand(words: readonly string[]): BashAllowedPrefixesCheck {
|
|
288
|
+
const gitWrite = rejectGitHostAccess(words);
|
|
289
|
+
if (gitWrite) return gitWrite;
|
|
275
290
|
if (words[0] !== "gjc") return { allowed: true };
|
|
276
291
|
|
|
277
292
|
if (words[1] === "ralplan") {
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
+
import { isEnoent } from "@gajae-code/utils";
|
|
3
4
|
import type { Skill } from "../extensibility/skills";
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
initializeLocalRoot,
|
|
7
|
+
type LocalProtocolOptions,
|
|
8
|
+
resolveLocalRoot,
|
|
9
|
+
resolveLocalUrlToPath,
|
|
10
|
+
} from "../internal-urls/local-protocol";
|
|
5
11
|
import { validateRelativePath } from "../internal-urls/skill-protocol";
|
|
6
12
|
import type { InternalResource } from "../internal-urls/types";
|
|
7
13
|
import { normalizeLocalScheme } from "./path-utils";
|
|
@@ -125,6 +131,52 @@ function matchSkillName(
|
|
|
125
131
|
return { skill: undefined, suffix: undefined };
|
|
126
132
|
}
|
|
127
133
|
|
|
134
|
+
function localPathEscapes(realRoot: string, candidate: string): boolean {
|
|
135
|
+
return candidate !== realRoot && !candidate.startsWith(`${realRoot}${path.sep}`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function canonicalLocalPath(localRoot: string, lexicalPath: string): Promise<string> {
|
|
139
|
+
const realRoot = await fs.realpath(localRoot);
|
|
140
|
+
try {
|
|
141
|
+
const realTarget = await fs.realpath(lexicalPath);
|
|
142
|
+
if (localPathEscapes(realRoot, realTarget)) {
|
|
143
|
+
throw new ToolError("local:// path escapes the session local root");
|
|
144
|
+
}
|
|
145
|
+
return realTarget;
|
|
146
|
+
} catch (error) {
|
|
147
|
+
if (error instanceof ToolError || !isEnoent(error)) throw error;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const missing: string[] = [];
|
|
151
|
+
let cursor = lexicalPath;
|
|
152
|
+
for (;;) {
|
|
153
|
+
let stat: Awaited<ReturnType<typeof fs.lstat>> | undefined;
|
|
154
|
+
try {
|
|
155
|
+
stat = await fs.lstat(cursor);
|
|
156
|
+
} catch (error) {
|
|
157
|
+
if (!isEnoent(error)) throw error;
|
|
158
|
+
}
|
|
159
|
+
if (stat) {
|
|
160
|
+
let realAncestor: string;
|
|
161
|
+
try {
|
|
162
|
+
realAncestor = await fs.realpath(cursor);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
if (isEnoent(error)) throw new ToolError("local:// path escapes the session local root");
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
const canonical = path.join(realAncestor, ...missing);
|
|
168
|
+
if (localPathEscapes(realRoot, canonical)) {
|
|
169
|
+
throw new ToolError("local:// path escapes the session local root");
|
|
170
|
+
}
|
|
171
|
+
return canonical;
|
|
172
|
+
}
|
|
173
|
+
const parent = path.dirname(cursor);
|
|
174
|
+
if (parent === cursor) throw new ToolError("local:// path escapes the session local root");
|
|
175
|
+
missing.unshift(path.basename(cursor));
|
|
176
|
+
cursor = parent;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
128
180
|
function extractScheme(url: string): SupportedInternalScheme | undefined {
|
|
129
181
|
const match = /^([a-z][a-z0-9+.-]*):\/\//i.exec(url);
|
|
130
182
|
if (!match) return undefined;
|
|
@@ -180,10 +232,11 @@ async function resolveInternalUrlToPath(
|
|
|
180
232
|
}
|
|
181
233
|
await initializeLocalRoot(localOptions);
|
|
182
234
|
const resolvedLocalPath = resolveLocalUrlToPath(url, localOptions);
|
|
235
|
+
const realTarget = await canonicalLocalPath(resolveLocalRoot(localOptions), resolvedLocalPath);
|
|
183
236
|
if (ensureLocalParentDirs) {
|
|
184
|
-
await fs.mkdir(path.dirname(
|
|
237
|
+
await fs.mkdir(path.dirname(realTarget), { recursive: true });
|
|
185
238
|
}
|
|
186
|
-
return
|
|
239
|
+
return realTarget;
|
|
187
240
|
}
|
|
188
241
|
|
|
189
242
|
if (!internalRouter?.canHandle(url)) {
|