@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
|
@@ -325,6 +325,7 @@ export class ExtensionRunner {
|
|
|
325
325
|
#getArtifactRangeFn: ExtensionContextActions["getArtifactRange"] = undefined;
|
|
326
326
|
|
|
327
327
|
#getJobsFn: ExtensionContextActions["getJobs"] = undefined;
|
|
328
|
+
#getProjectProgressFn: ExtensionContextActions["getProjectProgress"] = undefined;
|
|
328
329
|
#onJobFoldFn: ExtensionContextActions["onJobFold"] = undefined;
|
|
329
330
|
#onSessionEventFn: ExtensionContextActions["onSessionEvent"] = undefined;
|
|
330
331
|
#sdkControlFn: ExtensionContextActions["sdkControl"] = undefined;
|
|
@@ -482,6 +483,7 @@ export class ExtensionRunner {
|
|
|
482
483
|
this.#getArtifactRangeFn = contextActions.getArtifactRange;
|
|
483
484
|
|
|
484
485
|
this.#getJobsFn = contextActions.getJobs;
|
|
486
|
+
this.#getProjectProgressFn = contextActions.getProjectProgress;
|
|
485
487
|
this.#onJobFoldFn = contextActions.onJobFold;
|
|
486
488
|
this.#onSessionEventFn = contextActions.onSessionEvent;
|
|
487
489
|
this.#sdkControlFn = contextActions.sdkControl;
|
|
@@ -848,6 +850,7 @@ export class ExtensionRunner {
|
|
|
848
850
|
getArtifactRange: (id, offset, length) => this.#getArtifactRangeFn?.(id, offset, length),
|
|
849
851
|
|
|
850
852
|
getJobs: () => this.#getJobsFn?.(),
|
|
853
|
+
...(this.#getProjectProgressFn ? { getProjectProgress: this.#getProjectProgressFn } : {}),
|
|
851
854
|
onJobFold: listener => this.#onJobFoldFn?.(listener) ?? (() => {}),
|
|
852
855
|
onSessionEvent: listener => this.#onSessionEventFn?.(listener) ?? (() => {}),
|
|
853
856
|
sdkControl: (operation, input) => this.#sdkControlFn?.(operation, input),
|
|
@@ -867,6 +870,7 @@ export class ExtensionRunner {
|
|
|
867
870
|
...(this.#getExtensionsFn ? ["getExtensions"] : []),
|
|
868
871
|
...(this.#getArtifactRangeFn ? ["getArtifactRange"] : []),
|
|
869
872
|
...(this.#getJobsFn ? ["getJobs"] : []),
|
|
873
|
+
...(this.#getProjectProgressFn ? ["getProjectProgress"] : []),
|
|
870
874
|
...(this.#onJobFoldFn ? ["onJobFold"] : []),
|
|
871
875
|
...(this.#sdkControlFn ? ["sdkControl"] : []),
|
|
872
876
|
...(this.#invokeSkillFn ? ["invokeSkill"] : []),
|
|
@@ -49,6 +49,7 @@ import type { ExecOptions, ExecResult } from "../../exec/exec";
|
|
|
49
49
|
import type { CustomEditor } from "../../modes/components/custom-editor";
|
|
50
50
|
import type { WorkflowGateEmitter } from "../../modes/shared/agent-wire/workflow-gate-broker";
|
|
51
51
|
import type { Theme } from "../../modes/theme/theme";
|
|
52
|
+
import type { ProjectProgressSnapshot } from "../../progress/progress-contract";
|
|
52
53
|
import type { AgentSessionEventListener } from "../../session/agent-session";
|
|
53
54
|
import type {
|
|
54
55
|
ClientBridge,
|
|
@@ -538,6 +539,8 @@ export interface ExtensionContext {
|
|
|
538
539
|
| Promise<{ bytes: Uint8Array; totalBytes: number } | undefined>;
|
|
539
540
|
|
|
540
541
|
getJobs(): unknown;
|
|
542
|
+
/** Read-only project progress snapshot (`session.progress`), derived from durable session state. */
|
|
543
|
+
getProjectProgress?(): Promise<ProjectProgressSnapshot>;
|
|
541
544
|
/** Subscribe to foreground-wait folds (`bash_folded`). Returns an unsubscribe function. */
|
|
542
545
|
onJobFold?(listener: (event: JobFoldEvent) => void): () => void;
|
|
543
546
|
/** Observe session events synchronously in producer order. Returns an unsubscribe function. */
|
|
@@ -764,6 +767,8 @@ export interface AgentStartEvent extends SharedAgentStartEvent {
|
|
|
764
767
|
sdkRunToken?: string;
|
|
765
768
|
/** Complete set of SDK queue owners consumed together by this run. */
|
|
766
769
|
sdkRunTokens?: string[];
|
|
770
|
+
/** Internal public lifecycle boundary; accepted retries with a suppressed predecessor end retain it. */
|
|
771
|
+
lifecycleScope?: AttemptScope;
|
|
767
772
|
}
|
|
768
773
|
|
|
769
774
|
/** Fired when an agent run fails before emitting agent_end. The error is the
|
|
@@ -1723,6 +1728,8 @@ export interface ExtensionContextActions {
|
|
|
1723
1728
|
| undefined
|
|
1724
1729
|
| Promise<{ bytes: Uint8Array; totalBytes: number } | undefined>;
|
|
1725
1730
|
getJobs?: () => unknown;
|
|
1731
|
+
/** Read-only project progress snapshot backing the `session.progress` SDK query. */
|
|
1732
|
+
getProjectProgress?: () => Promise<ProjectProgressSnapshot>;
|
|
1726
1733
|
/** Subscribe to foreground-wait folds so the SDK host can publish `bash_folded`. Returns an unsubscribe. */
|
|
1727
1734
|
onJobFold?: (listener: (event: JobFoldEvent) => void) => () => void;
|
|
1728
1735
|
/** Ordered, synchronous AgentSession subscription, independent of async extension hooks. */
|
|
@@ -149,22 +149,70 @@ const initialTemporaryRoots = initialProcessEnvironment.then(async environment =
|
|
|
149
149
|
}
|
|
150
150
|
return [...roots];
|
|
151
151
|
});
|
|
152
|
-
|
|
153
|
-
|
|
152
|
+
|
|
153
|
+
/** Startup identity of a file: the object `hashStableFile` must still be reading. */
|
|
154
|
+
export interface StableFileIdentity {
|
|
155
|
+
dev: number;
|
|
156
|
+
ino: number;
|
|
157
|
+
size: number;
|
|
158
|
+
mtimeMs: number;
|
|
159
|
+
ctimeMs: number;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Startup identity of every canonical `node` on PATH, keyed by realpath. Only
|
|
164
|
+
* `stat` runs at startup; the digest is computed on first use from a descriptor
|
|
165
|
+
* whose identity must still match this snapshot, so a later replacement cannot
|
|
166
|
+
* become launch authority.
|
|
167
|
+
*/
|
|
168
|
+
const initialNodeIdentities = initialProcessEnvironment.then(async environment => {
|
|
169
|
+
const identities = new Map<string, StableFileIdentity>();
|
|
154
170
|
const temporaryRoots = await initialTemporaryRoots;
|
|
155
171
|
for (const pathEntry of (environment.get("PATH") ?? "").split(path.delimiter).filter(path.isAbsolute)) {
|
|
156
172
|
const lexical = path.join(pathEntry, process.platform === "win32" ? "node.exe" : "node");
|
|
157
173
|
try {
|
|
158
174
|
const real = await fs.realpath(lexical);
|
|
159
175
|
if (temporaryRoots.some(root => isWithin(root, real))) continue;
|
|
160
|
-
|
|
176
|
+
if (identities.has(real)) continue;
|
|
177
|
+
const stat = await fs.stat(real);
|
|
178
|
+
identities.set(real, {
|
|
179
|
+
dev: stat.dev,
|
|
180
|
+
ino: stat.ino,
|
|
181
|
+
size: stat.size,
|
|
182
|
+
mtimeMs: stat.mtimeMs,
|
|
183
|
+
ctimeMs: stat.ctimeMs,
|
|
184
|
+
});
|
|
161
185
|
} catch {
|
|
162
|
-
// Missing
|
|
186
|
+
// Missing startup candidates do not become authority.
|
|
163
187
|
}
|
|
164
188
|
}
|
|
165
|
-
return
|
|
189
|
+
return identities;
|
|
166
190
|
});
|
|
167
191
|
|
|
192
|
+
/** First-use digests of startup Node candidates, keyed by realpath; failures are cached as `undefined`. */
|
|
193
|
+
const initialNodeHashes = new Map<string, Promise<string | undefined>>();
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Digest of a `node` that was on PATH at startup, or `undefined` when the path
|
|
197
|
+
* was not a startup candidate or no longer is the startup file object.
|
|
198
|
+
*/
|
|
199
|
+
export function getInitialNodeHash(realPath: string): Promise<string | undefined> {
|
|
200
|
+
let digest = initialNodeHashes.get(realPath);
|
|
201
|
+
if (!digest) {
|
|
202
|
+
digest = initialNodeIdentities.then(async identities => {
|
|
203
|
+
const identity = identities.get(realPath);
|
|
204
|
+
if (!identity) return undefined;
|
|
205
|
+
try {
|
|
206
|
+
return await hashStableFile(realPath, "Initial Node executable", MCP_LAUNCHER_MAX_BYTES, identity);
|
|
207
|
+
} catch {
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
initialNodeHashes.set(realPath, digest);
|
|
212
|
+
}
|
|
213
|
+
return digest;
|
|
214
|
+
}
|
|
215
|
+
|
|
168
216
|
async function snapshotExistingFile(filePath: string): Promise<FileSnapshot | null> {
|
|
169
217
|
try {
|
|
170
218
|
const stat = await fs.stat(filePath);
|
|
@@ -416,11 +464,28 @@ const STABLE_HASH_CHUNK_BYTES = 1024 * 1024;
|
|
|
416
464
|
* on PATH; buffering each interpreter (~120 MiB) held its full bytes in the
|
|
417
465
|
* session process until exit.
|
|
418
466
|
*/
|
|
419
|
-
export async function hashStableFile(
|
|
467
|
+
export async function hashStableFile(
|
|
468
|
+
filePath: string,
|
|
469
|
+
label: string,
|
|
470
|
+
maxBytes: number,
|
|
471
|
+
expected?: StableFileIdentity,
|
|
472
|
+
): Promise<string> {
|
|
420
473
|
const handle = await fs.open(filePath, fs.constants.O_RDONLY);
|
|
421
474
|
try {
|
|
422
475
|
const before = await handle.stat();
|
|
423
476
|
if (!before.isFile()) throw new Error(`${label} is not a regular file`);
|
|
477
|
+
// Checked on the opened descriptor, so the digest belongs to the expected
|
|
478
|
+
// file object even if the path is swapped around the open.
|
|
479
|
+
if (
|
|
480
|
+
expected &&
|
|
481
|
+
(before.dev !== expected.dev ||
|
|
482
|
+
before.ino !== expected.ino ||
|
|
483
|
+
before.size !== expected.size ||
|
|
484
|
+
before.mtimeMs !== expected.mtimeMs ||
|
|
485
|
+
before.ctimeMs !== expected.ctimeMs)
|
|
486
|
+
) {
|
|
487
|
+
throw new Error(`${label} is not the expected file`);
|
|
488
|
+
}
|
|
424
489
|
if (before.size > maxBytes) throw new Error(`${label} exceeds its byte limit`);
|
|
425
490
|
const hash = createHash("sha256");
|
|
426
491
|
const chunk = Buffer.allocUnsafe(STABLE_HASH_CHUNK_BYTES);
|
|
@@ -515,7 +580,7 @@ async function isInitialManagedNodeLauncherPath(
|
|
|
515
580
|
const real = await fs.realpath(executablePath);
|
|
516
581
|
if (untrustedRoots.some(root => isWithin(root, real))) return false;
|
|
517
582
|
if ((await initialTemporaryRoots).some(root => isWithin(root, real))) return false;
|
|
518
|
-
const expected =
|
|
583
|
+
const expected = await getInitialNodeHash(real);
|
|
519
584
|
if (!expected) return false;
|
|
520
585
|
return (await hashStableFile(real, "Initial Node executable", MCP_LAUNCHER_MAX_BYTES)) === expected;
|
|
521
586
|
}
|
|
@@ -671,7 +736,7 @@ async function prepareVerifiedStdioLaunch(input: {
|
|
|
671
736
|
true,
|
|
672
737
|
);
|
|
673
738
|
const launcherReal = await fs.realpath(input.launcherPath);
|
|
674
|
-
const expectedLauncherHash =
|
|
739
|
+
const expectedLauncherHash = await getInitialNodeHash(launcherReal);
|
|
675
740
|
if (!expectedLauncherHash || sha256(launcherBytes) !== expectedLauncherHash) {
|
|
676
741
|
throw new Error("Plugin MCP Node interpreter drifted from startup authority");
|
|
677
742
|
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from "@gajae-code/utils";
|
|
13
13
|
import { withFileLock } from "../../config/file-lock";
|
|
14
14
|
import { extractPackageName, parsePluginSpec } from "./parser";
|
|
15
|
+
import { resolvePluginLinkPath } from "./plugin-link-path";
|
|
15
16
|
import type {
|
|
16
17
|
DoctorCheck,
|
|
17
18
|
DoctorOptions,
|
|
@@ -535,11 +536,11 @@ export class PluginManager {
|
|
|
535
536
|
|
|
536
537
|
await this.#ensurePluginsDir();
|
|
537
538
|
|
|
538
|
-
const linkPath =
|
|
539
|
+
const linkPath = resolvePluginLinkPath(getPluginsNodeModules(), pkg.name);
|
|
539
540
|
|
|
540
541
|
// Handle scoped packages
|
|
541
542
|
if (pkg.name.startsWith("@")) {
|
|
542
|
-
const scopeDir = path.
|
|
543
|
+
const scopeDir = path.dirname(linkPath);
|
|
543
544
|
await fs.promises.mkdir(scopeDir, { recursive: true });
|
|
544
545
|
}
|
|
545
546
|
|
|
@@ -87,6 +87,21 @@ export async function cachePlugin(
|
|
|
87
87
|
return targetPath;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/** Write a regular file. A symlink at the destination is removed, not followed. */
|
|
91
|
+
export async function writeCachedRegularFile(targetPath: string, content: string): Promise<void> {
|
|
92
|
+
try {
|
|
93
|
+
const stat = await fs.lstat(targetPath);
|
|
94
|
+
if (stat.isSymbolicLink()) {
|
|
95
|
+
await fs.unlink(targetPath);
|
|
96
|
+
} else if (!stat.isFile()) {
|
|
97
|
+
throw new Error("unsafe cache artifact");
|
|
98
|
+
}
|
|
99
|
+
} catch (error) {
|
|
100
|
+
if (!isEnoent(error)) throw error;
|
|
101
|
+
}
|
|
102
|
+
await Bun.write(targetPath, content);
|
|
103
|
+
}
|
|
104
|
+
|
|
90
105
|
export async function stageCachedPlugin(sourcePath: string, stagingPath: string): Promise<string> {
|
|
91
106
|
await fs.rm(stagingPath, { recursive: true, force: true });
|
|
92
107
|
await fs.cp(sourcePath, stagingPath, { recursive: true, errorOnExist: true, verbatimSymlinks: true });
|
|
@@ -19,7 +19,13 @@ import {
|
|
|
19
19
|
} from "@gajae-code/natives";
|
|
20
20
|
import { isEnoent, logger, pathIsWithin } from "@gajae-code/utils";
|
|
21
21
|
import { withFileLock } from "../../../config/file-lock";
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
cachePlugin,
|
|
24
|
+
getCachedPluginPath,
|
|
25
|
+
inspectCachedPlugin,
|
|
26
|
+
stageCachedPlugin,
|
|
27
|
+
writeCachedRegularFile,
|
|
28
|
+
} from "./cache";
|
|
23
29
|
import { classifySource, fetchMarketplace, parseMarketplaceCatalog, promoteCloneToCache } from "./fetcher";
|
|
24
30
|
import {
|
|
25
31
|
addInstalledPlugin,
|
|
@@ -1430,12 +1436,14 @@ export class MarketplaceManager {
|
|
|
1430
1436
|
if (!pathIsWithin(cachePath, sourcePath)) {
|
|
1431
1437
|
throw new Error(`Plugin "${entry.name}" lspServers path escapes the plugin directory`);
|
|
1432
1438
|
}
|
|
1439
|
+
const sourceStat = await fs.lstat(sourcePath);
|
|
1440
|
+
if (sourceStat.isSymbolicLink()) throw new Error(`Plugin "${entry.name}" lspServers path is a symlink`);
|
|
1433
1441
|
const content = await Bun.file(sourcePath).text();
|
|
1434
|
-
await
|
|
1442
|
+
await writeCachedRegularFile(targetPath, content);
|
|
1435
1443
|
return;
|
|
1436
1444
|
}
|
|
1437
1445
|
|
|
1438
|
-
await
|
|
1446
|
+
await writeCachedRegularFile(targetPath, `${JSON.stringify({ servers: lspServers }, null, 2)}\n`);
|
|
1439
1447
|
}
|
|
1440
1448
|
|
|
1441
1449
|
/**
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
/** Resolve a package.json name under the plugins node_modules root, or throw. */
|
|
4
|
+
export function resolvePluginLinkPath(root: string, name: string): string {
|
|
5
|
+
const base = path.resolve(root);
|
|
6
|
+
const destination = path.resolve(base, name);
|
|
7
|
+
const relative = path.relative(base, destination);
|
|
8
|
+
if (relative === "" || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
|
|
9
|
+
throw new Error(`package.json name is not a safe plugin link path: ${name}`);
|
|
10
|
+
}
|
|
11
|
+
return destination;
|
|
12
|
+
}
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
import { findRepoRoot } from "../capability/fs";
|
|
10
10
|
import type { Skill as CapabilitySkill } from "../capability/skill";
|
|
11
11
|
import type { SkillsSettings } from "../config/settings-schema";
|
|
12
|
-
import { resolveSkillScopeTrust } from "../config/skill-settings-defaults";
|
|
12
|
+
import { resolveGlobalUserSkillLinkTrust, resolveSkillScopeTrust } from "../config/skill-settings-defaults";
|
|
13
13
|
import { BUNDLED_GJC_SKILL_CATALOG } from "../defaults/gjc-skills.generated";
|
|
14
14
|
import { scanClaudeProjectSkills, scanClaudeUserSkills } from "../discovery/claude";
|
|
15
15
|
import { loadMarketplaceSkills } from "../discovery/claude-plugins";
|
|
@@ -79,6 +79,8 @@ export interface DiscoverRuntimeSkillsOptions {
|
|
|
79
79
|
offset?: number;
|
|
80
80
|
source?: RuntimeSkillDiscoveryScope | "all";
|
|
81
81
|
policy?: SkillsSettings;
|
|
82
|
+
/** User-owned permission to read external user skill symlinks; defaults to the direct policy value. */
|
|
83
|
+
allowExternalUserSkillSymlinks?: boolean;
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
function getRuntimeHome(): string {
|
|
@@ -296,6 +298,7 @@ async function scanProjectOrUserDir(
|
|
|
296
298
|
source: RuntimeSkillDiscoveryScope,
|
|
297
299
|
providerPriority: number,
|
|
298
300
|
authorityRoot?: string,
|
|
301
|
+
allowExternalUserSkillSymlinks?: boolean,
|
|
299
302
|
): Promise<ScanJobResult> {
|
|
300
303
|
const result = await scanSkillsFromDir(ctx, {
|
|
301
304
|
dir,
|
|
@@ -304,6 +307,7 @@ async function scanProjectOrUserDir(
|
|
|
304
307
|
providerId: "runtime",
|
|
305
308
|
level,
|
|
306
309
|
requireDescription: true,
|
|
310
|
+
allowExternalUserSkillSymlinks,
|
|
307
311
|
});
|
|
308
312
|
return {
|
|
309
313
|
items: result.items.map(skill => ({ skill, source, providerPriority })),
|
|
@@ -449,6 +453,8 @@ export async function discoverRuntimeSkills(
|
|
|
449
453
|
const home = options.home ?? getRuntimeHome();
|
|
450
454
|
const source = options.source ?? "all";
|
|
451
455
|
const policy = options.policy;
|
|
456
|
+
const allowExternalUserSkillSymlinks =
|
|
457
|
+
options.allowExternalUserSkillSymlinks ?? resolveGlobalUserSkillLinkTrust(policy ?? {});
|
|
452
458
|
const diagnostics: string[] = [];
|
|
453
459
|
const agentDir = resolveRuntimeAgentDir(home, options.agentDir, hasExplicitHome);
|
|
454
460
|
const profileAuthority =
|
|
@@ -478,6 +484,7 @@ export async function discoverRuntimeSkills(
|
|
|
478
484
|
"user",
|
|
479
485
|
100,
|
|
480
486
|
path.resolve(dir) === path.resolve(agentDir, "skills") ? agentDir : undefined,
|
|
487
|
+
allowExternalUserSkillSymlinks,
|
|
481
488
|
),
|
|
482
489
|
);
|
|
483
490
|
}
|
|
@@ -485,7 +492,16 @@ export async function discoverRuntimeSkills(
|
|
|
485
492
|
if ((source === "all" || source === "user") && policy?.enabled === true) {
|
|
486
493
|
for (const dir of getCustomSkillDirs(policy, home)) {
|
|
487
494
|
scanJobs.push(
|
|
488
|
-
scanProjectOrUserDir(
|
|
495
|
+
scanProjectOrUserDir(
|
|
496
|
+
{ cwd: options.cwd, home, repoRoot: home },
|
|
497
|
+
dir,
|
|
498
|
+
"user",
|
|
499
|
+
`custom ${dir}`,
|
|
500
|
+
"user",
|
|
501
|
+
0,
|
|
502
|
+
undefined,
|
|
503
|
+
allowExternalUserSkillSymlinks,
|
|
504
|
+
),
|
|
489
505
|
);
|
|
490
506
|
}
|
|
491
507
|
}
|
|
@@ -630,6 +646,7 @@ export async function findRuntimeSkillByName(
|
|
|
630
646
|
home?: string,
|
|
631
647
|
agentDir?: string,
|
|
632
648
|
profileAuthority?: "default" | "custom",
|
|
649
|
+
allowExternalUserSkillSymlinks?: boolean,
|
|
633
650
|
): Promise<Skill | undefined> {
|
|
634
651
|
const normalized = name.trim();
|
|
635
652
|
if (!normalized) return undefined;
|
|
@@ -650,6 +667,7 @@ export async function findRuntimeSkillByName(
|
|
|
650
667
|
home: resolvedHome,
|
|
651
668
|
agentDir: resolvedAgentDir,
|
|
652
669
|
profileAuthority: resolvedProfileAuthority,
|
|
670
|
+
allowExternalUserSkillSymlinks: allowExternalUserSkillSymlinks ?? resolveGlobalUserSkillLinkTrust(policy ?? {}),
|
|
653
671
|
});
|
|
654
672
|
return loaded.skills.find(skill => skill.name === normalized);
|
|
655
673
|
}
|
|
@@ -299,8 +299,10 @@ export async function listNativeSkillsForManagement(options: {
|
|
|
299
299
|
cwd: string;
|
|
300
300
|
home?: string;
|
|
301
301
|
agentDir?: string;
|
|
302
|
-
/** Resolver-owned profile classification; unlike path comparison, this survives HOME refreshes. */
|
|
302
|
+
/** Resolver-owned profile classification; unlike path comparison, this survives HOME/config refreshes. */
|
|
303
303
|
profileAuthority?: "default" | "custom";
|
|
304
|
+
/** User-owned permission to follow symlinks outside user skill scan roots. */
|
|
305
|
+
allowExternalUserSkillSymlinks?: boolean;
|
|
304
306
|
policy?: SkillManagementPolicy;
|
|
305
307
|
}): Promise<ManagedSkillRecord[]> {
|
|
306
308
|
const homeWasInjected = options.home !== undefined;
|
|
@@ -321,6 +323,7 @@ export async function listNativeSkillsForManagement(options: {
|
|
|
321
323
|
const policy = options.policy;
|
|
322
324
|
const projectTrusted = resolveSkillScopeTrust(policy ?? {}, "project");
|
|
323
325
|
const userTrusted = resolveSkillScopeTrust(policy ?? {}, "user");
|
|
326
|
+
const allowExternalUserSkillSymlinks = options.allowExternalUserSkillSymlinks ?? userTrusted;
|
|
324
327
|
|
|
325
328
|
const scanJobs: Array<Promise<{ dir: string; scope: SkillScope; items: CapabilitySkill[] }>> = [];
|
|
326
329
|
const projectDirs = await getProjectSkillDirs(options.cwd, home);
|
|
@@ -351,6 +354,7 @@ export async function listNativeSkillsForManagement(options: {
|
|
|
351
354
|
providerId: "runtime",
|
|
352
355
|
level: "user",
|
|
353
356
|
requireDescription: true,
|
|
357
|
+
allowExternalUserSkillSymlinks,
|
|
354
358
|
},
|
|
355
359
|
).then(result => ({ dir, scope: "user" as const, items: result.items })),
|
|
356
360
|
);
|
|
@@ -391,7 +395,7 @@ export async function listNativeSkillsForManagement(options: {
|
|
|
391
395
|
records.push({
|
|
392
396
|
name: skill.name,
|
|
393
397
|
description: typeof skill.frontmatter?.description === "string" ? skill.frontmatter.description : "",
|
|
394
|
-
path: skill.path,
|
|
398
|
+
path: skill.discoveryPath ?? skill.path,
|
|
395
399
|
scope,
|
|
396
400
|
source,
|
|
397
401
|
hidden: skill.frontmatter?.hide === true,
|
|
@@ -125,6 +125,8 @@ export interface LoadSkillsOptions extends SkillsSettings {
|
|
|
125
125
|
agentDir?: string;
|
|
126
126
|
/** Resolver-owned classification for the selected agent directory. */
|
|
127
127
|
profileAuthority?: "default" | "custom";
|
|
128
|
+
/** User-owned permission to follow symlinks outside user skill scan roots. */
|
|
129
|
+
allowExternalUserSkillSymlinks?: boolean;
|
|
128
130
|
}
|
|
129
131
|
|
|
130
132
|
/**
|
|
@@ -167,6 +169,7 @@ export async function loadSkills(options: LoadSkillsOptions = {}): Promise<LoadS
|
|
|
167
169
|
|
|
168
170
|
const projectTrusted = resolveSkillScopeTrust(options, "project");
|
|
169
171
|
const userTrusted = resolveSkillScopeTrust(options, "user");
|
|
172
|
+
const allowExternalUserSkillSymlinks = options.allowExternalUserSkillSymlinks ?? userTrusted;
|
|
170
173
|
|
|
171
174
|
// Skill scope trust decides which canonical locations are loaded: project
|
|
172
175
|
// scope covers `.gjc/skills` (walk-up), user scope covers `~/.gjc/agent/skills`
|
|
@@ -197,6 +200,7 @@ export async function loadSkills(options: LoadSkillsOptions = {}): Promise<LoadS
|
|
|
197
200
|
userAgentDir: agentDir,
|
|
198
201
|
profileAuthority,
|
|
199
202
|
repoRoot: await findRepoRoot(cwd),
|
|
203
|
+
allowExternalUserSkillSymlinks,
|
|
200
204
|
};
|
|
201
205
|
const providerResults = await Promise.all(
|
|
202
206
|
providers.map(async provider => {
|
|
@@ -311,12 +315,13 @@ export async function loadSkills(options: LoadSkillsOptions = {}): Promise<LoadS
|
|
|
311
315
|
customDirectories.map(async dir => {
|
|
312
316
|
const expandedDir = expandTilde(dir, home);
|
|
313
317
|
const scanResult = await scanSkillsFromDir(
|
|
314
|
-
{ cwd, home, repoRoot: null },
|
|
318
|
+
{ cwd, home, repoRoot: null, allowExternalUserSkillSymlinks },
|
|
315
319
|
{
|
|
316
320
|
dir: expandedDir,
|
|
317
321
|
providerId: "custom",
|
|
318
322
|
level: "user",
|
|
319
323
|
requireDescription: true,
|
|
324
|
+
allowExternalUserSkillSymlinks,
|
|
320
325
|
},
|
|
321
326
|
);
|
|
322
327
|
return { expandedDir, scanResult };
|