@gajae-code/coding-agent 0.18.7 → 0.18.9
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 +377 -1
- package/dist/types/async/endpoint-id.d.ts +16 -0
- package/dist/types/async/job-manager.d.ts +1 -1
- package/dist/types/async/support.d.ts +0 -14
- 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 +2 -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/commands/mcp-serve.d.ts +4 -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/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 +6 -1
- package/dist/types/discovery/mcp-json.d.ts +1 -0
- package/dist/types/eval/py/kernel.d.ts +18 -0
- package/dist/types/export/html/css-color.d.ts +2 -0
- package/dist/types/export/html/html-export-destination.d.ts +2 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/extensions/function-hooks-internal.d.ts +5 -0
- 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 +4 -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/psmux-detect.d.ts +3 -3
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +0 -5
- package/dist/types/gjc-runtime/sidecar-bootstrap-env.d.ts +6 -0
- 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 +14 -1
- package/dist/types/gjc-runtime/tmux-common.d.ts +8 -5
- 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/config.d.ts +8 -5
- 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/manager.d.ts +2 -1
- package/dist/types/runtime-mcp/mcp-redirect.d.ts +9 -0
- package/dist/types/runtime-mcp/oauth-public-url.d.ts +3 -0
- package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +1 -0
- package/dist/types/runtime-mcp/project-host-values.d.ts +27 -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 +24 -2
- package/dist/types/sdk/broker/doctor-restart.d.ts +5 -0
- package/dist/types/sdk/broker/ensure.d.ts +14 -1
- package/dist/types/sdk/broker/session-index.d.ts +12 -6
- 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/client/client.d.ts +2 -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 +2 -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 +26 -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 +2 -0
- package/dist/types/session/internal/task-artifact-owner-transcript.d.ts +9 -1
- package/dist/types/session/session-manager.d.ts +31 -5
- package/dist/types/session/session-retirement.d.ts +31 -7
- package/dist/types/session/session-storage.d.ts +69 -2
- package/dist/types/session/task-artifact-owner-codec.d.ts +2 -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/skill-state/workflow-mutation-guard.d.ts +3 -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/task/isolation-dir.d.ts +26 -0
- package/dist/types/tools/ast-edit-write-path.d.ts +13 -0
- package/dist/types/tools/bash-skill-urls.d.ts +1 -1
- package/dist/types/tools/browser/cdp-url.d.ts +2 -0
- package/dist/types/tools/github-side-effect.d.ts +22 -0
- package/dist/types/tools/index.d.ts +3 -0
- package/dist/types/tools/monitor-mutation.d.ts +9 -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/verify-managed-empty-stop.ts +28 -0
- package/src/async/endpoint-id.ts +26 -0
- package/src/async/job-manager.ts +247 -132
- package/src/async/support.ts +0 -25
- package/src/capability/index.ts +15 -0
- package/src/capability/mcp.ts +2 -0
- package/src/capability/skill.ts +2 -0
- package/src/capability/types.ts +2 -0
- package/src/cli/auth-broker-cli.ts +1 -1
- package/src/cli/customize-doctor.ts +15 -2
- package/src/cli/doctor/repairs.ts +4 -1
- package/src/cli/doctor-supervisor.ts +3 -0
- package/src/cli/public-command-errors.ts +21 -1
- package/src/cli/public-command-registry.ts +28 -6
- package/src/cli/setup-cli.ts +4 -4
- package/src/cli/skills-cli.ts +5 -0
- package/src/commands/deep-interview.ts +47 -8
- package/src/commands/mcp-serve.ts +7 -0
- package/src/commands/sdk.ts +455 -397
- package/src/config/autorouting-tier-map.ts +117 -7
- package/src/config/file-lock.ts +316 -79
- 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/skill-settings-defaults.ts +7 -0
- package/src/coordinator-mcp/policy.ts +3 -2
- package/src/coordinator-mcp/server.ts +2 -0
- 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 +4 -13
- package/src/dap/config.ts +2 -2
- 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/register.ts +10 -6
- 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 +13 -1
- package/src/discovery/helpers.ts +171 -29
- package/src/discovery/mcp-json.ts +26 -12
- package/src/discovery/ssh.ts +1 -1
- package/src/discovery/substitute-plugin-root.ts +3 -1
- package/src/edit/file-read-cache.ts +1 -1
- package/src/eval/py/executor.ts +796 -154
- package/src/eval/py/kernel.ts +79 -13
- package/src/eval/py/tool-bridge.ts +4 -0
- package/src/export/html/css-color.ts +9 -0
- package/src/export/html/html-export-destination.ts +16 -0
- package/src/export/html/index.ts +7 -10
- package/src/export/html/template.generated.ts +1 -1
- package/src/extensibility/extensions/function-hooks-internal.ts +27 -0
- package/src/extensibility/extensions/runner.ts +15 -1
- package/src/extensibility/extensions/types.ts +7 -0
- package/src/extensibility/gjc-plugins/mcp-policy.ts +2 -1
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +85 -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 +9 -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 -9
- package/src/gjc-runtime/launch-worktree.ts +70 -9
- package/src/gjc-runtime/psmux-detect.ts +28 -4
- package/src/gjc-runtime/ralplan-runtime.ts +447 -52
- package/src/gjc-runtime/session-state-sidecar.ts +3 -2
- package/src/gjc-runtime/sidecar-bootstrap-env.ts +19 -0
- 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 +105 -16
- package/src/gjc-runtime/tmux-common.ts +8 -5
- 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/internal-urls/docs-index.generated.ts +20 -19
- package/src/internal-urls/skill-path.ts +19 -0
- package/src/internal-urls/skill-protocol.ts +2 -0
- package/src/lsp/config.ts +12 -63
- package/src/lsp/edits.ts +6 -0
- package/src/lsp/workspace-path.ts +51 -0
- package/src/main.ts +195 -31
- package/src/memories/index.ts +88 -7
- package/src/modes/acp/acp-agent.ts +88 -4
- 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/hook-editor.ts +1 -1
- package/src/modes/components/hook-selector.ts +1 -1
- 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 +76 -31
- package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -16
- package/src/modes/controllers/selector-controller.ts +24 -15
- package/src/modes/controllers/todo-command-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +5 -1
- 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 +4 -2
- package/src/runtime-mcp/config-writer.ts +10 -6
- package/src/runtime-mcp/config.ts +1 -0
- package/src/runtime-mcp/json-rpc.ts +2 -1
- package/src/runtime-mcp/manager.ts +29 -11
- package/src/runtime-mcp/mcp-redirect.ts +73 -0
- package/src/runtime-mcp/oauth-flow.ts +14 -2
- package/src/runtime-mcp/oauth-public-url.ts +14 -0
- package/src/runtime-mcp/plugin-network-boundary.ts +4 -0
- package/src/runtime-mcp/pool.ts +6 -0
- package/src/runtime-mcp/project-host-values.ts +46 -0
- package/src/runtime-mcp/smithery-registry.ts +22 -3
- package/src/runtime-mcp/transports/http.ts +4 -3
- package/src/runtime-mcp/types.ts +6 -0
- package/src/sdk/acp/adapter.ts +95 -8
- package/src/sdk/broker/broker-exit.ts +52 -23
- package/src/sdk/broker/broker.ts +81 -6
- package/src/sdk/broker/doctor-restart.ts +4 -0
- package/src/sdk/broker/ensure.ts +31 -2
- package/src/sdk/broker/lifecycle.ts +711 -66
- package/src/sdk/broker/session-index.ts +191 -81
- package/src/sdk/bus/chat-daemon-cli.ts +38 -1
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/index.ts +238 -43
- package/src/sdk/bus/telegram-daemon-contract.ts +6 -1
- package/src/sdk/cli/session-cli.ts +17 -3
- package/src/sdk/client/client.ts +28 -8
- package/src/sdk/host/query/handlers.ts +9 -1
- package/src/sdk/host/session-runtime.test.ts +2705 -303
- package/src/sdk/host/session-runtime.ts +490 -143
- 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/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 +82 -18
- package/src/sdk/session-list.ts +1 -0
- package/src/sdk/session.ts +69 -45
- package/src/session/agent-session.ts +679 -238
- package/src/session/artifacts.ts +176 -71
- package/src/session/internal/managed-gc-retirement-codec.ts +319 -0
- package/src/session/internal/managed-session-scope.ts +3779 -462
- package/src/session/internal/managed-session-storage.ts +501 -78
- package/src/session/internal/managed-task-owner-cleanup.ts +473 -0
- package/src/session/internal/task-artifact-owner-access.ts +19 -3
- package/src/session/internal/task-artifact-owner-transcript.ts +547 -1
- package/src/session/session-manager.ts +951 -133
- package/src/session/session-retirement.ts +573 -14
- package/src/session/session-storage.ts +447 -15
- package/src/session/task-artifact-owner-codec.ts +71 -8
- package/src/session/task-artifact-owner-retirement.ts +21 -19
- 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 +145 -2
- package/src/skill-state/workflow-mutation-guard.ts +102 -10
- package/src/slash-commands/builtin-registry.ts +39 -9
- package/src/slash-commands/helpers/mcp.ts +8 -4
- 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/task/isolation-dir.ts +143 -0
- package/src/task/worktree.ts +16 -7
- package/src/tools/ask.ts +205 -6
- package/src/tools/ast-edit-write-path.ts +68 -0
- package/src/tools/ast-edit.ts +10 -1
- 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/browser/cdp-url.ts +18 -0
- package/src/tools/browser.ts +8 -1
- package/src/tools/descriptors.ts +5 -1
- package/src/tools/eval.ts +56 -36
- package/src/tools/gh.ts +10 -0
- package/src/tools/github-side-effect.ts +112 -0
- package/src/tools/image-gen.ts +1 -1
- package/src/tools/index.ts +3 -0
- package/src/tools/monitor-mutation.ts +27 -0
- package/src/tools/monitor.ts +7 -0
- package/src/tools/python.ts +188 -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/external-editor.ts +3 -2
- package/src/utils/tools-checksum.ts +34 -0
- package/src/utils/tools-manager.ts +24 -6
- package/src/web/insane/url-guard.ts +2 -1
- package/src/web/search/providers/codex.ts +3 -2
|
@@ -11,10 +11,13 @@ import { resolveGjcSessionForRead, SessionResolutionError } from "../gjc-runtime
|
|
|
11
11
|
import {
|
|
12
12
|
type ActiveSessionScope,
|
|
13
13
|
type GuardedWriteResult,
|
|
14
|
+
persistedStateRevision,
|
|
14
15
|
readActiveEntries,
|
|
16
|
+
readActiveEntriesStrict,
|
|
15
17
|
rebuildActiveSnapshot,
|
|
16
18
|
removeActiveEntry,
|
|
17
19
|
setActiveStateCacheInvalidator,
|
|
20
|
+
workflowEnvelopeChecksumStatus,
|
|
18
21
|
writeActiveEntry,
|
|
19
22
|
} from "../gjc-runtime/state-writer";
|
|
20
23
|
import { getSkillManifest } from "../gjc-runtime/workflow-manifest";
|
|
@@ -517,6 +520,18 @@ async function readModeStatePhase(
|
|
|
517
520
|
const phase = safeString(record.current_phase).trim();
|
|
518
521
|
if (!phase) return undefined;
|
|
519
522
|
if (record.active === false && !getSkillManifest("ralplan").canonicalOverrides.includes(phase)) return undefined;
|
|
523
|
+
const claimedSession = safeString(record.session_id).trim();
|
|
524
|
+
if (claimedSession && claimedSession !== sessionId) return undefined;
|
|
525
|
+
// A mode-state phase may replace the active entry only when the writer
|
|
526
|
+
// stamp still matches. A checksum mismatch is an out-of-band edit. An
|
|
527
|
+
// unsigned releasing phase is not a seal and must not paint "complete"
|
|
528
|
+
// onto a planning entry. Non-releasing canonical phases (final, handoff)
|
|
529
|
+
// still overlay, and a matching stamp still overlays a releasing phase.
|
|
530
|
+
const checksum = workflowEnvelopeChecksumStatus(record);
|
|
531
|
+
if (checksum === "mismatch") return undefined;
|
|
532
|
+
if (checksum === "absent" && getSkillManifest(skill).stopReleasingPhases.includes(phase.trim().toLowerCase())) {
|
|
533
|
+
return undefined;
|
|
534
|
+
}
|
|
520
535
|
return phase;
|
|
521
536
|
} catch {
|
|
522
537
|
return undefined;
|
|
@@ -667,13 +682,17 @@ async function mergeVisibleEntries(
|
|
|
667
682
|
cwd: string,
|
|
668
683
|
sessionState: SkillActiveState | null,
|
|
669
684
|
sessionId: string,
|
|
685
|
+
perSkillEntries?: readonly SkillActiveEntry[],
|
|
670
686
|
): Promise<SkillActiveEntry[]> {
|
|
671
687
|
// Use the raw (active + inactive) rows so a handoff demotion stays visible
|
|
672
688
|
// long enough to supersede a stale same-skill row before the active filter.
|
|
673
689
|
// Per-skill files in active/<skill>.json are authoritative and are merged
|
|
674
690
|
// after the derived snapshot cache, so a stale skill-active-state.json row
|
|
675
691
|
// cannot override the latest entry file.
|
|
676
|
-
const entries = [
|
|
692
|
+
const entries = [
|
|
693
|
+
...rawActiveEntries(sessionState),
|
|
694
|
+
...(perSkillEntries ?? (await readActiveEntries(cwd, { sessionId }))),
|
|
695
|
+
];
|
|
677
696
|
const merged = new Map(entries.map(entry => [entryKey(entry), entry]));
|
|
678
697
|
const canonicalRalplanPhase = await readModeStatePhase(cwd, sessionId, "ralplan");
|
|
679
698
|
const visibleEntries = dedupeVisibleBySkill([...merged.values()], sessionId)
|
|
@@ -788,7 +807,16 @@ async function readVisibleSkillActiveStateUncached(
|
|
|
788
807
|
): Promise<SkillActiveState | null> {
|
|
789
808
|
const { sessionPath } = getSkillActiveStatePaths(cwd, resolvedSessionId);
|
|
790
809
|
const sessionState = await readRawActiveStateForHandoff(sessionPath, false);
|
|
791
|
-
|
|
810
|
+
return await buildVisibleSkillActiveState(cwd, sessionState, resolvedSessionId);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
async function buildVisibleSkillActiveState(
|
|
814
|
+
cwd: string,
|
|
815
|
+
sessionState: SkillActiveState | null,
|
|
816
|
+
resolvedSessionId: string,
|
|
817
|
+
perSkillEntries?: readonly SkillActiveEntry[],
|
|
818
|
+
): Promise<SkillActiveState | null> {
|
|
819
|
+
const activeSkills = await mergeVisibleEntries(cwd, sessionState, resolvedSessionId, perSkillEntries);
|
|
792
820
|
if (activeSkills.length === 0) return null;
|
|
793
821
|
const primary = activeSkills[0];
|
|
794
822
|
return {
|
|
@@ -805,6 +833,121 @@ async function readVisibleSkillActiveStateUncached(
|
|
|
805
833
|
|
|
806
834
|
setActiveStateCacheInvalidator(invalidateVisibleSkillActiveStateCache);
|
|
807
835
|
|
|
836
|
+
/**
|
|
837
|
+
* Outcome of a failure-observable workflow-state read:
|
|
838
|
+
* - `absent`: neither a session snapshot nor a per-skill entry is recorded.
|
|
839
|
+
* - `read`: every recorded source was read and no snapshot row lacks authority.
|
|
840
|
+
* - `recovered`: the session snapshot was unreadable, but authoritative per-skill
|
|
841
|
+
* entries (which the snapshot is rebuilt from) supplied the visible state.
|
|
842
|
+
* - `discarded`: the snapshot is a derived cache that still lists active workflows
|
|
843
|
+
* whose per-skill entry is gone or inactive (e.g. a deactivation whose snapshot
|
|
844
|
+
* rebuild never ran); those stale rows were dropped and the visible state comes
|
|
845
|
+
* from the per-skill entries alone.
|
|
846
|
+
* - `unresolved`: the snapshot lists active workflows without an active per-skill
|
|
847
|
+
* entry, but it cannot be shown to be a derived cache, so those rows may be legacy
|
|
848
|
+
* authority or stale cache; they are excluded without confirming their absence.
|
|
849
|
+
* - `failed`: the session snapshot was unreadable and no per-skill entry could
|
|
850
|
+
* replace it (e.g. a corrupt legacy root-only record), so the state is unknown.
|
|
851
|
+
*/
|
|
852
|
+
export type VisibleSkillActiveStateReadStatus = "absent" | "read" | "recovered" | "discarded" | "unresolved" | "failed";
|
|
853
|
+
|
|
854
|
+
export interface VisibleSkillActiveStateRead {
|
|
855
|
+
status: VisibleSkillActiveStateReadStatus;
|
|
856
|
+
/** Visible active state; `null` when nothing is active or the read failed. */
|
|
857
|
+
state: SkillActiveState | null;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/** Strict snapshot read: `undefined` when absent; throws on I/O errors, invalid JSON, or a non-object record. */
|
|
861
|
+
async function readSessionSnapshotStrict(filePath: string): Promise<SkillActiveState | undefined> {
|
|
862
|
+
let raw: string;
|
|
863
|
+
try {
|
|
864
|
+
raw = await Bun.file(filePath).text();
|
|
865
|
+
} catch (error) {
|
|
866
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return undefined;
|
|
867
|
+
throw error;
|
|
868
|
+
}
|
|
869
|
+
const parsed: unknown = JSON.parse(raw);
|
|
870
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
871
|
+
throw new Error(`skill-active-state at ${filePath} is not a JSON object`);
|
|
872
|
+
return parsed as SkillActiveState;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* Whether a readable session snapshot can carry workflow authority of its own.
|
|
877
|
+
*
|
|
878
|
+
* - `legacy`: a root-only record (no `active_skills` array). The derived-cache
|
|
879
|
+
* writers (`rebuildActiveSnapshot` and the activation seed) always persist an
|
|
880
|
+
* `active_skills` array, so this shape predates per-skill entries and its row
|
|
881
|
+
* stays visible.
|
|
882
|
+
* - `derived`: an `active_skills` snapshot stamped by the guarded state writer.
|
|
883
|
+
* Every stamped producer persists the per-skill entry before the snapshot, so
|
|
884
|
+
* the per-skill entries are the only authority for its rows.
|
|
885
|
+
* - `indeterminate`: an unstamped `active_skills` snapshot. Unstamped derived
|
|
886
|
+
* rebuilds and unstamped authoritative activation seeds were both written before
|
|
887
|
+
* the guarded writer, so the record cannot establish which one it is.
|
|
888
|
+
*/
|
|
889
|
+
function classifySnapshotAuthority(snapshot: SkillActiveState): "legacy" | "derived" | "indeterminate" {
|
|
890
|
+
if (!Array.isArray(snapshot.active_skills)) return "legacy";
|
|
891
|
+
return persistedStateRevision(snapshot) > 0 ? "derived" : "indeterminate";
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
/**
|
|
895
|
+
* Uncached, failure-observable counterpart of `readVisibleSkillActiveState` for
|
|
896
|
+
* read-only projections that must not present unreadable state as absence. The
|
|
897
|
+
* tolerant reader keeps mapping an unreadable session snapshot to `null` for
|
|
898
|
+
* HUD and guard callers; this variant reports `failed` or `recovered` instead.
|
|
899
|
+
*
|
|
900
|
+
* Unlike the tolerant reader, a snapshot row never survives without authority: an
|
|
901
|
+
* active row with no active per-skill entry is dropped as stale (`discarded`) when
|
|
902
|
+
* the snapshot is provably derived, or excluded as `unresolved` when it is not.
|
|
903
|
+
* Only a legacy root-only record keeps its own row. Per-skill entry read failures
|
|
904
|
+
* propagate, as they do from the tolerant reader, and so does an entry file that
|
|
905
|
+
* exists but holds no usable skill entry (e.g. `{}`): the tolerant reader skips
|
|
906
|
+
* it, which would make it indistinguishable from a deleted entry and let it pass
|
|
907
|
+
* as proof that a snapshot row is stale. The read never repairs state.
|
|
908
|
+
*/
|
|
909
|
+
export async function readVisibleSkillActiveStateWithStatus(
|
|
910
|
+
cwd: string,
|
|
911
|
+
sessionId: string,
|
|
912
|
+
): Promise<VisibleSkillActiveStateRead> {
|
|
913
|
+
const resolvedCwd = path.resolve(cwd);
|
|
914
|
+
const { sessionPath } = getSkillActiveStatePaths(resolvedCwd, sessionId);
|
|
915
|
+
let sessionState: SkillActiveState | undefined;
|
|
916
|
+
let snapshotUnreadable = false;
|
|
917
|
+
try {
|
|
918
|
+
sessionState = await readSessionSnapshotStrict(sessionPath);
|
|
919
|
+
} catch {
|
|
920
|
+
snapshotUnreadable = true;
|
|
921
|
+
}
|
|
922
|
+
const perSkillEntries = await readActiveEntriesStrict(resolvedCwd, { sessionId });
|
|
923
|
+
const fromEntries = () => buildVisibleSkillActiveState(resolvedCwd, null, sessionId, perSkillEntries);
|
|
924
|
+
if (snapshotUnreadable) {
|
|
925
|
+
if (perSkillEntries.length === 0) return { status: "failed", state: null };
|
|
926
|
+
return { status: "recovered", state: await fromEntries() };
|
|
927
|
+
}
|
|
928
|
+
if (sessionState === undefined) {
|
|
929
|
+
return perSkillEntries.length === 0
|
|
930
|
+
? { status: "absent", state: null }
|
|
931
|
+
: { status: "read", state: await fromEntries() };
|
|
932
|
+
}
|
|
933
|
+
const authority = classifySnapshotAuthority(sessionState);
|
|
934
|
+
if (authority === "legacy") {
|
|
935
|
+
return {
|
|
936
|
+
status: "read",
|
|
937
|
+
state: await buildVisibleSkillActiveState(resolvedCwd, sessionState, sessionId, perSkillEntries),
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
const activeEntrySkills = new Set(
|
|
941
|
+
perSkillEntries.filter(entry => entry.active !== false).map(entry => safeString(entry.skill).trim()),
|
|
942
|
+
);
|
|
943
|
+
const hasUnbackedRow = rawActiveEntries(sessionState).some(
|
|
944
|
+
entry => entry.active !== false && !activeEntrySkills.has(entry.skill),
|
|
945
|
+
);
|
|
946
|
+
const state = await fromEntries();
|
|
947
|
+
if (!hasUnbackedRow) return { status: "read", state };
|
|
948
|
+
return { status: authority === "derived" ? "discarded" : "unresolved", state };
|
|
949
|
+
}
|
|
950
|
+
|
|
808
951
|
export async function readVisibleSkillActiveState(
|
|
809
952
|
cwd: string,
|
|
810
953
|
sessionId?: string,
|
|
@@ -8,8 +8,10 @@ import { expandApplyPatchToEntries } from "../edit/modes/apply-patch";
|
|
|
8
8
|
import { GJC_SESSION_PREFIX, modeStatePath as sessionModeStatePath } from "../gjc-runtime/session-layout";
|
|
9
9
|
import { resolveGjcSessionForRead } from "../gjc-runtime/session-resolution";
|
|
10
10
|
import { ModeStateSchema } from "../gjc-runtime/state-schema";
|
|
11
|
+
import { workflowEnvelopeChecksumStatus } from "../gjc-runtime/state-writer";
|
|
11
12
|
import { getSkillManifest } from "../gjc-runtime/workflow-manifest";
|
|
12
13
|
import { LocalProtocolHandler, resolveLocalUrlToPath } from "../internal-urls/local-protocol";
|
|
14
|
+
import { isGithubReadOnlyArgs } from "../tools/github-side-effect";
|
|
13
15
|
import { resolveToCwd } from "../tools/path-utils";
|
|
14
16
|
import { ToolError } from "../tools/tool-errors";
|
|
15
17
|
import { listActiveSkills, readVisibleSkillActiveState, type SkillActiveEntry } from "./active-state";
|
|
@@ -38,7 +40,14 @@ function planningPhaseBlockMessage(skill: CanonicalGjcWorkflowSkill): string {
|
|
|
38
40
|
return DEEP_INTERVIEW_MUTATION_BLOCK_MESSAGE;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
|
|
43
|
+
/** Tools whose execute path can mutate product state. `github` is included so side-effect ops share this guard. */
|
|
44
|
+
export const WORKFLOW_MUTATION_TOOL_NAMES = ["edit", "write", "ast_edit", "bash", "github"] as const;
|
|
45
|
+
|
|
46
|
+
const BLOCKED_TOOL_NAMES = new Set<string>(WORKFLOW_MUTATION_TOOL_NAMES);
|
|
47
|
+
|
|
48
|
+
export function isWorkflowMutationTool(name: string): boolean {
|
|
49
|
+
return BLOCKED_TOOL_NAMES.has(name);
|
|
50
|
+
}
|
|
42
51
|
/**
|
|
43
52
|
* Only `/dev/null` is exempt. `/dev/stdout`, `/dev/stderr`, and `/dev/fd/<n>` are descriptor
|
|
44
53
|
* aliases: `exec 1<>src/product.ts; printf x >/dev/stdout` reaches a real repository file
|
|
@@ -317,10 +326,20 @@ function resolveCurrentWorkflowEntry(entries: SkillActiveEntry[], topLevelSkill:
|
|
|
317
326
|
* governs, so a stale planning entry can never block while an executor runs and
|
|
318
327
|
* a resumed planning phase reliably re-blocks.
|
|
319
328
|
*
|
|
320
|
-
* Fail-open contract: a missing or invalid durable mode-state releases
|
|
321
|
-
* (a corrupt state file must not lock all mutation), matching the
|
|
322
|
-
* historical behavior — this is intentionally looser than the Stop
|
|
323
|
-
* fails closed for handoff-required skills.
|
|
329
|
+
* Fail-open contract: a missing or schema-invalid durable mode-state releases
|
|
330
|
+
* the block (a corrupt state file must not lock all mutation), matching the
|
|
331
|
+
* guard's historical behavior — this is intentionally looser than the Stop
|
|
332
|
+
* hook, which fails closed for handoff-required skills. A checksum mismatch is
|
|
333
|
+
* not that case: the file is still schema-valid, and the CLI already refuses
|
|
334
|
+
* it as an out-of-band edit. The guard keeps the active entry's phase instead
|
|
335
|
+
* of letting the forged `current_phase` or `active: false` release the block.
|
|
336
|
+
* An unsigned envelope may restate that entry, but it may not release a block
|
|
337
|
+
* the entry still holds. It may still name a stricter blocking phase: the
|
|
338
|
+
* pre-fix guard honored that phase, and dropping it would allow an edit the
|
|
339
|
+
* base rejected. A `session_id` or `thread_id` that does not match this
|
|
340
|
+
* session is the same kind of claim: it does not make the file someone else's,
|
|
341
|
+
* and it does not open the block. A matching `content_sha256` whose context
|
|
342
|
+
* also matches is the existing writer stamp and is trusted.
|
|
324
343
|
*/
|
|
325
344
|
async function getActivePlanningSkill(
|
|
326
345
|
cwd: string,
|
|
@@ -343,11 +362,73 @@ async function getActivePlanningSkill(
|
|
|
343
362
|
const modeState = guardContext.modeStates.get(current.skill) ?? null;
|
|
344
363
|
|
|
345
364
|
if (!modeState) return null;
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
365
|
+
const activePhase = String(current.phase ?? "").trim();
|
|
366
|
+
const posture = planningPostureFromModeState(current.skill, activePhase, modeState, resolvedSessionId, threadId);
|
|
367
|
+
if (posture.tampered) {
|
|
368
|
+
warnInvalidModeState(
|
|
369
|
+
modeStatePath(cwd, current.skill, resolvedSessionId),
|
|
370
|
+
"out-of-band edit detected (content_sha256 mismatch)",
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
if (!posture.blocking) return null;
|
|
374
|
+
return { skill: current.skill, phase: posture.phase };
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Decide whether `modeState` may release the planning block.
|
|
379
|
+
*
|
|
380
|
+
* The active entry is the seal. A mode-state file releases that seal only when
|
|
381
|
+
* its writer-stamped `content_sha256` matches and its session context matches,
|
|
382
|
+
* or when an unsigned file in the same context does not claim a looser posture
|
|
383
|
+
* than the entry. An unsigned file may still impose a stricter blocking phase.
|
|
384
|
+
* Checksum mismatch ignores the file. A context mismatch does not release the
|
|
385
|
+
* entry's block: the file was read from this session's path.
|
|
386
|
+
*/
|
|
387
|
+
function planningPostureFromModeState(
|
|
388
|
+
skill: MutationGatedSkill,
|
|
389
|
+
activePhase: string,
|
|
390
|
+
modeState: ModeState,
|
|
391
|
+
sessionId?: string,
|
|
392
|
+
threadId?: string,
|
|
393
|
+
): { blocking: boolean; phase: string; tampered: boolean } {
|
|
394
|
+
const status = workflowEnvelopeChecksumStatus(modeState);
|
|
395
|
+
if (status === "mismatch") {
|
|
396
|
+
return {
|
|
397
|
+
blocking: isBlockingPlanningPhase(skill, activePhase),
|
|
398
|
+
phase: activePhase,
|
|
399
|
+
tampered: true,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
if (!modeStateMatchesContext(modeState, sessionId, threadId)) {
|
|
403
|
+
return {
|
|
404
|
+
blocking: isBlockingPlanningPhase(skill, activePhase),
|
|
405
|
+
phase: activePhase,
|
|
406
|
+
tampered: false,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
if (status === "match") {
|
|
410
|
+
if (modeState.active !== true) return { blocking: false, phase: activePhase, tampered: false };
|
|
411
|
+
const phase = String(modeState.current_phase ?? activePhase).trim();
|
|
412
|
+
return { blocking: isBlockingPlanningPhase(skill, phase), phase, tampered: false };
|
|
413
|
+
}
|
|
414
|
+
if (!isBlockingPlanningPhase(skill, activePhase)) {
|
|
415
|
+
const stricterPhase = String(modeState.current_phase ?? "").trim();
|
|
416
|
+
if (modeState.active === true && stricterPhase && isBlockingPlanningPhase(skill, stricterPhase)) {
|
|
417
|
+
return { blocking: true, phase: stricterPhase, tampered: false };
|
|
418
|
+
}
|
|
419
|
+
return { blocking: false, phase: activePhase, tampered: false };
|
|
420
|
+
}
|
|
421
|
+
if (modeState.active !== true) return { blocking: true, phase: activePhase, tampered: false };
|
|
422
|
+
const modePhase = String(modeState.current_phase ?? "").trim();
|
|
423
|
+
if (
|
|
424
|
+
modePhase &&
|
|
425
|
+
!isBlockingPlanningPhase(skill, modePhase) &&
|
|
426
|
+
modePhase.trim().toLowerCase() !== activePhase.trim().toLowerCase()
|
|
427
|
+
) {
|
|
428
|
+
return { blocking: true, phase: activePhase, tampered: false };
|
|
429
|
+
}
|
|
430
|
+
const phase = modePhase || activePhase;
|
|
431
|
+
return { blocking: isBlockingPlanningPhase(skill, phase), phase, tampered: false };
|
|
351
432
|
}
|
|
352
433
|
|
|
353
434
|
function normalizePosix(value: string): string {
|
|
@@ -1607,11 +1688,22 @@ function extractBashTargets(args: unknown, depth = 0): ExtractedTargets {
|
|
|
1607
1688
|
}
|
|
1608
1689
|
return targets;
|
|
1609
1690
|
}
|
|
1691
|
+
/**
|
|
1692
|
+
* Read-only github ops extract no mutation. Every other op fails closed during
|
|
1693
|
+
* planning: remote push, PR creation, and worktree/config writes are not a
|
|
1694
|
+
* filesystem path the planning allowlist can clear.
|
|
1695
|
+
*/
|
|
1696
|
+
function extractGithubTargets(args: unknown): ExtractedTargets {
|
|
1697
|
+
if (isGithubReadOnlyArgs(args)) return { paths: [], unknown: false };
|
|
1698
|
+
return { paths: [], unknown: true, explicitMutation: true };
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1610
1701
|
function extractTargets(tool: ToolWithEditMode, args: unknown): ExtractedTargets {
|
|
1611
1702
|
if (tool.name === "write") return extractWriteTargets(args);
|
|
1612
1703
|
if (tool.name === "ast_edit") return extractAstEditTargets(args);
|
|
1613
1704
|
if (tool.name === "edit") return extractEditTargets(args, tool);
|
|
1614
1705
|
if (tool.name === "bash") return extractBashTargets(args);
|
|
1706
|
+
if (tool.name === "github") return extractGithubTargets(args);
|
|
1615
1707
|
return { paths: [], unknown: true };
|
|
1616
1708
|
}
|
|
1617
1709
|
|
|
@@ -31,6 +31,8 @@ import {
|
|
|
31
31
|
type InteractiveModeContext,
|
|
32
32
|
} from "../modes/types";
|
|
33
33
|
import { parseUiLanguage, resolveUiLanguage, UI_LANGUAGE_LABELS, UI_LANGUAGES, uiString } from "../modes/ui-language";
|
|
34
|
+
import { buildSessionProjectProgress } from "../progress/collect-project-progress";
|
|
35
|
+
import { PROGRESS_COMMAND_ACP_DESCRIPTION } from "../progress/render-progress";
|
|
34
36
|
// W1b/W5b: notification-service and daemon controllers stay off the static
|
|
35
37
|
// import graph; the /notify handlers import them lazily at first use.
|
|
36
38
|
import type { NotificationProvider } from "../sdk/bus/config";
|
|
@@ -63,6 +65,7 @@ import { switchSessionCredentialCommand } from "./helpers/credential-switch";
|
|
|
63
65
|
import { buildFastStatusReport } from "./helpers/fast-status-report";
|
|
64
66
|
import { formatDuration } from "./helpers/format";
|
|
65
67
|
import { commandConsumed, errorMessage, parseSlashCommand, parseSubcommand, usage } from "./helpers/parse";
|
|
68
|
+
import { renderProgressReportLines, renderProgressReportText } from "./helpers/progress-report";
|
|
66
69
|
import { handleSshAcp } from "./helpers/ssh";
|
|
67
70
|
import { buildUsageReportText, collectCachedUsageReports } from "./helpers/usage-report";
|
|
68
71
|
import type {
|
|
@@ -1059,14 +1062,17 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1059
1062
|
const profileLabel = formatModelProfileDisplayLabel(
|
|
1060
1063
|
runtime.session.modelRegistry.getModelProfile(presetName) ?? { name: presetName },
|
|
1061
1064
|
);
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1065
|
+
runtime.session.markUserModelSelection();
|
|
1066
|
+
await runtime.session.withSdkControlMutation(() =>
|
|
1067
|
+
activateModelProfile(
|
|
1068
|
+
{
|
|
1069
|
+
session: runtime.session,
|
|
1070
|
+
modelRegistry: runtime.session.modelRegistry,
|
|
1071
|
+
settings: runtime.settings,
|
|
1072
|
+
profileName: presetName,
|
|
1073
|
+
},
|
|
1074
|
+
{ persistDefault: false },
|
|
1075
|
+
),
|
|
1070
1076
|
);
|
|
1071
1077
|
await runtime.output(`Model profile: ${profileLabel}`);
|
|
1072
1078
|
await runtime.notifyTitleChanged?.();
|
|
@@ -1106,6 +1112,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1106
1112
|
throw new Error(`No API key for ${selection.model.provider}/${selection.model.id}`);
|
|
1107
1113
|
}
|
|
1108
1114
|
}
|
|
1115
|
+
if (includesRoleAgent && !includesDefault) runtime.session.markUserModelSelection();
|
|
1109
1116
|
|
|
1110
1117
|
const overrides = runtime.settings.get("task.agentModelOverrides");
|
|
1111
1118
|
const assignments = new Map<GjcModelAssignmentTargetId, string>();
|
|
@@ -1136,7 +1143,6 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1136
1143
|
runtime.session.setThinkingLevel(existingDefaultThinkingLevel);
|
|
1137
1144
|
}
|
|
1138
1145
|
}
|
|
1139
|
-
|
|
1140
1146
|
const materializedProfile = materializeActiveModelProfileAssignments({
|
|
1141
1147
|
session: runtime.session,
|
|
1142
1148
|
settings: runtime.settings,
|
|
@@ -1584,6 +1590,30 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1584
1590
|
runtime.ctx.editor.setText("");
|
|
1585
1591
|
},
|
|
1586
1592
|
},
|
|
1593
|
+
{
|
|
1594
|
+
name: "progress",
|
|
1595
|
+
description:
|
|
1596
|
+
"Show a read-only project progress overview from durable goal, todo, workflow, agent, and verification state",
|
|
1597
|
+
// Production ACP advertises and dispatches `/progress` itself over the
|
|
1598
|
+
// `session.progress` SDK query (modes/acp/acp-agent.ts) with the same copy.
|
|
1599
|
+
acpDescription: PROGRESS_COMMAND_ACP_DESCRIPTION,
|
|
1600
|
+
allowArgs: false,
|
|
1601
|
+
handle: async (_command, runtime) => {
|
|
1602
|
+
const report = await buildSessionProjectProgress(runtime.session, runtime.sessionManager);
|
|
1603
|
+
await runtime.output(renderProgressReportText(report));
|
|
1604
|
+
return commandConsumed();
|
|
1605
|
+
},
|
|
1606
|
+
handleTui: async (_command, runtime) => {
|
|
1607
|
+
const ctx = runtime.ctx;
|
|
1608
|
+
const report = await buildSessionProjectProgress(ctx.session, ctx.sessionManager);
|
|
1609
|
+
ctx.chatContainer.addChild(new Spacer(1));
|
|
1610
|
+
ctx.chatContainer.addChild(new DynamicBorder());
|
|
1611
|
+
ctx.chatContainer.addChild(new Text(renderProgressReportLines(report, theme).join("\n"), 1, 0));
|
|
1612
|
+
ctx.chatContainer.addChild(new DynamicBorder());
|
|
1613
|
+
ctx.ui.requestRender();
|
|
1614
|
+
ctx.editor.setText("");
|
|
1615
|
+
},
|
|
1616
|
+
},
|
|
1587
1617
|
{
|
|
1588
1618
|
name: "usage",
|
|
1589
1619
|
description: "Show provider usage and limits",
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
updateMCPServer,
|
|
10
10
|
} from "../../runtime-mcp/config-writer";
|
|
11
11
|
import { MCPManager } from "../../runtime-mcp/manager";
|
|
12
|
+
import { nativeMcpSource } from "../../runtime-mcp/project-host-values";
|
|
12
13
|
import { getSmitheryApiKey } from "../../runtime-mcp/smithery-auth";
|
|
13
14
|
import { searchSmitheryRegistry } from "../../runtime-mcp/smithery-registry";
|
|
14
15
|
import type { MCPServerConfig, MCPServerConnection } from "../../runtime-mcp/types";
|
|
@@ -199,6 +200,7 @@ async function withPreparedMcpConnection<T>(
|
|
|
199
200
|
runtime: SlashCommandRuntime,
|
|
200
201
|
name: string,
|
|
201
202
|
config: MCPServerConfig,
|
|
203
|
+
scope: AcpMcpScope,
|
|
202
204
|
fn: (connection: MCPServerConnection) => Promise<T>,
|
|
203
205
|
): Promise<T> {
|
|
204
206
|
const manager = new MCPManager(runtime.cwd, null, {
|
|
@@ -207,7 +209,9 @@ async function withPreparedMcpConnection<T>(
|
|
|
207
209
|
// Auth storage must be wired in before the prepared lease so OAuth-backed
|
|
208
210
|
// servers can refresh credentials and inject Authorization headers.
|
|
209
211
|
manager.setAuthStorage(runtime.session.modelRegistry.authStorage);
|
|
210
|
-
return manager.withPreparedLease(name, config, lease => fn(lease.connectionForLease())
|
|
212
|
+
return manager.withPreparedLease(name, config, lease => fn(lease.connectionForLease()), {
|
|
213
|
+
source: nativeMcpSource(scope, getMCPConfigPath(scope, runtime.cwd)),
|
|
214
|
+
});
|
|
211
215
|
}
|
|
212
216
|
|
|
213
217
|
async function collectConnectedMcpLines(
|
|
@@ -218,9 +222,9 @@ async function collectConnectedMcpLines(
|
|
|
218
222
|
if (servers.length === 0) return undefined;
|
|
219
223
|
|
|
220
224
|
const lines: string[] = [];
|
|
221
|
-
for (const { name, config } of servers) {
|
|
225
|
+
for (const { name, config, scope } of servers) {
|
|
222
226
|
try {
|
|
223
|
-
const collected = await withPreparedMcpConnection(runtime, name, config, connection =>
|
|
227
|
+
const collected = await withPreparedMcpConnection(runtime, name, config, scope, connection =>
|
|
224
228
|
collect(name, connection),
|
|
225
229
|
);
|
|
226
230
|
lines.push(...collected);
|
|
@@ -265,7 +269,7 @@ async function handleTestCommand(rest: string, runtime: SlashCommandRuntime): Pr
|
|
|
265
269
|
if (!server) return usage(`Server "${name}" not found. Run /mcp list to see configured servers.`, runtime);
|
|
266
270
|
|
|
267
271
|
try {
|
|
268
|
-
return await withPreparedMcpConnection(runtime, name, server.config, async connection => {
|
|
272
|
+
return await withPreparedMcpConnection(runtime, name, server.config, server.scope, async connection => {
|
|
269
273
|
const tools = await listTools(connection);
|
|
270
274
|
const lines = [`Server "${name}" connected (${tools.length} tools).`];
|
|
271
275
|
for (const tool of tools) lines.push(` - ${tool.name}`);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { replaceTabs, truncateToWidth } from "@gajae-code/tui";
|
|
2
|
+
import { sanitizeDisplayLine } from "@gajae-code/utils";
|
|
3
|
+
import { toProjectProgressSnapshot } from "../../progress/progress-snapshot";
|
|
4
|
+
import type { ProjectProgressReport } from "../../progress/project-progress";
|
|
5
|
+
import { type ProgressRenderStyle, renderProgressSnapshot } from "../../progress/render-progress";
|
|
6
|
+
|
|
7
|
+
/** Bound untrusted durable text (objectives, titles, HUD values) to one sanitized display line of `max` columns. */
|
|
8
|
+
export function clipProgressText(text: string, max: number): string {
|
|
9
|
+
return truncateToWidth(sanitizeDisplayLine(replaceTabs(text)).trim(), max);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** TUI view: the `session.progress` snapshot rendered with column-aware clipping and theme colours. */
|
|
13
|
+
export function renderProgressReportLines(report: ProjectProgressReport, style?: ProgressRenderStyle): string[] {
|
|
14
|
+
return renderProgressSnapshot(toProjectProgressSnapshot(report), {
|
|
15
|
+
clip: clipProgressText,
|
|
16
|
+
...(style ? { style } : {}),
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Plain text view: identical to what production ACP renders from the `session.progress` query. */
|
|
21
|
+
export function renderProgressReportText(report: ProjectProgressReport): string {
|
|
22
|
+
return renderProgressSnapshot(toProjectProgressSnapshot(report)).join("\n");
|
|
23
|
+
}
|
package/src/stt/setup.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { macosOptionForwardingInstruction } from "../modes/utils/macos-option-forwarding";
|
|
1
2
|
import { detectRecordingTools } from "./recorder";
|
|
2
3
|
import { resolvePython } from "./transcriber";
|
|
3
4
|
|
|
@@ -20,11 +21,8 @@ export function formatSTTUsage(
|
|
|
20
21
|
];
|
|
21
22
|
if (platform === "darwin") {
|
|
22
23
|
lines.push("On macOS, Alt+H is Option+H. Your terminal must forward Option as Meta/Esc.");
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"Ghostty: set macos-option-as-alt = true in its config, then reload the config or restart Ghostty.",
|
|
26
|
-
);
|
|
27
|
-
}
|
|
24
|
+
const instruction = macosOptionForwardingInstruction(terminalProgram);
|
|
25
|
+
if (instruction) lines.push(instruction);
|
|
28
26
|
}
|
|
29
27
|
lines.push("Run /hotkeys inside GJC to confirm the active shortcut.");
|
|
30
28
|
return lines.join("\n");
|
package/src/task/commands.ts
CHANGED
|
@@ -120,7 +120,9 @@ export function getCommand(commands: WorkflowCommand[], name: string): WorkflowC
|
|
|
120
120
|
* Replaces $@ with the provided input.
|
|
121
121
|
*/
|
|
122
122
|
export function expandCommand(command: WorkflowCommand, input: string): string {
|
|
123
|
-
|
|
123
|
+
// Function replacer: `input` is user text and must be inserted verbatim, not
|
|
124
|
+
// parsed for `$&`, `$$`, `` $` `` or `$'` replacement patterns.
|
|
125
|
+
return command.instructions.replace(/\$@/g, () => input);
|
|
124
126
|
}
|
|
125
127
|
|
|
126
128
|
/**
|
package/src/task/executor.ts
CHANGED
|
@@ -33,6 +33,7 @@ import * as canonicalSdk from "@gajae-code/coding-agent/sdk";
|
|
|
33
33
|
import { logger, prompt, untilAborted } from "@gajae-code/utils";
|
|
34
34
|
import { AsyncJobManager } from "../async";
|
|
35
35
|
import { AUTOROUTING_SELECTOR_MAX_LENGTH, type AutoroutingReasonCode } from "../config/autorouting-contract";
|
|
36
|
+
import type { ModelProfileOwnershipMarker } from "../config/model-profile-ownership";
|
|
36
37
|
import { ModelRegistry } from "../config/model-registry";
|
|
37
38
|
import {
|
|
38
39
|
formatModelString,
|
|
@@ -262,6 +263,8 @@ export interface ExecutorOptions {
|
|
|
262
263
|
* Manual/direct parents (no active profile) keep exact resolution.
|
|
263
264
|
*/
|
|
264
265
|
parentActiveModelProfile?: string;
|
|
266
|
+
/** Parent session's model profile ownership marker for propagating to subagent sessions. */
|
|
267
|
+
parentModelProfileOwnershipMarker?: ModelProfileOwnershipMarker;
|
|
265
268
|
parentSessionId?: string;
|
|
266
269
|
parentCredentialSessionId?: string;
|
|
267
270
|
thinkingLevel?: ThinkingLevel;
|
|
@@ -2026,6 +2029,7 @@ export async function runSubprocessOnce(options: ExecutorOptions): Promise<Singl
|
|
|
2026
2029
|
model,
|
|
2027
2030
|
thinkingLevel: effectiveThinkingLevel,
|
|
2028
2031
|
activeModelProfile: options.parentActiveModelProfile,
|
|
2032
|
+
modelProfileOwnershipMarker: options.parentModelProfileOwnershipMarker,
|
|
2029
2033
|
credentialSessionId: options.parentCredentialSessionId ?? options.parentSessionId,
|
|
2030
2034
|
modelSubstitution:
|
|
2031
2035
|
modelSubstitutionWarning?.reason === "auth_unavailable" && requestedModel
|
package/src/task/index.ts
CHANGED
|
@@ -1738,6 +1738,10 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1738
1738
|
const parentActiveModelProfile = (
|
|
1739
1739
|
this.session as { getActiveModelProfile?: () => string | undefined }
|
|
1740
1740
|
).getActiveModelProfile?.();
|
|
1741
|
+
// Propagate the parent session's ownership marker when a session-local profile is active.
|
|
1742
|
+
const parentModelProfileOwnershipMarker = (
|
|
1743
|
+
this.session as { sessionManager?: { getModelProfileOwnershipMarker?: () => unknown } }
|
|
1744
|
+
).sessionManager?.getModelProfileOwnershipMarker?.();
|
|
1741
1745
|
const parentProfileDefinition = parentActiveModelProfile
|
|
1742
1746
|
? this.session.modelRegistry?.getModelProfile?.(parentActiveModelProfile)
|
|
1743
1747
|
: undefined;
|
|
@@ -2273,6 +2277,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
2273
2277
|
modelOverride: effectivePatterns(index),
|
|
2274
2278
|
parentActiveModelPattern,
|
|
2275
2279
|
parentActiveModelProfile: parentOwnedModelProfile,
|
|
2280
|
+
parentModelProfileOwnershipMarker: parentModelProfileOwnershipMarker as any,
|
|
2276
2281
|
parentSessionId: this.session.getSessionId?.() ?? undefined,
|
|
2277
2282
|
parentCredentialSessionId:
|
|
2278
2283
|
this.session.getCredentialSessionId?.() ?? this.session.getSessionId?.() ?? undefined,
|
|
@@ -2360,6 +2365,7 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
2360
2365
|
modelOverride: effectivePatterns(index),
|
|
2361
2366
|
parentActiveModelPattern,
|
|
2362
2367
|
parentActiveModelProfile: parentOwnedModelProfile,
|
|
2368
|
+
parentModelProfileOwnershipMarker: parentModelProfileOwnershipMarker as any,
|
|
2363
2369
|
parentSessionId: this.session.getSessionId?.() ?? undefined,
|
|
2364
2370
|
parentCredentialSessionId:
|
|
2365
2371
|
this.session.getCredentialSessionId?.() ?? this.session.getSessionId?.() ?? undefined,
|