@gajae-code/coding-agent 0.11.11 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +131 -4
- package/dist/types/async/job-manager.d.ts +17 -3
- package/dist/types/cli/read-cli.d.ts +1 -0
- package/dist/types/commands/deep-interview.d.ts +9 -0
- package/dist/types/commands/harness.d.ts +22 -0
- package/dist/types/commands/read.d.ts +6 -0
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/provider-auth-health.d.ts +14 -0
- package/dist/types/config/provider-ranking.d.ts +59 -0
- package/dist/types/config/settings-schema.d.ts +83 -1
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/exa/mcp-client.d.ts +10 -1
- package/dist/types/exec/bash-executor.d.ts +45 -12
- package/dist/types/extensibility/extensions/types.d.ts +38 -4
- package/dist/types/extensibility/gjc-plugins/index.d.ts +12 -2
- package/dist/types/extensibility/gjc-plugins/installer.d.ts +63 -7
- package/dist/types/extensibility/gjc-plugins/lifecycle-reconciliation.d.ts +55 -0
- package/dist/types/extensibility/gjc-plugins/lifecycle.d.ts +64 -0
- package/dist/types/extensibility/gjc-plugins/runtime-quarantine.d.ts +67 -0
- package/dist/types/extensibility/gjc-plugins/session-validation.d.ts +3 -1
- package/dist/types/extensibility/gjc-plugins/types.d.ts +124 -0
- package/dist/types/extensibility/shared-events.d.ts +6 -0
- package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +5 -12
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +17 -32
- package/dist/types/gjc-runtime/deep-interview-stage.d.ts +37 -0
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +4 -135
- package/dist/types/gjc-runtime/gc-runtime.d.ts +1 -1
- package/dist/types/gjc-runtime/launch-tmux.d.ts +1 -0
- package/dist/types/gjc-runtime/memory-guard-owner-claims.d.ts +47 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +70 -1
- package/dist/types/gjc-runtime/state-writer.d.ts +5 -46
- package/dist/types/gjc-runtime/team-launch.d.ts +1 -1
- package/dist/types/gjc-runtime/team-runtime.d.ts +27 -1
- package/dist/types/gjc-runtime/team-store.d.ts +12 -0
- package/dist/types/gjc-runtime/team-worker-heartbeat.d.ts +43 -0
- package/dist/types/gjc-runtime/team-worker-memory-guard.d.ts +92 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +17 -0
- package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +8 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +31 -80
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +3 -5
- package/dist/types/internal-urls/local-root-gc.d.ts +2 -0
- package/dist/types/main.d.ts +1 -1
- package/dist/types/memories/index.d.ts +2 -0
- package/dist/types/modes/acp/acp-agent.d.ts +10 -19
- package/dist/types/modes/components/assistant-message.d.ts +3 -1
- package/dist/types/modes/components/gjc-bundle-settings.d.ts +34 -0
- package/dist/types/modes/components/irc-sidebar.d.ts +41 -0
- package/dist/types/modes/components/notifications-settings-editor.d.ts +1 -0
- package/dist/types/modes/components/read-tool-group.d.ts +4 -4
- package/dist/types/modes/components/session-observer-overlay.d.ts +22 -0
- package/dist/types/modes/components/settings-selector.d.ts +11 -0
- package/dist/types/modes/components/status-line/gh.d.ts +5 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -0
- package/dist/types/modes/components/transcript-viewer-overlay.d.ts +18 -1
- package/dist/types/modes/controllers/event-controller.d.ts +3 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/irc-observation-ledger.d.ts +17 -1
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +16 -0
- package/dist/types/reminders/star-reminder.d.ts +3 -13
- package/dist/types/runtime-mcp/manager.d.ts +1 -0
- package/dist/types/runtime-mcp/smithery-auth.d.ts +5 -0
- package/dist/types/sdk/acp/adapter.d.ts +3 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +5 -0
- package/dist/types/sdk/broker/transport.d.ts +2 -0
- package/dist/types/sdk/bus/attachment-registry.d.ts +1 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +4 -1
- package/dist/types/sdk/bus/config.d.ts +6 -2
- package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
- package/dist/types/sdk/bus/index.d.ts +44 -1
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +44 -0
- package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +4 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +2 -1
- package/dist/types/sdk/bus/reconciliation-store.d.ts +69 -0
- package/dist/types/sdk/bus/rich-draft.d.ts +1 -0
- package/dist/types/sdk/bus/rich-render.d.ts +2 -0
- package/dist/types/sdk/bus/telegram-adoption-intent.d.ts +165 -0
- package/dist/types/sdk/bus/telegram-cli.d.ts +12 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +11 -4
- package/dist/types/sdk/bus/telegram-daemon.d.ts +25 -1
- package/dist/types/sdk/bus/telegram-reference.d.ts +11 -1
- package/dist/types/sdk/bus/threaded-render.d.ts +3 -2
- package/dist/types/sdk/bus/topic-registry.d.ts +11 -1
- package/dist/types/sdk/host/control/operations.d.ts +1 -1
- package/dist/types/sdk/host/host.d.ts +2 -0
- package/dist/types/sdk/host/query/handlers.d.ts +5 -0
- package/dist/types/sdk/host/query/revision-store.d.ts +2 -0
- package/dist/types/sdk/host/reverse-leases.d.ts +1 -1
- package/dist/types/sdk/prompt-status.d.ts +23 -3
- package/dist/types/sdk/session.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +82 -12
- package/dist/types/session/artifacts.d.ts +8 -0
- package/dist/types/session/blob-store.d.ts +57 -4
- package/dist/types/session/internal/managed-session-storage.d.ts +8 -1
- package/dist/types/session/memory-guard-checkpoint-participant.d.ts +69 -0
- package/dist/types/session/session-manager.d.ts +48 -19
- package/dist/types/session/session-storage.d.ts +4 -0
- package/dist/types/session/streaming-output.d.ts +96 -12
- package/dist/types/setup/credential-import.d.ts +12 -0
- package/dist/types/skill-state/active-state.d.ts +12 -4
- package/dist/types/skill-state/workflow-hud.d.ts +9 -0
- package/dist/types/task/executor.d.ts +3 -2
- package/dist/types/task/index.d.ts +3 -1
- package/dist/types/task/provider-retry-status.d.ts +8 -0
- package/dist/types/task/receipt.d.ts +7 -1
- package/dist/types/task/types.d.ts +23 -0
- package/dist/types/tools/ask-answer-registry.d.ts +2 -2
- package/dist/types/tools/bash-interactive.d.ts +4 -1
- package/dist/types/tools/bash.d.ts +3 -1
- package/dist/types/tools/browser/launch.d.ts +7 -0
- package/dist/types/tools/fetch.d.ts +17 -1
- package/dist/types/tools/image-gen.d.ts +4 -0
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/tools/output-meta.d.ts +44 -3
- package/dist/types/tools/path-utils.d.ts +1 -0
- package/dist/types/tools/read-internals.d.ts +12 -0
- package/dist/types/tools/read.d.ts +45 -7
- package/dist/types/tools/resource-gc.d.ts +4 -0
- package/dist/types/tools/review.d.ts +5 -7
- package/dist/types/tools/subagent.d.ts +2 -0
- package/dist/types/tools/tool-result.d.ts +7 -1
- package/dist/types/utils/gh.d.ts +13 -0
- package/dist/types/utils/image-resize.d.ts +1 -0
- package/dist/types/web/search/providers/kimi.d.ts +2 -0
- package/dist/types/web/search/providers/searxng.d.ts +7 -0
- package/dist/types/web/search/providers/xai.d.ts +2 -0
- package/package.json +11 -13
- package/scripts/acp-conformance-agent.ts +218 -0
- package/scripts/benchmark-sticky-viewport-pr1.ts +239 -0
- package/scripts/capture-gjc-bundle-settings.ts +306 -0
- package/scripts/capture-sticky-viewport-showcase.ts +292 -0
- package/scripts/generate-sdk-operation-inventory.ts +5 -0
- package/scripts/resident-memory-bench.ts +1341 -0
- package/scripts/run-acp-conformance.ts +285 -0
- package/scripts/verify-gjc-sdk-canonicalization.ts +122 -18
- package/scripts/verify-sticky-viewport-showcase.ts +611 -0
- package/src/async/job-manager.ts +78 -20
- package/src/capability/index.ts +8 -0
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/plugin-cli.ts +176 -18
- package/src/cli/read-cli.ts +2 -1
- package/src/cli.ts +1 -0
- package/src/commands/deep-interview.ts +29 -9
- package/src/commands/harness.ts +35 -12
- package/src/commands/read.ts +11 -2
- package/src/commands/team.ts +20 -2
- package/src/config/file-lock.ts +11 -2
- package/src/config/model-registry.ts +21 -2
- package/src/config/provider-auth-health.ts +42 -0
- package/src/config/provider-ranking.ts +119 -0
- package/src/config/settings-schema.ts +74 -1
- package/src/config/settings.ts +246 -108
- package/src/coordinator-mcp/question-gate-codec.ts +1 -1
- package/src/coordinator-mcp/server.ts +5 -12
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +230 -82
- package/src/defaults/gjc/skills/ralplan/SKILL.md +77 -41
- package/src/defaults/gjc/skills/team/SKILL.md +3 -1
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +62 -39
- package/src/defaults/gjc/skills/ultragoal/validation-batch-contracts.md +12 -0
- package/src/defaults/gjc-defaults.ts +4 -4
- package/src/eval/py/executor.ts +7 -2
- package/src/exa/mcp-client.ts +12 -3
- package/src/exec/bash-executor.ts +144 -27
- package/src/extensibility/extensions/runner.ts +15 -0
- package/src/extensibility/extensions/types.ts +46 -5
- package/src/extensibility/gjc-plugins/constrained-hooks.ts +8 -2
- package/src/extensibility/gjc-plugins/index.ts +19 -2
- package/src/extensibility/gjc-plugins/installer.ts +189 -50
- package/src/extensibility/gjc-plugins/lifecycle-reconciliation.ts +199 -0
- package/src/extensibility/gjc-plugins/lifecycle.ts +626 -0
- package/src/extensibility/gjc-plugins/observability.ts +3 -3
- package/src/extensibility/gjc-plugins/prompt-appendix.ts +5 -1
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +12 -2
- package/src/extensibility/gjc-plugins/runtime-quarantine.ts +121 -0
- package/src/extensibility/gjc-plugins/schema.ts +84 -12
- package/src/extensibility/gjc-plugins/session-validation.ts +9 -3
- package/src/extensibility/gjc-plugins/types.ts +141 -0
- package/src/extensibility/gjc-plugins/validation.ts +9 -0
- package/src/extensibility/shared-events.ts +5 -0
- package/src/gjc-runtime/deep-interview-ambiguity.ts +47 -82
- package/src/gjc-runtime/deep-interview-recorder.ts +227 -310
- package/src/gjc-runtime/deep-interview-runtime.ts +109 -270
- package/src/gjc-runtime/deep-interview-stage.ts +1016 -0
- package/src/gjc-runtime/deep-interview-state.ts +12 -943
- package/src/gjc-runtime/gc-render.ts +1 -0
- package/src/gjc-runtime/gc-runtime.ts +11 -1
- package/src/gjc-runtime/launch-tmux.ts +25 -6
- package/src/gjc-runtime/managed-owner-admission.ts +1 -1
- package/src/gjc-runtime/memory-guard-owner-claims.ts +389 -0
- package/src/gjc-runtime/ralplan-runtime.ts +1149 -98
- package/src/gjc-runtime/state-runtime.ts +2 -1
- package/src/gjc-runtime/state-writer.ts +70 -222
- package/src/gjc-runtime/team-launch.ts +42 -5
- package/src/gjc-runtime/team-runtime.ts +1085 -40
- package/src/gjc-runtime/team-store.ts +32 -0
- package/src/gjc-runtime/team-worker-heartbeat.ts +149 -0
- package/src/gjc-runtime/team-worker-memory-guard.ts +363 -0
- package/src/gjc-runtime/team-workers.ts +1 -0
- package/src/gjc-runtime/tmux-owner-isolation.ts +28 -0
- package/src/gjc-runtime/tmux-sessions.ts +58 -13
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +20 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +414 -618
- package/src/gjc-runtime/workflow-manifest.generated.json +105 -449
- package/src/gjc-runtime/workflow-manifest.ts +38 -247
- package/src/hooks/native-skill-hook.ts +17 -4
- package/src/hooks/skill-state.ts +1 -23
- package/src/internal-urls/docs-index.generated.ts +21 -22
- package/src/internal-urls/local-root-gc.ts +127 -0
- package/src/main.ts +9 -5
- package/src/memories/index.ts +10 -0
- package/src/modes/DESIGN.md +98 -0
- package/src/modes/acp/acp-agent.ts +339 -191
- package/src/modes/acp/acp-event-mapper.ts +14 -7
- package/src/modes/acp/acp-mode.ts +39 -3
- package/src/modes/components/assistant-message.ts +123 -22
- package/src/modes/components/gjc-bundle-settings.ts +508 -0
- package/src/modes/components/irc-sidebar.ts +176 -62
- package/src/modes/components/model-selector.ts +61 -4
- package/src/modes/components/notifications-settings-editor.ts +12 -1
- package/src/modes/components/oauth-selector.ts +96 -28
- package/src/modes/components/read-tool-group.ts +82 -14
- package/src/modes/components/session-observer-overlay.ts +457 -83
- package/src/modes/components/settings-selector.ts +116 -23
- package/src/modes/components/status-line/gh.ts +16 -0
- package/src/modes/components/tool-execution.ts +138 -27
- package/src/modes/components/tool-status-header.ts +3 -14
- package/src/modes/components/transcript-viewer-overlay.ts +99 -24
- package/src/modes/controllers/command-controller.ts +4 -0
- package/src/modes/controllers/event-controller.ts +109 -27
- package/src/modes/controllers/extension-ui-controller.ts +26 -2
- package/src/modes/controllers/input-controller.ts +18 -3
- package/src/modes/controllers/selector-controller.ts +127 -36
- package/src/modes/interactive-mode.ts +62 -3
- package/src/modes/irc-observation-ledger.ts +71 -15
- package/src/modes/runtime-init.ts +22 -2
- package/src/modes/types.ts +2 -0
- package/src/modes/utils/ui-helpers.ts +94 -1
- package/src/prompts/agent-fragments/ralplan-persistence.md +4 -3
- package/src/prompts/agents/architect.md +9 -1
- package/src/prompts/agents/critic.md +9 -0
- package/src/prompts/system/eager-todo.md +8 -4
- package/src/prompts/system/system-prompt.md +11 -5
- package/src/prompts/tools/bash.md +5 -5
- package/src/prompts/tools/read.md +4 -2
- package/src/prompts/tools/search-tool-bm25.md +5 -0
- package/src/prompts/tools/task.md +1 -1
- package/src/reminders/star-reminder.ts +4 -42
- package/src/runtime-mcp/manager.ts +19 -4
- package/src/runtime-mcp/smithery-auth.ts +17 -2
- package/src/runtime-mcp/smithery-connect.ts +11 -1
- package/src/sdk/acp/adapter.ts +22 -6
- package/src/sdk/broker/lifecycle.ts +25 -6
- package/src/sdk/broker/transport.ts +35 -3
- package/src/sdk/bus/attachment-registry.ts +5 -1
- package/src/sdk/bus/chat-daemon-cli.ts +4 -39
- package/src/sdk/bus/chat-daemon-control.ts +6 -3
- package/src/sdk/bus/config.ts +18 -2
- package/src/sdk/bus/control-drain-lease.ts +104 -0
- package/src/sdk/bus/index.ts +984 -146
- package/src/sdk/bus/kind-aware-reconciliation.ts +317 -0
- package/src/sdk/bus/lifecycle-commands.ts +4 -1
- package/src/sdk/bus/lifecycle-control-runtime.ts +403 -88
- package/src/sdk/bus/prompt-reconciliation.ts +2 -1
- package/src/sdk/bus/reconciliation-store.ts +303 -0
- package/src/sdk/bus/rich-draft.ts +1 -1
- package/src/sdk/bus/rich-render.ts +2 -2
- package/src/sdk/bus/telegram-adoption-intent.ts +574 -0
- package/src/sdk/bus/telegram-cli.ts +33 -8
- package/src/sdk/bus/telegram-daemon-cli.ts +1 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +11 -4
- package/src/sdk/bus/telegram-daemon.ts +916 -23
- package/src/sdk/bus/telegram-reference.ts +40 -3
- package/src/sdk/bus/threaded-render.ts +48 -3
- package/src/sdk/bus/topic-registry.ts +20 -0
- package/src/sdk/host/control/dispatch.ts +5 -1
- package/src/sdk/host/control/operations.ts +1 -1
- package/src/sdk/host/host.ts +23 -1
- package/src/sdk/host/query/handlers.ts +55 -0
- package/src/sdk/host/query/revision-store.ts +36 -2
- package/src/sdk/host/reverse-leases.ts +44 -7
- package/src/sdk/mcp/server.ts +46 -18
- package/src/sdk/prompt-status.ts +16 -3
- package/src/sdk/protocol/operation-inventory.generated.json +51 -0
- package/src/sdk/protocol/operation-registry.ts +7 -2
- package/src/sdk/session.ts +101 -10
- package/src/session/agent-session.ts +1286 -220
- package/src/session/artifacts.ts +22 -0
- package/src/session/auth-broker-config.ts +10 -3
- package/src/session/blob-store.ts +915 -24
- package/src/session/internal/managed-session-scope.ts +44 -6
- package/src/session/internal/managed-session-storage.ts +48 -6
- package/src/session/memory-guard-checkpoint-participant.ts +131 -0
- package/src/session/session-manager.ts +2364 -438
- package/src/session/session-storage.ts +9 -1
- package/src/session/streaming-output.ts +500 -111
- package/src/setup/credential-import.ts +75 -13
- package/src/setup/provider-onboarding.ts +19 -4
- package/src/skill-state/active-state.ts +32 -10
- package/src/skill-state/workflow-hud.ts +42 -2
- package/src/skill-state/workflow-mutation-guard.ts +560 -8
- package/src/task/executor.ts +122 -39
- package/src/task/index.ts +23 -14
- package/src/task/provider-retry-status.ts +16 -0
- package/src/task/receipt.ts +23 -7
- package/src/task/render.ts +6 -2
- package/src/task/types.ts +108 -0
- package/src/tools/ask-answer-registry.ts +12 -6
- package/src/tools/ask.ts +169 -118
- package/src/tools/ast-edit.ts +1 -0
- package/src/tools/ast-grep.ts +1 -0
- package/src/tools/bash-interactive.ts +8 -3
- package/src/tools/bash.ts +503 -60
- package/src/tools/browser/launch.ts +41 -6
- package/src/tools/fetch.ts +114 -23
- package/src/tools/find.ts +1 -0
- package/src/tools/image-gen.ts +29 -5
- package/src/tools/index.ts +4 -0
- package/src/tools/output-meta.ts +281 -20
- package/src/tools/path-utils.ts +8 -3
- package/src/tools/read-internals.ts +44 -0
- package/src/tools/read.ts +1560 -310
- package/src/tools/resource-gc.ts +182 -18
- package/src/tools/review.ts +5 -7
- package/src/tools/search.ts +1 -0
- package/src/tools/subagent-render.ts +6 -2
- package/src/tools/subagent.ts +6 -0
- package/src/tools/telegram-send.ts +6 -3
- package/src/tools/tool-result.ts +14 -1
- package/src/utils/gh.ts +39 -0
- package/src/utils/image-resize.ts +3 -0
- package/src/web/search/providers/anthropic.ts +4 -4
- package/src/web/search/providers/codex.ts +1 -1
- package/src/web/search/providers/kimi.ts +15 -5
- package/src/web/search/providers/searxng.ts +47 -4
- package/src/web/search/providers/xai.ts +7 -2
- package/dist/types/gjc-runtime/deep-interview-draft.d.ts +0 -8
- package/dist/types/gjc-runtime/deep-interview-payload.d.ts +0 -41
- package/dist/types/gjc-runtime/deep-interview-repair.d.ts +0 -17
- package/dist/types/gjc-runtime/ultragoal-pipeline.d.ts +0 -23
- package/src/defaults/gjc/skills/ultragoal/pipeline-validation-contracts.md +0 -30
- package/src/gjc-runtime/deep-interview-draft.ts +0 -843
- package/src/gjc-runtime/deep-interview-payload.ts +0 -262
- package/src/gjc-runtime/deep-interview-repair.ts +0 -1674
- package/src/gjc-runtime/ultragoal-pipeline.ts +0 -306
|
@@ -4,6 +4,7 @@ import * as path from "node:path";
|
|
|
4
4
|
import { bindPluginMcpToPublicNetwork } from "../../runtime-mcp/plugin-network-boundary";
|
|
5
5
|
import { loadCustomTools } from "../custom-tools/loader";
|
|
6
6
|
import type { CustomTool } from "../custom-tools/types";
|
|
7
|
+
import { bundleIdentity } from "./lifecycle-reconciliation";
|
|
7
8
|
import { loadEffectiveGjcPluginRegistry, registryPathForScope } from "./registry";
|
|
8
9
|
import { type SessionQuarantine, type SessionValidationResult, validateSessionBundles } from "./session-validation";
|
|
9
10
|
import type { GjcPluginRegistryEntry, GjcPluginScope } from "./types";
|
|
@@ -105,6 +106,7 @@ async function verifyEntryHashesCached(entry: GjcPluginRegistryEntry): Promise<S
|
|
|
105
106
|
const snapshot = await snapshotExistingFile(abs);
|
|
106
107
|
if (!snapshot) {
|
|
107
108
|
return {
|
|
109
|
+
identity: bundleIdentity(entry.scope, entry.name),
|
|
108
110
|
plugin: entry.name,
|
|
109
111
|
surfaceId: `plugin:${entry.name}`,
|
|
110
112
|
code: "runtime_mismatch",
|
|
@@ -113,6 +115,7 @@ async function verifyEntryHashesCached(entry: GjcPluginRegistryEntry): Promise<S
|
|
|
113
115
|
}
|
|
114
116
|
if ((await hashFile(snapshot)) !== file.sha256) {
|
|
115
117
|
return {
|
|
118
|
+
identity: bundleIdentity(entry.scope, entry.name),
|
|
116
119
|
plugin: entry.name,
|
|
117
120
|
surfaceId: `plugin:${entry.name}`,
|
|
118
121
|
code: "runtime_mismatch",
|
|
@@ -186,12 +189,16 @@ export async function loadAlwaysOnPluginTools(input: {
|
|
|
186
189
|
);
|
|
187
190
|
|
|
188
191
|
// Map declared (path -> name) for every active always-on tool surface.
|
|
189
|
-
const declared = new Map<string, { name: string; plugin: string }>();
|
|
192
|
+
const declared = new Map<string, { name: string; plugin: string; scope: GjcPluginScope }>();
|
|
190
193
|
for (const entry of active) {
|
|
191
194
|
const disabled = new Set(entry.disabledSurfaceIds);
|
|
192
195
|
for (const t of entry.surfaces.tools) {
|
|
193
196
|
if (disabled.has(t.extensionId)) continue;
|
|
194
|
-
declared.set(path.join(entry.pluginRoot, t.relativePath), {
|
|
197
|
+
declared.set(path.join(entry.pluginRoot, t.relativePath), {
|
|
198
|
+
name: t.name,
|
|
199
|
+
plugin: entry.name,
|
|
200
|
+
scope: entry.scope,
|
|
201
|
+
});
|
|
195
202
|
}
|
|
196
203
|
}
|
|
197
204
|
if (declared.size === 0) return { tools: [], quarantine };
|
|
@@ -218,6 +225,7 @@ export async function loadAlwaysOnPluginTools(input: {
|
|
|
218
225
|
// Manifest is authoritative: exactly the one declared name must come back.
|
|
219
226
|
if (returned.length !== 1 || returned[0] !== info.name) {
|
|
220
227
|
quarantine.push({
|
|
228
|
+
identity: bundleIdentity(info.scope, info.plugin),
|
|
221
229
|
plugin: info.plugin,
|
|
222
230
|
surfaceId: `tool:${info.name}`,
|
|
223
231
|
code: "runtime_mismatch",
|
|
@@ -228,6 +236,7 @@ export async function loadAlwaysOnPluginTools(input: {
|
|
|
228
236
|
if (seenNames.has(info.name)) {
|
|
229
237
|
// Defense in depth: never overwrite a reserved/earlier name.
|
|
230
238
|
quarantine.push({
|
|
239
|
+
identity: bundleIdentity(info.scope, info.plugin),
|
|
231
240
|
plugin: info.plugin,
|
|
232
241
|
surfaceId: `tool:${info.name}`,
|
|
233
242
|
code: "session_collision",
|
|
@@ -335,6 +344,7 @@ export async function buildPluginMcpConfigs(input: { cwd: string }): Promise<{
|
|
|
335
344
|
}
|
|
336
345
|
} catch (error) {
|
|
337
346
|
quarantine.push({
|
|
347
|
+
identity: bundleIdentity(entry.scope, entry.name),
|
|
338
348
|
plugin: entry.name,
|
|
339
349
|
surfaceId: m.extensionId,
|
|
340
350
|
code: "security_policy",
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { identityEquals, identityKey } from "./lifecycle-reconciliation";
|
|
2
|
+
import type { GjcBundleIdentity, GjcRuntimeFinding, GjcRuntimeSnapshot, GjcRuntimeSnapshotState } from "./types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Deterministic numeric activation generation for an activation fingerprint.
|
|
6
|
+
*
|
|
7
|
+
* Equal activation inputs yield an equal generation, so a consumer holding a
|
|
8
|
+
* snapshot can tell whether it still describes the state it is rendering.
|
|
9
|
+
* Derived from the fingerprint's leading hex so it stays inside the safe
|
|
10
|
+
* integer range.
|
|
11
|
+
*/
|
|
12
|
+
export function gjcActivationGenerationFor(activationFingerprint: string): number {
|
|
13
|
+
const parsed = Number.parseInt(activationFingerprint.slice(0, 13), 16);
|
|
14
|
+
return Number.isSafeInteger(parsed) ? parsed : 0;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Caller-owned accumulator for scope-qualified runtime evidence.
|
|
19
|
+
*
|
|
20
|
+
* Producers (loaders, adapters, validators) hand findings to an accumulator
|
|
21
|
+
* they were given; they never publish. Exactly one coordinator publishes a
|
|
22
|
+
* complete generation snapshot, and consumers merge it only when the identity
|
|
23
|
+
* and generation match.
|
|
24
|
+
*/
|
|
25
|
+
export class GjcRuntimeFindingAccumulator {
|
|
26
|
+
private readonly findings: GjcRuntimeFinding[] = [];
|
|
27
|
+
|
|
28
|
+
constructor(readonly generation: number) {}
|
|
29
|
+
|
|
30
|
+
add(finding: GjcRuntimeFinding): void {
|
|
31
|
+
this.findings.push(finding);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
addAll(findings: readonly GjcRuntimeFinding[]): void {
|
|
35
|
+
for (const finding of findings) this.add(finding);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Sorted, de-duplicated snapshot for the generation this accumulator owns. */
|
|
39
|
+
snapshot(): GjcRuntimeSnapshot {
|
|
40
|
+
const seen = new Set<string>();
|
|
41
|
+
const unique: GjcRuntimeFinding[] = [];
|
|
42
|
+
for (const finding of this.findings) {
|
|
43
|
+
const key = [identityKey(finding.identity), finding.surfaceId, finding.code, finding.message].join("\u0000");
|
|
44
|
+
if (seen.has(key)) continue;
|
|
45
|
+
seen.add(key);
|
|
46
|
+
unique.push(finding);
|
|
47
|
+
}
|
|
48
|
+
unique.sort((a, b) => {
|
|
49
|
+
const ka = `${identityKey(a.identity)}\u0000${a.surfaceId}\u0000${a.code}`;
|
|
50
|
+
const kb = `${identityKey(b.identity)}\u0000${b.surfaceId}\u0000${b.code}`;
|
|
51
|
+
return ka.localeCompare(kb);
|
|
52
|
+
});
|
|
53
|
+
return { generation: this.generation, findings: unique };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Read-only view of the most recently published complete generation. */
|
|
58
|
+
export interface GjcRuntimeSnapshotProvider {
|
|
59
|
+
current(): GjcRuntimeSnapshotState;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Single-writer publisher for runtime evidence.
|
|
64
|
+
*
|
|
65
|
+
* Passes can overlap: the session's prompt rebuild is re-entrant, so a second
|
|
66
|
+
* pass may start while a first is still awaiting its producers. Publication is
|
|
67
|
+
* therefore fenced by a monotonic epoch. A pass reserves an epoch when it
|
|
68
|
+
* begins, which immediately retires whatever was published before, and its
|
|
69
|
+
* later publish is accepted only if no newer pass has reserved since. A slow or
|
|
70
|
+
* failed older pass can never overwrite a newer one, and an incomplete pass
|
|
71
|
+
* simply never publishes, leaving consumers at `unavailable`.
|
|
72
|
+
*/
|
|
73
|
+
export class GjcRuntimeSnapshotStore implements GjcRuntimeSnapshotProvider {
|
|
74
|
+
private state: GjcRuntimeSnapshotState = { status: "unavailable" };
|
|
75
|
+
private epoch = 0;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Begin a pass. Retires the current snapshot and returns the epoch token the
|
|
79
|
+
* caller must present to publish.
|
|
80
|
+
*/
|
|
81
|
+
beginPass(): number {
|
|
82
|
+
this.epoch += 1;
|
|
83
|
+
this.state = { status: "unavailable" };
|
|
84
|
+
return this.epoch;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Publish only if `epoch` is still the newest reserved pass. */
|
|
88
|
+
publish(snapshot: GjcRuntimeSnapshot, epoch?: number): void {
|
|
89
|
+
if (epoch !== undefined && epoch !== this.epoch) return;
|
|
90
|
+
this.state = { status: "current", snapshot };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
invalidate(): void {
|
|
94
|
+
this.state = { status: "unavailable" };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
current(): GjcRuntimeSnapshotState {
|
|
98
|
+
return this.state;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Findings for one bundle, but only when the snapshot is current AND describes
|
|
104
|
+
* the exact generation the consumer is rendering. Missing provider, mismatched
|
|
105
|
+
* generation, or unavailable state resolve to `unavailable` — never to a
|
|
106
|
+
* silently empty "clear" result.
|
|
107
|
+
*/
|
|
108
|
+
export function findingsForBundle(
|
|
109
|
+
provider: GjcRuntimeSnapshotProvider | undefined,
|
|
110
|
+
identity: GjcBundleIdentity,
|
|
111
|
+
expectedGeneration: number,
|
|
112
|
+
): { status: "unavailable" } | { status: "current"; findings: GjcRuntimeFinding[] } {
|
|
113
|
+
if (!provider) return { status: "unavailable" };
|
|
114
|
+
const state = provider.current();
|
|
115
|
+
if (state.status !== "current") return { status: "unavailable" };
|
|
116
|
+
if (state.snapshot.generation !== expectedGeneration) return { status: "unavailable" };
|
|
117
|
+
return {
|
|
118
|
+
status: "current",
|
|
119
|
+
findings: state.snapshot.findings.filter(f => identityEquals(f.identity, identity)),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
type GjcPluginAppendixManifestEntry,
|
|
6
6
|
type GjcPluginHookManifestEntry,
|
|
7
7
|
GjcPluginLoadError,
|
|
8
|
+
type GjcPluginLoadErrorCode,
|
|
8
9
|
type GjcPluginManifest,
|
|
9
10
|
type GjcPluginMcpManifestEntry,
|
|
10
11
|
type GjcPluginMcpTransport,
|
|
@@ -53,6 +54,67 @@ function requireNonEmptyString(value: unknown, field: string, filePath: string):
|
|
|
53
54
|
return value;
|
|
54
55
|
}
|
|
55
56
|
|
|
57
|
+
/**
|
|
58
|
+
* A bundle name is echoed by the CLI, rendered in Settings, and used to derive
|
|
59
|
+
* a directory segment, so it is constrained at the parse boundary rather than
|
|
60
|
+
* sanitized at every display site. Anything outside this set — control or ANSI
|
|
61
|
+
* sequences, path separators, whitespace, or credential-looking text — is
|
|
62
|
+
* rejected before it can ever be stored.
|
|
63
|
+
*/
|
|
64
|
+
function manifestSafeName(
|
|
65
|
+
value: unknown,
|
|
66
|
+
field: string,
|
|
67
|
+
manifestPath: string,
|
|
68
|
+
code: GjcPluginLoadErrorCode = "invalid_manifest",
|
|
69
|
+
): string {
|
|
70
|
+
const name =
|
|
71
|
+
code === "invalid_frontmatter"
|
|
72
|
+
? requireNonEmptyString(value, field, manifestPath)
|
|
73
|
+
: manifestString(value, field, manifestPath);
|
|
74
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/.test(name)) {
|
|
75
|
+
throw new GjcPluginLoadError(
|
|
76
|
+
code,
|
|
77
|
+
`GJC plugin ${field} must be 1-128 characters of letters, digits, dot, underscore, or hyphen (${manifestPath})`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
return name;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Free-form prose that is rendered into prompts or UI. It is not constrained to
|
|
85
|
+
* the identifier grammar, but control and ANSI characters are rejected so a
|
|
86
|
+
* manifest cannot inject escape sequences into a rendered surface.
|
|
87
|
+
*/
|
|
88
|
+
function manifestSafeProse(
|
|
89
|
+
value: unknown,
|
|
90
|
+
field: string,
|
|
91
|
+
manifestPath: string,
|
|
92
|
+
code: GjcPluginLoadErrorCode = "invalid_manifest",
|
|
93
|
+
): string {
|
|
94
|
+
const text = requireNonEmptyString(value, field, manifestPath);
|
|
95
|
+
// C0, DEL, and the C1 block: a single-byte CSI (U+009B) is an escape
|
|
96
|
+
// introducer on its own, so rejecting only C0 leaves the same injection open.
|
|
97
|
+
if (/[\u0000-\u001f\u007f-\u009f]/.test(text)) {
|
|
98
|
+
throw new GjcPluginLoadError(code, `GJC plugin ${field} must not contain control characters (${manifestPath})`);
|
|
99
|
+
}
|
|
100
|
+
return text;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* A version string is rendered next to the bundle name everywhere the bundle
|
|
105
|
+
* appears, so it is constrained to printable version-like characters.
|
|
106
|
+
*/
|
|
107
|
+
function manifestSafeVersion(value: unknown, manifestPath: string): string {
|
|
108
|
+
const version = manifestString(value, "version", manifestPath);
|
|
109
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9._+-]{0,63}$/.test(version)) {
|
|
110
|
+
throw new GjcPluginLoadError(
|
|
111
|
+
"invalid_manifest",
|
|
112
|
+
`GJC plugin version must be 1-64 characters of letters, digits, dot, plus, underscore, or hyphen (${manifestPath})`,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
return version;
|
|
116
|
+
}
|
|
117
|
+
|
|
56
118
|
function manifestString(value: unknown, field: string, manifestPath: string): string {
|
|
57
119
|
if (typeof value !== "string" || value.trim().length === 0) {
|
|
58
120
|
throw new GjcPluginLoadError(
|
|
@@ -109,12 +171,12 @@ function parseTools(value: unknown, manifestPath: string): GjcPluginToolManifest
|
|
|
109
171
|
`Invalid GJC plugin manifest at ${manifestPath}: tools[${index}] must be a string or object`,
|
|
110
172
|
);
|
|
111
173
|
}
|
|
112
|
-
const name =
|
|
174
|
+
const name = manifestSafeName(entry.name, `tools[${index}].name`, manifestPath);
|
|
113
175
|
const path = manifestString(entry.path, `tools[${index}].path`, manifestPath);
|
|
114
176
|
const description =
|
|
115
177
|
entry.description === undefined
|
|
116
178
|
? undefined
|
|
117
|
-
:
|
|
179
|
+
: manifestSafeProse(entry.description, `tools[${index}].description`, manifestPath);
|
|
118
180
|
const sha256 =
|
|
119
181
|
entry.sha256 === undefined ? undefined : manifestString(entry.sha256, `tools[${index}].sha256`, manifestPath);
|
|
120
182
|
return { name, path, description, sha256, surface: "always-on" };
|
|
@@ -130,11 +192,15 @@ function parseHooks(value: unknown, manifestPath: string): GjcPluginHookManifest
|
|
|
130
192
|
`Invalid GJC plugin manifest at ${manifestPath}: hooks[${index}] must be an object`,
|
|
131
193
|
);
|
|
132
194
|
}
|
|
133
|
-
const name =
|
|
134
|
-
|
|
195
|
+
const name = manifestSafeName(entry.name, `hooks[${index}].name`, manifestPath);
|
|
196
|
+
// event/target become part of the hook surface ID
|
|
197
|
+
// (`hook:<event>:<phase>:<target>:<name>`), which is rendered and printed.
|
|
198
|
+
const event = manifestSafeName(entry.event, `hooks[${index}].event`, manifestPath);
|
|
135
199
|
const path = manifestString(entry.path, `hooks[${index}].path`, manifestPath);
|
|
136
200
|
const target =
|
|
137
|
-
entry.target === undefined
|
|
201
|
+
entry.target === undefined
|
|
202
|
+
? undefined
|
|
203
|
+
: manifestSafeName(entry.target, `hooks[${index}].target`, manifestPath);
|
|
138
204
|
let phase: "before" | "after" | undefined;
|
|
139
205
|
if (entry.phase !== undefined) {
|
|
140
206
|
if (entry.phase !== "before" && entry.phase !== "after") {
|
|
@@ -160,7 +226,7 @@ function parseMcps(value: unknown, manifestPath: string): GjcPluginMcpManifestEn
|
|
|
160
226
|
`Invalid GJC plugin manifest at ${manifestPath}: mcps[${index}] must be an object`,
|
|
161
227
|
);
|
|
162
228
|
}
|
|
163
|
-
const name =
|
|
229
|
+
const name = manifestSafeName(entry.name, `mcps[${index}].name`, manifestPath);
|
|
164
230
|
const transport = entry.transport;
|
|
165
231
|
if (typeof transport !== "string" || !MCP_TRANSPORTS.includes(transport as GjcPluginMcpTransport)) {
|
|
166
232
|
throw new GjcPluginLoadError(
|
|
@@ -207,7 +273,7 @@ function parseAppendixEntry(entry: unknown, field: string, manifestPath: string)
|
|
|
207
273
|
`Invalid GJC plugin manifest at ${manifestPath}: ${field} must be an object`,
|
|
208
274
|
);
|
|
209
275
|
}
|
|
210
|
-
const name =
|
|
276
|
+
const name = manifestSafeName(entry.name, `${field}.name`, manifestPath);
|
|
211
277
|
const path = entry.path === undefined ? undefined : manifestString(entry.path, `${field}.path`, manifestPath);
|
|
212
278
|
// Content may be empty/whitespace here; the compiler enforces non-empty and
|
|
213
279
|
// maps emptiness to invalid_appendix (not invalid_manifest).
|
|
@@ -282,8 +348,8 @@ export function parseManifest(raw: unknown, manifestPath: string): GjcPluginMani
|
|
|
282
348
|
);
|
|
283
349
|
}
|
|
284
350
|
|
|
285
|
-
const name =
|
|
286
|
-
const version =
|
|
351
|
+
const name = manifestSafeName(raw.name, "name", manifestPath);
|
|
352
|
+
const version = manifestSafeVersion(raw.version, manifestPath);
|
|
287
353
|
|
|
288
354
|
return {
|
|
289
355
|
name,
|
|
@@ -300,10 +366,16 @@ export function parseManifest(raw: unknown, manifestPath: string): GjcPluginMani
|
|
|
300
366
|
|
|
301
367
|
export function parseSubskillFrontmatter(fm: Record<string, unknown>, filePath: string): SubskillFrontmatter {
|
|
302
368
|
return {
|
|
303
|
-
|
|
369
|
+
// Name and activation_arg become part of the surface ID
|
|
370
|
+
// (`subskill:<parent>:<phase>:<arg>`) and are rendered, so they share the
|
|
371
|
+
// identifier grammar. binds_to and phase are separately checked against
|
|
372
|
+
// the known parent/phase sets. The description is prose and may not be
|
|
373
|
+
// constrained to that grammar, but must not carry control characters into
|
|
374
|
+
// a rendered prompt.
|
|
375
|
+
name: manifestSafeName(fm.name, "name", filePath, "invalid_frontmatter"),
|
|
304
376
|
binds_to: requireNonEmptyString(fm.binds_to, "binds_to", filePath),
|
|
305
377
|
phase: requireNonEmptyString(fm.phase, "phase", filePath),
|
|
306
|
-
activation_arg:
|
|
307
|
-
description:
|
|
378
|
+
activation_arg: manifestSafeName(fm.activation_arg, "activation_arg", filePath, "invalid_frontmatter"),
|
|
379
|
+
description: manifestSafeProse(fm.description, "description", filePath, "invalid_frontmatter"),
|
|
308
380
|
};
|
|
309
381
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import * as fs from "node:fs/promises";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import
|
|
4
|
+
import { bundleIdentity, identityKey } from "./lifecycle-reconciliation";
|
|
5
|
+
import type { GjcBundleIdentity, GjcPluginLoadErrorCode, GjcPluginRegistryEntry } from "./types";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Session-start validation: the registry is the collision authority. Capability
|
|
@@ -22,6 +23,8 @@ export interface SessionCapabilityEvidence {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export interface SessionQuarantine {
|
|
26
|
+
/** Scope-qualified canonical target this finding belongs to. */
|
|
27
|
+
identity: GjcBundleIdentity;
|
|
25
28
|
plugin: string;
|
|
26
29
|
surfaceId: string;
|
|
27
30
|
code: GjcPluginLoadErrorCode;
|
|
@@ -52,6 +55,7 @@ export async function verifyEntryHashes(entry: GjcPluginRegistryEntry): Promise<
|
|
|
52
55
|
buf = await fs.readFile(abs);
|
|
53
56
|
} catch {
|
|
54
57
|
return {
|
|
58
|
+
identity: bundleIdentity(entry.scope, entry.name),
|
|
55
59
|
plugin: entry.name,
|
|
56
60
|
surfaceId: `plugin:${entry.name}`,
|
|
57
61
|
code: "runtime_mismatch",
|
|
@@ -60,6 +64,7 @@ export async function verifyEntryHashes(entry: GjcPluginRegistryEntry): Promise<
|
|
|
60
64
|
}
|
|
61
65
|
if (sha256(buf) !== file.sha256) {
|
|
62
66
|
return {
|
|
67
|
+
identity: bundleIdentity(entry.scope, entry.name),
|
|
63
68
|
plugin: entry.name,
|
|
64
69
|
surfaceId: `plugin:${entry.name}`,
|
|
65
70
|
code: "runtime_mismatch",
|
|
@@ -103,7 +108,7 @@ export function validateSessionBundles(
|
|
|
103
108
|
preQuarantined: readonly SessionQuarantine[] = [],
|
|
104
109
|
): SessionValidationResult {
|
|
105
110
|
const quarantine: SessionQuarantine[] = [...preQuarantined];
|
|
106
|
-
const quarantinedPlugins = new Set(preQuarantined.map(q => q.
|
|
111
|
+
const quarantinedPlugins = new Set(preQuarantined.map(q => identityKey(q.identity)));
|
|
107
112
|
|
|
108
113
|
const seenTools = new Set<string>(evidence.toolNames ?? []);
|
|
109
114
|
const seenMcps = new Set<string>(evidence.mcpNames ?? []);
|
|
@@ -113,12 +118,13 @@ export function validateSessionBundles(
|
|
|
113
118
|
const active: GjcPluginRegistryEntry[] = [];
|
|
114
119
|
for (const entry of entries) {
|
|
115
120
|
if (!entry.enabled) continue; // user-disabled, not an error
|
|
116
|
-
if (quarantinedPlugins.has(entry.name)) continue;
|
|
121
|
+
if (quarantinedPlugins.has(identityKey(bundleIdentity(entry.scope, entry.name)))) continue;
|
|
117
122
|
const surfaces = activeSurfaceIds(entry);
|
|
118
123
|
let collided = false;
|
|
119
124
|
const recordCollision = (surfaceId: string, what: string): void => {
|
|
120
125
|
collided = true;
|
|
121
126
|
quarantine.push({
|
|
127
|
+
identity: bundleIdentity(entry.scope, entry.name),
|
|
122
128
|
plugin: entry.name,
|
|
123
129
|
surfaceId,
|
|
124
130
|
code: "session_collision",
|
|
@@ -291,3 +291,144 @@ export interface GjcPluginRegistry {
|
|
|
291
291
|
* disabledSurfaceIds, and quarantine bookkeeping.
|
|
292
292
|
*/
|
|
293
293
|
export type GjcPluginSurfaceExtensionId = string;
|
|
294
|
+
|
|
295
|
+
/** Canonical GJC bundle identity: kind is fixed, target is (scope, name). */
|
|
296
|
+
export const GJC_BUNDLE_KIND = "gjc-bundle";
|
|
297
|
+
|
|
298
|
+
export interface GjcBundleIdentity {
|
|
299
|
+
kind: typeof GJC_BUNDLE_KIND;
|
|
300
|
+
scope: GjcPluginScope;
|
|
301
|
+
name: string;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/** Source descriptor exposed to CLI/Settings: never carries raw locator secrets. */
|
|
305
|
+
export interface GjcBundleSafeSource {
|
|
306
|
+
kind: GjcPluginSourceKind;
|
|
307
|
+
/** Redacted display locator (host + path only; no userinfo/query/fragment). */
|
|
308
|
+
display: string;
|
|
309
|
+
/** Conservative safe git ref, omitted when the stored value is unsafe. */
|
|
310
|
+
ref?: string;
|
|
311
|
+
/** Hex-only revision identifier, omitted when the stored value is unsafe. */
|
|
312
|
+
sha?: string;
|
|
313
|
+
resolvedAt: string;
|
|
314
|
+
/** True when this source kind supports re-resolution during update. */
|
|
315
|
+
updatable: boolean;
|
|
316
|
+
/** Present only when updatable is false. */
|
|
317
|
+
unsupportedReason?: string;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export interface GjcBundleSurfaceSummary {
|
|
321
|
+
extensionId: string;
|
|
322
|
+
kind: "tool" | "hook" | "mcp" | "system-appendix" | "agent-appendix" | "subskill";
|
|
323
|
+
name: string;
|
|
324
|
+
/** Persisted user intent (registry disabledSurfaceIds). */
|
|
325
|
+
enabled: boolean;
|
|
326
|
+
/** Deterministic quarantine derived from persisted registry state only. */
|
|
327
|
+
quarantined: boolean;
|
|
328
|
+
quarantineCode?: GjcPluginLoadErrorCode;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/** Installed-bundle DTO shared by CLI and Settings. Contains no raw locators. */
|
|
332
|
+
export interface GjcBundleSummary {
|
|
333
|
+
identity: GjcBundleIdentity;
|
|
334
|
+
version: string;
|
|
335
|
+
description?: string;
|
|
336
|
+
enabled: boolean;
|
|
337
|
+
source: GjcBundleSafeSource;
|
|
338
|
+
installedAt: string;
|
|
339
|
+
updatedAt: string;
|
|
340
|
+
manifestHash: string;
|
|
341
|
+
/** Deterministic fingerprint of the exact installed target. */
|
|
342
|
+
targetFingerprint: string;
|
|
343
|
+
surfaces: GjcBundleSurfaceSummary[];
|
|
344
|
+
/** True when any deterministic quarantine blocks enablement. */
|
|
345
|
+
quarantined: boolean;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Host-derived token binding an update preview to the exact candidate, the
|
|
350
|
+
* exact installed baseline, and the deterministic decision context. Apply is a
|
|
351
|
+
* compare-and-swap against all three fingerprints.
|
|
352
|
+
*/
|
|
353
|
+
export interface GjcReviewedUpdateToken {
|
|
354
|
+
identity: GjcBundleIdentity;
|
|
355
|
+
candidateFingerprint: string;
|
|
356
|
+
baselineFingerprint: string;
|
|
357
|
+
decisionContextFingerprint: string;
|
|
358
|
+
reviewedAt: string;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export type GjcLifecycleErrorCode =
|
|
362
|
+
| "already_installed_use_upgrade"
|
|
363
|
+
| "not_installed"
|
|
364
|
+
| "identity_mismatch"
|
|
365
|
+
| "stale_candidate"
|
|
366
|
+
| "stale_baseline"
|
|
367
|
+
| "stale_decision_context"
|
|
368
|
+
| "source_unsupported"
|
|
369
|
+
| "source_unavailable"
|
|
370
|
+
| "quarantined"
|
|
371
|
+
| "surface_unknown"
|
|
372
|
+
| "invalid_target";
|
|
373
|
+
|
|
374
|
+
export interface GjcLifecycleError {
|
|
375
|
+
code: GjcLifecycleErrorCode;
|
|
376
|
+
/** Sanitized operator-facing message; never contains raw locators or causes. */
|
|
377
|
+
message: string;
|
|
378
|
+
/** Safe scoped recovery hint (e.g. the exact command to run instead). */
|
|
379
|
+
recovery?: string;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export type GjcLifecycleResult<T> = { ok: true; value: T } | { ok: false; error: GjcLifecycleError };
|
|
383
|
+
|
|
384
|
+
export interface GjcUpdatePreview {
|
|
385
|
+
identity: GjcBundleIdentity;
|
|
386
|
+
current: GjcBundleSummary;
|
|
387
|
+
candidateVersion: string;
|
|
388
|
+
candidateManifestHash: string;
|
|
389
|
+
/** Surface IDs added, removed, or retained by this candidate. */
|
|
390
|
+
addedSurfaceIds: string[];
|
|
391
|
+
removedSurfaceIds: string[];
|
|
392
|
+
retainedSurfaceIds: string[];
|
|
393
|
+
changed: boolean;
|
|
394
|
+
token: GjcReviewedUpdateToken;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export type GjcUpdateApplyStatus = "updated" | "unchanged";
|
|
398
|
+
|
|
399
|
+
export interface GjcUpdateApplyResult {
|
|
400
|
+
status: GjcUpdateApplyStatus;
|
|
401
|
+
summary: GjcBundleSummary;
|
|
402
|
+
/** Number of filesystem remnants that could not be removed after a successful swap. */
|
|
403
|
+
remnantCount: number;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
export interface GjcInstallResult {
|
|
407
|
+
status: "installed";
|
|
408
|
+
summary: GjcBundleSummary;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export interface GjcToggleResult {
|
|
412
|
+
summary: GjcBundleSummary;
|
|
413
|
+
/** False when the requested state already matched (no persisted mutation). */
|
|
414
|
+
mutated: boolean;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Scope-qualified runtime evidence emitted by producers. Producers never
|
|
419
|
+
* publish; the session coordinator accumulates one complete generation.
|
|
420
|
+
*/
|
|
421
|
+
export interface GjcRuntimeFinding {
|
|
422
|
+
identity: GjcBundleIdentity;
|
|
423
|
+
surfaceId: string;
|
|
424
|
+
code: GjcPluginLoadErrorCode;
|
|
425
|
+
message: string;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
export interface GjcRuntimeSnapshot {
|
|
429
|
+
/** Monotonic activation generation this snapshot describes. */
|
|
430
|
+
generation: number;
|
|
431
|
+
findings: GjcRuntimeFinding[];
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
export type GjcRuntimeSnapshotState = { status: "unavailable" } | { status: "current"; snapshot: GjcRuntimeSnapshot };
|
|
@@ -88,6 +88,15 @@ export function validateInstallPlan(
|
|
|
88
88
|
bundle: NormalizedGjcPluginBundle,
|
|
89
89
|
effectiveEntries: readonly GjcPluginRegistryEntry[],
|
|
90
90
|
): void {
|
|
91
|
+
// Collision universe: the caller passes the effective registry across BOTH
|
|
92
|
+
// scopes, because surface IDs derive from the SURFACE name
|
|
93
|
+
// (`tool:<toolName>`, `mcp:<mcpName>`), not the bundle name. A differently
|
|
94
|
+
// named bundle in the opposite scope can therefore claim the same ID.
|
|
95
|
+
//
|
|
96
|
+
// Entries sharing this bundle's name are excluded in every scope: they are
|
|
97
|
+
// the same logical bundle being installed or replaced, and installing one
|
|
98
|
+
// bundle into both scopes is supported, so its own surfaces must not count
|
|
99
|
+
// as collisions against itself.
|
|
91
100
|
const others = effectiveEntries.filter(e => e.name !== bundle.name);
|
|
92
101
|
|
|
93
102
|
const toolNames = new Set<string>();
|
|
@@ -38,6 +38,9 @@ export interface SessionBeforeSwitchEvent {
|
|
|
38
38
|
targetSessionFile?: string;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/** Origin used when an interactive session selector resumes a session. */
|
|
42
|
+
export const INTERACTIVE_SELECTOR_RESUME_ORIGIN = "interactive_selector_resume";
|
|
43
|
+
|
|
41
44
|
/** Fired after switching to another session */
|
|
42
45
|
export interface SessionSwitchEvent {
|
|
43
46
|
type: "session_switch";
|
|
@@ -45,6 +48,8 @@ export interface SessionSwitchEvent {
|
|
|
45
48
|
reason: "new" | "resume" | "fork";
|
|
46
49
|
/** Session file we came from */
|
|
47
50
|
previousSessionFile: string | undefined;
|
|
51
|
+
/** Optional provenance for this session transition. */
|
|
52
|
+
transition?: { origin: string };
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
/** Fired before branching a session (can be cancelled) */
|