@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
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
|
|
4
|
+
export async function realSkillPath(baseDir: string, targetPath: string): Promise<string> {
|
|
5
|
+
const realBase = await fs.realpath(baseDir);
|
|
6
|
+
let realTarget: string;
|
|
7
|
+
try {
|
|
8
|
+
realTarget = await fs.realpath(targetPath);
|
|
9
|
+
} catch (error) {
|
|
10
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
11
|
+
throw new Error(`File not found: ${targetPath}`);
|
|
12
|
+
}
|
|
13
|
+
throw error;
|
|
14
|
+
}
|
|
15
|
+
if (realTarget !== realBase && !realTarget.startsWith(realBase + path.sep)) {
|
|
16
|
+
throw new Error("Path traversal is not allowed");
|
|
17
|
+
}
|
|
18
|
+
return realTarget;
|
|
19
|
+
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import * as path from "node:path";
|
|
11
11
|
import { getActiveSkills } from "../extensibility/skills";
|
|
12
|
+
import { realSkillPath } from "./skill-path";
|
|
12
13
|
import type { InternalResource, InternalUrl, ProtocolHandler } from "./types";
|
|
13
14
|
|
|
14
15
|
function getContentType(filePath: string): InternalResource["contentType"] {
|
|
@@ -70,6 +71,7 @@ export class SkillProtocolHandler implements ProtocolHandler {
|
|
|
70
71
|
if (!resolvedPath.startsWith(resolvedBaseDir + path.sep) && resolvedPath !== resolvedBaseDir) {
|
|
71
72
|
throw new Error("Path traversal is not allowed");
|
|
72
73
|
}
|
|
74
|
+
targetPath = await realSkillPath(skill.baseDir, targetPath);
|
|
73
75
|
} else {
|
|
74
76
|
targetPath = skill.filePath;
|
|
75
77
|
}
|
package/src/lsp/edits.ts
CHANGED
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
WorkspaceEdit,
|
|
14
14
|
} from "./types";
|
|
15
15
|
import { uriToFile } from "./utils";
|
|
16
|
+
import { assertInsideWorkspace } from "./workspace-path";
|
|
16
17
|
|
|
17
18
|
// =============================================================================
|
|
18
19
|
// Text Edit Application
|
|
@@ -133,6 +134,7 @@ export async function applyWorkspaceEdit(edit: WorkspaceEdit, cwd: string): Prom
|
|
|
133
134
|
const textEditsByUri = flattenWorkspaceTextEdits(edit);
|
|
134
135
|
for (const [uri, textEdits] of textEditsByUri) {
|
|
135
136
|
const filePath = uriToFile(uri);
|
|
137
|
+
await assertInsideWorkspace(cwd, filePath);
|
|
136
138
|
await applyTextEdits(filePath, textEdits);
|
|
137
139
|
applied.push(`Applied ${textEdits.length} edit(s) to ${formatPathRelativeToCwd(filePath, cwd)}`);
|
|
138
140
|
}
|
|
@@ -144,18 +146,22 @@ export async function applyWorkspaceEdit(edit: WorkspaceEdit, cwd: string): Prom
|
|
|
144
146
|
if (change.kind === "create") {
|
|
145
147
|
const createOp = change as CreateFile;
|
|
146
148
|
const filePath = uriToFile(createOp.uri);
|
|
149
|
+
await assertInsideWorkspace(cwd, filePath);
|
|
147
150
|
await Bun.write(filePath, "");
|
|
148
151
|
applied.push(`Created ${formatPathRelativeToCwd(filePath, cwd)}`);
|
|
149
152
|
} else if (change.kind === "rename") {
|
|
150
153
|
const renameOp = change as RenameFile;
|
|
151
154
|
const oldPath = uriToFile(renameOp.oldUri);
|
|
152
155
|
const newPath = uriToFile(renameOp.newUri);
|
|
156
|
+
await assertInsideWorkspace(cwd, oldPath);
|
|
157
|
+
await assertInsideWorkspace(cwd, newPath);
|
|
153
158
|
await fs.mkdir(path.dirname(newPath), { recursive: true });
|
|
154
159
|
await fs.rename(oldPath, newPath);
|
|
155
160
|
applied.push(`Renamed ${formatPathRelativeToCwd(oldPath, cwd)} → ${formatPathRelativeToCwd(newPath, cwd)}`);
|
|
156
161
|
} else if (change.kind === "delete") {
|
|
157
162
|
const deleteOp = change as DeleteFile;
|
|
158
163
|
const filePath = uriToFile(deleteOp.uri);
|
|
164
|
+
await assertInsideWorkspace(cwd, filePath);
|
|
159
165
|
await fs.rm(filePath, { recursive: true });
|
|
160
166
|
applied.push(`Deleted ${formatPathRelativeToCwd(filePath, cwd)}`);
|
|
161
167
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { ToolError } from "../tools/tool-errors";
|
|
4
|
+
|
|
5
|
+
function isEnoent(error: unknown): boolean {
|
|
6
|
+
return (error as NodeJS.ErrnoException).code === "ENOENT";
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function escapes(root: string, candidate: string): boolean {
|
|
10
|
+
return candidate !== root && !candidate.startsWith(`${root}${path.sep}`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Resolve a missing path through the nearest existing ancestor. */
|
|
14
|
+
async function canonicalize(filePath: string): Promise<string> {
|
|
15
|
+
try {
|
|
16
|
+
return await fs.realpath(filePath);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
if (!isEnoent(error)) throw error;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const missing: string[] = [];
|
|
22
|
+
let cursor = filePath;
|
|
23
|
+
for (;;) {
|
|
24
|
+
let stat: Awaited<ReturnType<typeof fs.lstat>> | undefined;
|
|
25
|
+
try {
|
|
26
|
+
stat = await fs.lstat(cursor);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (!isEnoent(error)) throw error;
|
|
29
|
+
}
|
|
30
|
+
if (stat) {
|
|
31
|
+
try {
|
|
32
|
+
return path.join(await fs.realpath(cursor), ...missing);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
if (isEnoent(error)) throw new ToolError(`LSP edit escapes the workspace: ${filePath}`);
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const parent = path.dirname(cursor);
|
|
39
|
+
if (parent === cursor) throw new ToolError(`LSP edit escapes the workspace: ${filePath}`);
|
|
40
|
+
missing.unshift(path.basename(cursor));
|
|
41
|
+
cursor = parent;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function assertInsideWorkspace(cwd: string, filePath: string): Promise<void> {
|
|
46
|
+
const root = await fs.realpath(cwd);
|
|
47
|
+
const candidate = await canonicalize(filePath);
|
|
48
|
+
if (escapes(root, candidate)) {
|
|
49
|
+
throw new ToolError(`LSP edit escapes the workspace: ${filePath}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
package/src/main.ts
CHANGED
|
@@ -475,6 +475,11 @@ type StartupModelProfileArgs = {
|
|
|
475
475
|
preferCachedDefaultProfile?: boolean;
|
|
476
476
|
};
|
|
477
477
|
|
|
478
|
+
type StartupModelProfileFailure = {
|
|
479
|
+
profileName: string;
|
|
480
|
+
error: ModelProfileCredentialError | UnknownModelProfileError;
|
|
481
|
+
};
|
|
482
|
+
|
|
478
483
|
function staleDefaultProfileMessage(error: UnknownModelProfileError, profileCatalogRefreshUnavailable = false): string {
|
|
479
484
|
const refreshNotice = profileCatalogRefreshUnavailable
|
|
480
485
|
? " The online profile catalog could not be refreshed; continuing with the last accepted catalog."
|
|
@@ -488,6 +493,8 @@ async function applyStartupModelProfilesWithPolicy(
|
|
|
488
493
|
onUnknownDefault?: (error: UnknownModelProfileError, profileCatalogRefreshUnavailable: boolean) => void,
|
|
489
494
|
): Promise<void> {
|
|
490
495
|
let profileCatalogRefreshUnavailable = false;
|
|
496
|
+
let profilePassFailures: StartupModelProfileFailure[] = [];
|
|
497
|
+
let unhandledProfileName: string | undefined;
|
|
491
498
|
const applyProfile = async (
|
|
492
499
|
profileName: string,
|
|
493
500
|
persistDefault: boolean,
|
|
@@ -496,37 +503,66 @@ async function applyStartupModelProfilesWithPolicy(
|
|
|
496
503
|
tolerateCredentialError?: boolean;
|
|
497
504
|
tolerateUnknownDefault?: boolean;
|
|
498
505
|
runtimeBindingsOnly?: boolean;
|
|
506
|
+
userSelectionRevision?: number;
|
|
499
507
|
} = {},
|
|
500
508
|
): Promise<boolean> => {
|
|
501
|
-
|
|
509
|
+
const isCurrent =
|
|
510
|
+
options.userSelectionRevision === undefined
|
|
511
|
+
? undefined
|
|
512
|
+
: () => args.session.getUserModelSelectionRevision() === options.userSelectionRevision;
|
|
513
|
+
if (isCurrent && !isCurrent()) return false;
|
|
514
|
+
const apply = async (): Promise<boolean> => {
|
|
515
|
+
if (isCurrent && !isCurrent()) return false;
|
|
502
516
|
if (options.runtimeBindingsOnly) {
|
|
503
|
-
await applyModelProfileRuntimeBindings(
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
517
|
+
await applyModelProfileRuntimeBindings(
|
|
518
|
+
{
|
|
519
|
+
session: args.session,
|
|
520
|
+
modelRegistry: args.modelRegistry,
|
|
521
|
+
settings: args.settings,
|
|
522
|
+
profileName,
|
|
523
|
+
},
|
|
524
|
+
isCurrent,
|
|
525
|
+
);
|
|
509
526
|
} else {
|
|
510
527
|
await activateModelProfile(
|
|
511
528
|
{ session: args.session, modelRegistry: args.modelRegistry, settings: args.settings, profileName },
|
|
512
|
-
{
|
|
529
|
+
{
|
|
530
|
+
persistDefault,
|
|
531
|
+
thinkingLevelOverride: options.thinkingLevelOverride,
|
|
532
|
+
isCurrent,
|
|
533
|
+
},
|
|
513
534
|
);
|
|
514
535
|
}
|
|
515
536
|
return true;
|
|
537
|
+
};
|
|
538
|
+
try {
|
|
539
|
+
return isCurrent ? await args.session.withSdkControlMutation(apply) : await apply();
|
|
516
540
|
} catch (error) {
|
|
517
541
|
if (error instanceof ModelProfileCredentialError && (onCredentialError || options.tolerateCredentialError)) {
|
|
518
|
-
|
|
519
|
-
|
|
542
|
+
profilePassFailures.push({ profileName, error });
|
|
543
|
+
logger.warn("Startup model profile not applied: missing provider credentials", {
|
|
544
|
+
profile: profileName,
|
|
545
|
+
errorClass: error.name,
|
|
546
|
+
providers: error.providers,
|
|
547
|
+
role: error.role,
|
|
548
|
+
});
|
|
549
|
+
if (!onCredentialError) process.stderr.write(`${chalk.yellow(`Warning: ${error.message}`)}\n`);
|
|
520
550
|
return false;
|
|
521
551
|
}
|
|
522
552
|
if (error instanceof UnknownModelProfileError && options.tolerateUnknownDefault) {
|
|
523
|
-
|
|
524
|
-
|
|
553
|
+
profilePassFailures.push({ profileName, error });
|
|
554
|
+
logger.warn("Startup model profile not applied: unknown profile", {
|
|
555
|
+
profile: profileName,
|
|
556
|
+
errorClass: error.name,
|
|
557
|
+
catalogRefreshUnavailable: profileCatalogRefreshUnavailable,
|
|
558
|
+
});
|
|
559
|
+
if (!onUnknownDefault)
|
|
525
560
|
process.stderr.write(
|
|
526
561
|
`${chalk.yellow(`Warning: ${staleDefaultProfileMessage(error, profileCatalogRefreshUnavailable)}`)}\n`,
|
|
527
562
|
);
|
|
528
563
|
return false;
|
|
529
564
|
}
|
|
565
|
+
unhandledProfileName = profileName;
|
|
530
566
|
throw error;
|
|
531
567
|
}
|
|
532
568
|
};
|
|
@@ -546,9 +582,16 @@ async function applyStartupModelProfilesWithPolicy(
|
|
|
546
582
|
const preferCachedProfiles =
|
|
547
583
|
(args.preferCachedModels === true && args.parsedArgs.mpreset !== undefined) ||
|
|
548
584
|
(args.preferCachedDefaultProfile === true && defaultProfile !== undefined);
|
|
549
|
-
|
|
585
|
+
let modelSelectionChangedDuringRecovery = false;
|
|
586
|
+
const applyConfiguredProfiles = async (
|
|
587
|
+
allowMissingDefault: boolean,
|
|
588
|
+
profileNames?: ReadonlySet<string>,
|
|
589
|
+
userSelectionRevision?: number,
|
|
590
|
+
): Promise<boolean> => {
|
|
591
|
+
profilePassFailures = [];
|
|
592
|
+
unhandledProfileName = undefined;
|
|
550
593
|
let applied = true;
|
|
551
|
-
if (defaultProfile) {
|
|
594
|
+
if (defaultProfile && (!profileNames || profileNames.has(defaultProfile))) {
|
|
552
595
|
applied =
|
|
553
596
|
(await applyProfile(defaultProfile, false, {
|
|
554
597
|
thinkingLevelOverride: args.settings.has("defaultThinkingLevel")
|
|
@@ -556,37 +599,141 @@ async function applyStartupModelProfilesWithPolicy(
|
|
|
556
599
|
: undefined,
|
|
557
600
|
tolerateCredentialError: tolerateDefaultProfileFailure,
|
|
558
601
|
tolerateUnknownDefault:
|
|
559
|
-
|
|
602
|
+
onUnknownDefault !== undefined || (allowMissingDefault && tolerateDefaultProfileFailure),
|
|
560
603
|
runtimeBindingsOnly: args.session.hasRecoveredDefaultFallbackChain(),
|
|
604
|
+
userSelectionRevision,
|
|
561
605
|
})) && applied;
|
|
562
606
|
}
|
|
563
|
-
if (args.parsedArgs.mpreset) {
|
|
564
|
-
applied =
|
|
607
|
+
if (args.parsedArgs.mpreset && (!profileNames || profileNames.has(args.parsedArgs.mpreset))) {
|
|
608
|
+
applied =
|
|
609
|
+
(await applyProfile(args.parsedArgs.mpreset, args.parsedArgs.default === true, {
|
|
610
|
+
userSelectionRevision,
|
|
611
|
+
})) && applied;
|
|
565
612
|
}
|
|
566
613
|
return applied;
|
|
567
614
|
};
|
|
615
|
+
const profilesFromFirstFailure = (failedProfiles: ReadonlySet<string>): ReadonlySet<string> => {
|
|
616
|
+
const orderedProfiles = [defaultProfile, args.parsedArgs.mpreset].filter(
|
|
617
|
+
(name): name is string => name !== undefined,
|
|
618
|
+
);
|
|
619
|
+
const firstFailureIndex = orderedProfiles.findIndex(name => failedProfiles.has(name));
|
|
620
|
+
return firstFailureIndex === -1 ? failedProfiles : new Set(orderedProfiles.slice(firstFailureIndex));
|
|
621
|
+
};
|
|
622
|
+
const applyFailedProfiles = async (
|
|
623
|
+
failedProfiles: ReadonlySet<string>,
|
|
624
|
+
userSelectionRevision: number,
|
|
625
|
+
): Promise<boolean> => {
|
|
626
|
+
const applied = await applyConfiguredProfiles(true, failedProfiles, userSelectionRevision);
|
|
627
|
+
if (
|
|
628
|
+
applied &&
|
|
629
|
+
defaultProfile !== undefined &&
|
|
630
|
+
failedProfiles.has(defaultProfile) &&
|
|
631
|
+
args.parsedArgs.mpreset !== undefined &&
|
|
632
|
+
!failedProfiles.has(args.parsedArgs.mpreset)
|
|
633
|
+
) {
|
|
634
|
+
return applyProfile(args.parsedArgs.mpreset, args.parsedArgs.default === true, { userSelectionRevision });
|
|
635
|
+
}
|
|
636
|
+
return applied;
|
|
637
|
+
};
|
|
638
|
+
const refreshAuthAndCatalog = async (continueOnRefreshError: boolean): Promise<void> => {
|
|
639
|
+
try {
|
|
640
|
+
await args.modelRegistry.authStorage?.reload();
|
|
641
|
+
} catch (error) {
|
|
642
|
+
logger.warn("Failed to reload auth before startup model profile recovery", {
|
|
643
|
+
error: error instanceof Error ? error.message : String(error),
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
try {
|
|
647
|
+
await args.modelRegistry.refreshModelPresetProfilesFromRegistry();
|
|
648
|
+
} catch (error) {
|
|
649
|
+
if (!continueOnRefreshError && args.modelRegistry.getError()) throw error;
|
|
650
|
+
profileCatalogRefreshUnavailable = true;
|
|
651
|
+
logger.warn("Failed to refresh model profile catalog before startup recovery", {
|
|
652
|
+
error: error instanceof Error ? error.message : String(error),
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
try {
|
|
656
|
+
await args.modelRegistry.refresh("online-if-uncached", args.session.credentialSessionId);
|
|
657
|
+
} catch (error) {
|
|
658
|
+
if (!continueOnRefreshError) throw error;
|
|
659
|
+
logger.warn("Failed to refresh model catalog before startup profile recovery", {
|
|
660
|
+
error: error instanceof Error ? error.message : String(error),
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
const reportFinalProfileFailures = (markDefaultUnavailable = true): void => {
|
|
665
|
+
for (const failure of profilePassFailures) {
|
|
666
|
+
if (failure.error instanceof ModelProfileCredentialError) {
|
|
667
|
+
onCredentialError?.(failure.error);
|
|
668
|
+
} else {
|
|
669
|
+
onUnknownDefault?.(failure.error, profileCatalogRefreshUnavailable);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
if (
|
|
673
|
+
markDefaultUnavailable &&
|
|
674
|
+
(onCredentialError || onUnknownDefault) &&
|
|
675
|
+
args.parsedArgs.mpreset === undefined &&
|
|
676
|
+
args.parsedArgs.model === undefined
|
|
677
|
+
) {
|
|
678
|
+
const defaultFailure = profilePassFailures.find(failure => failure.profileName === defaultProfile);
|
|
679
|
+
if (defaultFailure) args.session.setUnavailableModelProfile(defaultFailure.profileName);
|
|
680
|
+
}
|
|
681
|
+
};
|
|
568
682
|
|
|
683
|
+
// Deferred --mpreset startup can overlap selector input; never reapply its
|
|
684
|
+
// captured profile after the user has selected a newer model.
|
|
569
685
|
if (preferCachedProfiles) {
|
|
570
686
|
let applied: boolean;
|
|
571
687
|
let refreshedOnline = false;
|
|
688
|
+
const selectionRevision = args.session.getUserModelSelectionRevision();
|
|
572
689
|
try {
|
|
573
|
-
applied = await applyConfiguredProfiles(false);
|
|
690
|
+
applied = await applyConfiguredProfiles(false, undefined, selectionRevision);
|
|
574
691
|
} catch (error) {
|
|
575
692
|
if (error instanceof ModelProfileCredentialError) throw error;
|
|
576
693
|
if (error instanceof UnknownModelProfileError) {
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
if (args.modelRegistry.getError()) throw refreshError;
|
|
581
|
-
profileCatalogRefreshUnavailable = true;
|
|
582
|
-
logger.warn("Failed to refresh model profile catalog before startup recovery", {
|
|
583
|
-
error: refreshError instanceof Error ? refreshError.message : String(refreshError),
|
|
584
|
-
});
|
|
585
|
-
}
|
|
694
|
+
await refreshAuthAndCatalog(onCredentialError !== undefined || onUnknownDefault !== undefined);
|
|
695
|
+
} else {
|
|
696
|
+
await args.modelRegistry.refresh("online-if-uncached", args.session.credentialSessionId);
|
|
586
697
|
}
|
|
587
|
-
|
|
698
|
+
if (args.session.getUserModelSelectionRevision() !== selectionRevision) return;
|
|
588
699
|
refreshedOnline = true;
|
|
589
|
-
|
|
700
|
+
const failedProfiles = new Set(profilePassFailures.map(failure => failure.profileName));
|
|
701
|
+
if (unhandledProfileName) failedProfiles.add(unhandledProfileName);
|
|
702
|
+
const profilesToRetry = profilesFromFirstFailure(failedProfiles);
|
|
703
|
+
applied = await applyConfiguredProfiles(
|
|
704
|
+
true,
|
|
705
|
+
profilesToRetry.size > 0 ? profilesToRetry : undefined,
|
|
706
|
+
selectionRevision,
|
|
707
|
+
);
|
|
708
|
+
if (args.session.getUserModelSelectionRevision() !== selectionRevision) return;
|
|
709
|
+
}
|
|
710
|
+
if (args.session.getUserModelSelectionRevision() !== selectionRevision) {
|
|
711
|
+
modelSelectionChangedDuringRecovery = true;
|
|
712
|
+
applied = false;
|
|
713
|
+
}
|
|
714
|
+
if (
|
|
715
|
+
!modelSelectionChangedDuringRecovery &&
|
|
716
|
+
!applied &&
|
|
717
|
+
profilePassFailures.length > 0 &&
|
|
718
|
+
(onCredentialError !== undefined || onUnknownDefault !== undefined)
|
|
719
|
+
) {
|
|
720
|
+
if (!refreshedOnline) {
|
|
721
|
+
const failedProfiles = new Set(profilePassFailures.map(failure => failure.profileName));
|
|
722
|
+
await refreshAuthAndCatalog(true);
|
|
723
|
+
refreshedOnline = true;
|
|
724
|
+
if (args.session.getUserModelSelectionRevision() !== selectionRevision) {
|
|
725
|
+
modelSelectionChangedDuringRecovery = true;
|
|
726
|
+
applied = false;
|
|
727
|
+
} else {
|
|
728
|
+
applied = await applyFailedProfiles(failedProfiles, selectionRevision);
|
|
729
|
+
if (args.session.getUserModelSelectionRevision() !== selectionRevision) {
|
|
730
|
+
modelSelectionChangedDuringRecovery = true;
|
|
731
|
+
applied = false;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
if (applied) args.session.setUnavailableModelProfile(undefined);
|
|
736
|
+
else reportFinalProfileFailures(!modelSelectionChangedDuringRecovery);
|
|
590
737
|
}
|
|
591
738
|
if (applied && !refreshedOnline)
|
|
592
739
|
args.modelRegistry.refreshInBackground("online-if-uncached", args.session.credentialSessionId);
|
|
@@ -598,7 +745,7 @@ async function applyStartupModelProfilesWithPolicy(
|
|
|
598
745
|
}
|
|
599
746
|
|
|
600
747
|
// Explicit CLI --model/--thinking must win over any activated or skipped profile.
|
|
601
|
-
if (explicitModel) {
|
|
748
|
+
if (explicitModel && !modelSelectionChangedDuringRecovery) {
|
|
602
749
|
await args.session.setModelTemporary(explicitModel, args.startupThinkingLevel ?? args.parsedArgs.thinking, {
|
|
603
750
|
persistAsSessionDefault: true,
|
|
604
751
|
cause: "startup-override",
|
|
@@ -606,7 +753,7 @@ async function applyStartupModelProfilesWithPolicy(
|
|
|
606
753
|
const selector = `${explicitModel.provider}/${explicitModel.id}`;
|
|
607
754
|
args.session.setConfiguredModelChain("default", [selector], "startup-override", undefined, true);
|
|
608
755
|
args.session.seedDefaultFallbackResolution(0, []);
|
|
609
|
-
} else if (args.parsedArgs.thinking && args.session.model) {
|
|
756
|
+
} else if (!modelSelectionChangedDuringRecovery && args.parsedArgs.thinking && args.session.model) {
|
|
610
757
|
await args.session.setModelTemporary(args.session.model, args.parsedArgs.thinking, { cause: "startup-override" });
|
|
611
758
|
}
|
|
612
759
|
}
|
|
@@ -975,6 +1122,21 @@ export async function runInteractiveMode(
|
|
|
975
1122
|
);
|
|
976
1123
|
}
|
|
977
1124
|
|
|
1125
|
+
// Startup input bypasses the editor submit path, so seed the automatic title
|
|
1126
|
+
// here. Only text that `prompt` actually dispatches to a loaded skill is
|
|
1127
|
+
// skipped; unknown `/skill:` text is submitted as an ordinary prompt and is
|
|
1128
|
+
// titled like one. It must run before the prompt (the title gate skips
|
|
1129
|
+
// sessions that already hold a user message), and a title failure must never
|
|
1130
|
+
// drop the prompt.
|
|
1131
|
+
const maybeGenerateStartupTitle = (text: string, images?: readonly unknown[]): void => {
|
|
1132
|
+
try {
|
|
1133
|
+
if (session.resolvePromptSkillInvocation(text, images) || session.isLocallyHandledSlashCommand(text)) return;
|
|
1134
|
+
mode.maybeGenerateSessionTitle(text);
|
|
1135
|
+
} catch (error: unknown) {
|
|
1136
|
+
logger.warn("Startup session title generation failed", { error: String(error) });
|
|
1137
|
+
}
|
|
1138
|
+
};
|
|
1139
|
+
|
|
978
1140
|
const runStartupInputAndPromptLoop = async (): Promise<never> => {
|
|
979
1141
|
const hasStartupInput = initialMessage !== undefined || initialMessages.length > 0;
|
|
980
1142
|
if (!hasStartupInput && resumeAction === "continue-tail") {
|
|
@@ -988,6 +1150,7 @@ export async function runInteractiveMode(
|
|
|
988
1150
|
|
|
989
1151
|
if (initialMessage !== undefined) {
|
|
990
1152
|
try {
|
|
1153
|
+
maybeGenerateStartupTitle(initialMessage, initialImages);
|
|
991
1154
|
await session.prompt(initialMessage, { images: initialImages });
|
|
992
1155
|
} catch (error: unknown) {
|
|
993
1156
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
@@ -1004,6 +1167,7 @@ export async function runInteractiveMode(
|
|
|
1004
1167
|
});
|
|
1005
1168
|
if (slashResult === true) continue;
|
|
1006
1169
|
if (typeof slashResult === "string") text = slashResult;
|
|
1170
|
+
maybeGenerateStartupTitle(text);
|
|
1007
1171
|
await session.prompt(text);
|
|
1008
1172
|
} catch (error: unknown) {
|
|
1009
1173
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|