@gajae-code/coding-agent 0.17.1 → 0.17.4
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 +266 -0
- package/dist/types/async/job-manager.d.ts +7 -6
- package/dist/types/cli/daemon-cli.d.ts +3 -1
- package/dist/types/cli/github-release.d.ts +17 -0
- package/dist/types/cli/public-command-entry.d.ts +56 -0
- package/dist/types/cli/public-command-errors.d.ts +126 -0
- package/dist/types/cli/public-command-evidence.d.ts +99 -0
- package/dist/types/cli/public-command-help.d.ts +65 -0
- package/dist/types/cli/public-command-registry.d.ts +69 -0
- package/dist/types/cli/session-picker.d.ts +1 -1
- package/dist/types/cli/setup-cli.d.ts +1 -0
- package/dist/types/cli/skills-cli.d.ts +5 -0
- package/dist/types/cli.d.ts +3 -0
- package/dist/types/commands/daemon.d.ts +4 -50
- package/dist/types/commands/sdk.d.ts +20 -37
- package/dist/types/commands/setup.d.ts +3 -0
- package/dist/types/commands/skills.d.ts +9 -0
- package/dist/types/config/autorouting-tier-map.d.ts +24 -0
- package/dist/types/config/file-lock.d.ts +10 -1
- package/dist/types/config/model-profile-activation.d.ts +63 -4
- package/dist/types/config/model-registry.d.ts +20 -2
- package/dist/types/config/settings-schema.d.ts +11 -0
- package/dist/types/coordinator-mcp/question-state.d.ts +6 -1
- package/dist/types/coordinator-mcp/server.d.ts +3 -0
- package/dist/types/daemon/operator-contract.d.ts +5 -1
- package/dist/types/eval/js/worker-protocol.d.ts +2 -0
- package/dist/types/extensibility/extensions/types.d.ts +12 -0
- package/dist/types/extensibility/gjc-plugins/registry.d.ts +4 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +18 -0
- package/dist/types/extensibility/shared-events.d.ts +3 -1
- package/dist/types/gjc-runtime/session-state-lock.d.ts +61 -0
- package/dist/types/internal-urls/embedded-protocol.d.ts +9 -0
- package/dist/types/internal-urls/index.d.ts +1 -0
- package/dist/types/internal-urls/parse.d.ts +9 -1
- package/dist/types/main.d.ts +22 -0
- package/dist/types/modes/acp/acp-agent.d.ts +94 -1
- package/dist/types/modes/acp/startup-provenance.d.ts +16 -0
- package/dist/types/modes/components/custom-provider-wizard.d.ts +19 -1
- package/dist/types/modes/components/session-selector.d.ts +5 -2
- package/dist/types/modes/components/thinking-selector.d.ts +2 -1
- package/dist/types/modes/components/user-message-selector.d.ts +5 -2
- package/dist/types/modes/controllers/command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +1 -1
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +11 -1
- package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +14 -1
- package/dist/types/modes/types.d.ts +2 -2
- package/dist/types/runtime/process-lifecycle.d.ts +2 -0
- package/dist/types/runtime-mcp/manager.d.ts +56 -0
- package/dist/types/runtime-mcp/pool.d.ts +17 -0
- package/dist/types/runtime-mcp/redaction.d.ts +20 -0
- package/dist/types/runtime-mcp/startup-policy.d.ts +6 -0
- package/dist/types/runtime-mcp/tool-bridge.d.ts +9 -0
- package/dist/types/runtime-mcp/transports/http.d.ts +11 -0
- package/dist/types/sdk/acp/adapter.d.ts +27 -0
- package/dist/types/sdk/acp/final-text.d.ts +8 -0
- package/dist/types/sdk/broker/broker.d.ts +13 -2
- package/dist/types/sdk/broker/ensure.d.ts +46 -1
- package/dist/types/sdk/broker/identity.d.ts +3 -1
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +23 -3
- package/dist/types/sdk/broker/lifecycle.d.ts +15 -8
- package/dist/types/sdk/broker/runtime.d.ts +1 -1
- package/dist/types/sdk/broker/session-index.d.ts +22 -3
- package/dist/types/sdk/broker/spawn-authority.d.ts +4 -0
- package/dist/types/sdk/broker/stderr-drainer.d.ts +13 -0
- package/dist/types/sdk/bus/index.d.ts +19 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +3 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +5 -0
- package/dist/types/sdk/cli/master-cli.d.ts +6 -10
- package/dist/types/sdk/cli/rows.d.ts +19 -1
- package/dist/types/sdk/cli/session-cli.d.ts +22 -5
- package/dist/types/sdk/client/client.d.ts +2 -0
- package/dist/types/sdk/guides/cli.d.ts +3 -4
- package/dist/types/sdk/guides/verify.d.ts +2 -0
- package/dist/types/sdk/host/host.d.ts +9 -0
- package/dist/types/sdk/host/session-runtime.d.ts +31 -0
- package/dist/types/sdk/host/types.d.ts +10 -0
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/prompt-deadline-flush.d.ts +83 -0
- package/dist/types/sdk/prompt-deadline-manager.d.ts +38 -1
- package/dist/types/sdk/prompt-failure.d.ts +30 -1
- package/dist/types/sdk/prompt-status.d.ts +8 -0
- package/dist/types/sdk/prompt-tool-boundary.d.ts +73 -0
- package/dist/types/sdk/router/replay-retention-gap-cache.d.ts +13 -0
- package/dist/types/sdk/router/session-router.d.ts +2 -0
- package/dist/types/sdk/startup-capability.d.ts +2 -0
- package/dist/types/sdk/transport/relay.d.ts +1 -0
- package/dist/types/sdk/transport/serve-cli.d.ts +73 -2
- package/dist/types/sdk/transport/stdio.d.ts +5 -1
- package/dist/types/session/agent-session.d.ts +168 -21
- package/dist/types/session/fallback-chain-controller.d.ts +6 -6
- package/dist/types/session/fold-coordinator.d.ts +17 -3
- package/dist/types/session/internal/managed-session-storage.d.ts +7 -1
- package/dist/types/session/session-manager.d.ts +64 -1
- package/dist/types/session/session-work-lease.d.ts +14 -0
- package/dist/types/setup/model-onboarding-guidance.d.ts +1 -1
- package/dist/types/setup/provider-onboarding.d.ts +81 -1
- package/dist/types/thinking.d.ts +3 -1
- package/dist/types/tools/bash.d.ts +37 -11
- package/dist/types/tools/job.d.ts +1 -1
- package/dist/types/tools/steer-fold.d.ts +31 -0
- package/dist/types/utils/git.d.ts +28 -2
- package/dist/types/utils/mupdf-embedded.d.ts +2 -0
- package/dist/types/utils/mupdf-wasm-embedded.d.ts +2 -0
- package/dist/types/utils/mupdf.d.ts +4 -0
- package/dist/types/utils/session-title-generation.d.ts +6 -0
- package/dist/types/utils/title-generator.d.ts +11 -2
- package/dist/types/web/scrapers/types.d.ts +3 -0
- package/package.json +18 -10
- package/scripts/build-binary.ts +9 -3
- package/scripts/compile-args.ts +0 -9
- package/scripts/embed-mupdf.ts +52 -0
- package/scripts/generate-sdk-operation-inventory.ts +31 -1
- package/scripts/vendor-markit.ts +196 -0
- package/src/async/job-manager.ts +60 -9
- package/src/cli/daemon-cli.ts +53 -18
- package/src/cli/github-release.ts +87 -5
- package/src/cli/mcp-cli.ts +38 -4
- package/src/cli/public-command-entry.ts +450 -0
- package/src/cli/public-command-errors.ts +581 -0
- package/src/cli/public-command-evidence.ts +717 -0
- package/src/cli/public-command-help.ts +360 -0
- package/src/cli/public-command-registry.ts +535 -0
- package/src/cli/session-picker.ts +2 -1
- package/src/cli/setup-cli.ts +9 -1
- package/src/cli/skills-cli.ts +46 -2
- package/src/cli.ts +61 -1
- package/src/commands/daemon.ts +65 -69
- package/src/commands/sdk.ts +232 -100
- package/src/commands/setup.ts +2 -0
- package/src/commands/skills.ts +11 -0
- package/src/config/autorouting-tier-map.ts +16 -0
- package/src/config/file-lock.ts +199 -68
- package/src/config/model-profile-activation.ts +406 -53
- package/src/config/model-registry.ts +204 -25
- package/src/config/settings-schema.ts +26 -2
- package/src/coordinator-mcp/policy.ts +10 -5
- package/src/coordinator-mcp/question-gate-codec.ts +17 -0
- package/src/coordinator-mcp/question-state.ts +175 -8
- package/src/coordinator-mcp/server.ts +528 -87
- package/src/coordinator-mcp/session-reaper.ts +6 -4
- package/src/crash/record-loader.ts +6 -2
- package/src/daemon/operator-contract.ts +26 -12
- package/src/debug/report-bundle.ts +114 -24
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +1 -1
- package/src/eval/js/context-manager.ts +8 -8
- package/src/eval/js/worker-core.ts +9 -4
- package/src/eval/js/worker-protocol.ts +2 -0
- package/src/extensibility/extensions/runner.ts +52 -4
- package/src/extensibility/extensions/types.ts +10 -0
- package/src/extensibility/gjc-plugins/registry.ts +196 -37
- package/src/extensibility/runtime-skill-discovery.ts +61 -3
- package/src/extensibility/shared-events.ts +3 -1
- package/src/gjc-runtime/managed-owner-admission.ts +67 -50
- package/src/gjc-runtime/session-state-lock.ts +165 -24
- package/src/gjc-runtime/ultragoal-succession.ts +31 -3
- package/src/internal-urls/docs-index.generated.ts +21 -19
- package/src/internal-urls/embedded-protocol.ts +83 -0
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/parse.ts +21 -1
- package/src/internal-urls/router.ts +4 -2
- package/src/main.ts +123 -55
- package/src/modes/acp/acp-agent.ts +593 -47
- package/src/modes/acp/acp-event-mapper.ts +9 -0
- package/src/modes/acp/startup-provenance.ts +59 -0
- package/src/modes/components/custom-provider-wizard.ts +308 -7
- package/src/modes/components/model-selector.ts +31 -15
- package/src/modes/components/oauth-selector.ts +9 -0
- package/src/modes/components/session-selector.ts +47 -4
- package/src/modes/components/status-line/segments.ts +6 -1
- package/src/modes/components/thinking-selector.ts +7 -3
- package/src/modes/components/user-message-selector.ts +57 -15
- package/src/modes/controllers/command-controller.ts +58 -32
- package/src/modes/controllers/extension-ui-controller.ts +33 -7
- package/src/modes/controllers/input-controller.ts +4 -0
- package/src/modes/controllers/selector-controller.ts +177 -18
- package/src/modes/interactive-mode.ts +21 -7
- package/src/modes/runtime-init.ts +1 -0
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +124 -7
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +16 -2
- package/src/modes/types.ts +2 -2
- package/src/prompts/agent-fragments/browser-aside.md +1 -0
- package/src/prompts/system/title-system.md +1 -1
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/job.md +1 -1
- package/src/prompts/tools/read.md +2 -0
- package/src/runtime/network-prewarm-service.test.ts +36 -1
- package/src/runtime/network-prewarm-service.ts +28 -3
- package/src/runtime/process-lifecycle.ts +22 -11
- package/src/runtime-mcp/config.ts +16 -1
- package/src/runtime-mcp/manager.ts +689 -55
- package/src/runtime-mcp/pool.test.ts +31 -0
- package/src/runtime-mcp/pool.ts +304 -28
- package/src/runtime-mcp/redaction.ts +42 -0
- package/src/runtime-mcp/startup-policy.ts +7 -0
- package/src/runtime-mcp/tool-bridge.ts +71 -0
- package/src/runtime-mcp/transports/http.ts +108 -25
- package/src/sdk/acp/adapter.ts +90 -9
- package/src/sdk/acp/final-text.ts +19 -2
- package/src/sdk/broker/broker.ts +378 -52
- package/src/sdk/broker/ensure.ts +97 -19
- package/src/sdk/broker/identity.ts +9 -1
- package/src/sdk/broker/lifecycle-ledger.ts +95 -31
- package/src/sdk/broker/lifecycle.ts +690 -141
- package/src/sdk/broker/runtime.ts +1 -1
- package/src/sdk/broker/session-index.ts +97 -4
- package/src/sdk/broker/spawn-authority.ts +35 -7
- package/src/sdk/broker/stderr-drainer.ts +59 -0
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +1 -0
- package/src/sdk/bus/index.ts +721 -84
- package/src/sdk/bus/telegram-daemon-cli.ts +31 -2
- package/src/sdk/bus/telegram-daemon-contract.ts +3 -1
- package/src/sdk/bus/telegram-daemon.ts +10 -3
- package/src/sdk/cli/master-cli.ts +47 -36
- package/src/sdk/cli/rows.ts +30 -7
- package/src/sdk/cli/session-cli.ts +687 -184
- package/src/sdk/client/client.ts +18 -0
- package/src/sdk/guides/catalog.ts +2 -2
- package/src/sdk/guides/cli.ts +20 -62
- package/src/sdk/guides/verify.ts +14 -0
- package/src/sdk/host/host.ts +49 -5
- package/src/sdk/host/session-runtime.test.ts +636 -9
- package/src/sdk/host/session-runtime.ts +499 -118
- package/src/sdk/host/types.ts +12 -1
- package/src/sdk/host/websocket-transport.lifecycle.test.ts +84 -0
- package/src/sdk/host/websocket-transport.ts +40 -6
- package/src/sdk/index.ts +11 -0
- package/src/sdk/prompt-deadline-flush.ts +334 -0
- package/src/sdk/prompt-deadline-manager.ts +136 -4
- package/src/sdk/prompt-failure.ts +43 -1
- package/src/sdk/prompt-status.ts +8 -0
- package/src/sdk/prompt-tool-boundary.ts +121 -0
- package/src/sdk/protocol/operation-inventory.generated.json +106 -0
- package/src/sdk/router/replay-retention-gap-cache.ts +27 -0
- package/src/sdk/router/session-router.ts +31 -4
- package/src/sdk/session.ts +194 -16
- package/src/sdk/startup-capability.ts +1 -1
- package/src/sdk/transport/relay.ts +71 -6
- package/src/sdk/transport/serve-cli.ts +494 -49
- package/src/sdk/transport/socket.ts +12 -11
- package/src/sdk/transport/stdio.ts +7 -3
- package/src/session/agent-session.ts +2437 -400
- package/src/session/fallback-chain-controller.ts +6 -7
- package/src/session/fold-coordinator.ts +24 -5
- package/src/session/internal/managed-session-scope.ts +58 -18
- package/src/session/internal/managed-session-storage.ts +304 -11
- package/src/session/session-manager.ts +382 -44
- package/src/session/session-work-lease.ts +34 -0
- package/src/session-import/codex.ts +12 -3
- package/src/setup/model-onboarding-guidance.ts +1 -1
- package/src/setup/provider-onboarding.ts +397 -24
- package/src/slash-commands/builtin-registry.ts +258 -14
- package/src/task/index.ts +14 -2
- package/src/thinking.ts +21 -2
- package/src/tools/bash.ts +98 -78
- package/src/tools/fetch.ts +119 -20
- package/src/tools/job.ts +151 -25
- package/src/tools/output-meta.ts +15 -2
- package/src/tools/path-utils.ts +27 -3
- package/src/tools/read.ts +3 -3
- package/src/tools/steer-fold.ts +107 -0
- package/src/tools/tool-catalog.generated.ts +29 -2
- package/src/tools/tool-errors.ts +4 -0
- package/src/utils/git.ts +53 -3
- package/src/utils/markit.ts +55 -41
- package/src/utils/mupdf-assets.d.ts +4 -0
- package/src/utils/mupdf-embedded.ts +4 -0
- package/src/utils/mupdf-wasm-embedded.ts +3 -0
- package/src/utils/mupdf-wasm.ts +24 -2
- package/src/utils/mupdf.ts +129 -0
- package/src/utils/session-title-generation.ts +29 -0
- package/src/utils/title-generator.ts +46 -2
- package/src/web/scrapers/types.ts +24 -3
- package/vendor/markit-ai/LICENSE +21 -0
- package/vendor/markit-ai/MANIFEST.json +412 -0
- package/vendor/markit-ai/dist/commands/config.d.ts +4 -0
- package/vendor/markit-ai/dist/commands/config.js +139 -0
- package/vendor/markit-ai/dist/commands/convert.d.ts +6 -0
- package/vendor/markit-ai/dist/commands/convert.js +113 -0
- package/vendor/markit-ai/dist/commands/formats.d.ts +2 -0
- package/vendor/markit-ai/dist/commands/formats.js +92 -0
- package/vendor/markit-ai/dist/commands/init.d.ts +2 -0
- package/vendor/markit-ai/dist/commands/init.js +29 -0
- package/vendor/markit-ai/dist/commands/onboard.d.ts +2 -0
- package/vendor/markit-ai/dist/commands/onboard.js +61 -0
- package/vendor/markit-ai/dist/commands/plugin.d.ts +4 -0
- package/vendor/markit-ai/dist/commands/plugin.js +58 -0
- package/vendor/markit-ai/dist/config.d.ts +26 -0
- package/vendor/markit-ai/dist/config.js +42 -0
- package/vendor/markit-ai/dist/converters/audio.d.ts +7 -0
- package/vendor/markit-ai/dist/converters/audio.js +104 -0
- package/vendor/markit-ai/dist/converters/csv.d.ts +7 -0
- package/vendor/markit-ai/dist/converters/csv.js +83 -0
- package/vendor/markit-ai/dist/converters/docx.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/docx.js +57 -0
- package/vendor/markit-ai/dist/converters/epub.d.ts +8 -0
- package/vendor/markit-ai/dist/converters/epub.js +128 -0
- package/vendor/markit-ai/dist/converters/github.d.ts +18 -0
- package/vendor/markit-ai/dist/converters/github.js +148 -0
- package/vendor/markit-ai/dist/converters/html.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/html.js +30 -0
- package/vendor/markit-ai/dist/converters/image.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/image.js +124 -0
- package/vendor/markit-ai/dist/converters/ipynb.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/ipynb.js +75 -0
- package/vendor/markit-ai/dist/converters/iwork.d.ts +20 -0
- package/vendor/markit-ai/dist/converters/iwork.js +391 -0
- package/vendor/markit-ai/dist/converters/json.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/json.js +21 -0
- package/vendor/markit-ai/dist/converters/pdf/columns.d.ts +35 -0
- package/vendor/markit-ai/dist/converters/pdf/columns.js +93 -0
- package/vendor/markit-ai/dist/converters/pdf/extract.d.ts +19 -0
- package/vendor/markit-ai/dist/converters/pdf/extract.js +514 -0
- package/vendor/markit-ai/dist/converters/pdf/grid.d.ts +25 -0
- package/vendor/markit-ai/dist/converters/pdf/grid.js +795 -0
- package/vendor/markit-ai/dist/converters/pdf/headers.d.ts +24 -0
- package/vendor/markit-ai/dist/converters/pdf/headers.js +108 -0
- package/vendor/markit-ai/dist/converters/pdf/index.d.ts +19 -0
- package/vendor/markit-ai/dist/converters/pdf/index.js +136 -0
- package/vendor/markit-ai/dist/converters/pdf/mupdf-loader.js +9 -0
- package/vendor/markit-ai/dist/converters/pdf/render.d.ts +24 -0
- package/vendor/markit-ai/dist/converters/pdf/render.js +513 -0
- package/vendor/markit-ai/dist/converters/pdf/types.d.ts +75 -0
- package/vendor/markit-ai/dist/converters/pdf/types.js +1 -0
- package/vendor/markit-ai/dist/converters/plain-text.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/plain-text.js +87 -0
- package/vendor/markit-ai/dist/converters/pptx.d.ts +8 -0
- package/vendor/markit-ai/dist/converters/pptx.js +289 -0
- package/vendor/markit-ai/dist/converters/rss.d.ts +11 -0
- package/vendor/markit-ai/dist/converters/rss.js +138 -0
- package/vendor/markit-ai/dist/converters/wikipedia.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/wikipedia.js +33 -0
- package/vendor/markit-ai/dist/converters/xlsx.d.ts +8 -0
- package/vendor/markit-ai/dist/converters/xlsx.js +147 -0
- package/vendor/markit-ai/dist/converters/xml.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/xml.js +18 -0
- package/vendor/markit-ai/dist/converters/yaml.d.ts +6 -0
- package/vendor/markit-ai/dist/converters/yaml.js +17 -0
- package/vendor/markit-ai/dist/converters/zip.d.ts +8 -0
- package/vendor/markit-ai/dist/converters/zip.js +57 -0
- package/vendor/markit-ai/dist/index.d.ts +30 -0
- package/vendor/markit-ai/dist/index.js +26 -0
- package/vendor/markit-ai/dist/main.d.ts +2 -0
- package/vendor/markit-ai/dist/main.js +191 -0
- package/vendor/markit-ai/dist/markit.d.ts +45 -0
- package/vendor/markit-ai/dist/markit.js +267 -0
- package/vendor/markit-ai/dist/plugins/api.d.ts +7 -0
- package/vendor/markit-ai/dist/plugins/api.js +42 -0
- package/vendor/markit-ai/dist/plugins/index.d.ts +4 -0
- package/vendor/markit-ai/dist/plugins/index.js +3 -0
- package/vendor/markit-ai/dist/plugins/installer.d.ts +25 -0
- package/vendor/markit-ai/dist/plugins/installer.js +176 -0
- package/vendor/markit-ai/dist/plugins/loader.d.ts +6 -0
- package/vendor/markit-ai/dist/plugins/loader.js +61 -0
- package/vendor/markit-ai/dist/plugins/types.d.ts +25 -0
- package/vendor/markit-ai/dist/plugins/types.js +1 -0
- package/vendor/markit-ai/dist/providers/anthropic.d.ts +2 -0
- package/vendor/markit-ai/dist/providers/anthropic.js +47 -0
- package/vendor/markit-ai/dist/providers/index.d.ts +21 -0
- package/vendor/markit-ai/dist/providers/index.js +56 -0
- package/vendor/markit-ai/dist/providers/openai.d.ts +2 -0
- package/vendor/markit-ai/dist/providers/openai.js +76 -0
- package/vendor/markit-ai/dist/providers/types.d.ts +26 -0
- package/vendor/markit-ai/dist/providers/types.js +1 -0
- package/vendor/markit-ai/dist/types.d.ts +36 -0
- package/vendor/markit-ai/dist/types.js +1 -0
- package/vendor/markit-ai/dist/utils/exit-codes.d.ts +4 -0
- package/vendor/markit-ai/dist/utils/exit-codes.js +4 -0
- package/vendor/markit-ai/dist/utils/output.d.ts +22 -0
- package/vendor/markit-ai/dist/utils/output.js +31 -0
- package/vendor/markit-ai/dist/utils/turndown.d.ts +8 -0
- package/vendor/markit-ai/dist/utils/turndown.js +70 -0
- package/vendor/markit-ai/package.json +74 -0
- package/vendor/markit-ai.patch +49 -0
|
@@ -67,6 +67,7 @@ import { assertSafePathComponent } from "../gjc-runtime/session-layout";
|
|
|
67
67
|
import { writeTextAtomic } from "../gjc-runtime/state-writer";
|
|
68
68
|
import type { ManagedLegacyLocalMigrationSource } from "../internal-urls/local-protocol";
|
|
69
69
|
import * as git from "../utils/git";
|
|
70
|
+
import { invalidateSessionTitleGeneration } from "../utils/session-title-generation";
|
|
70
71
|
import { ArtifactManager } from "./artifacts";
|
|
71
72
|
import {
|
|
72
73
|
type BlobPutResult,
|
|
@@ -2162,8 +2163,47 @@ export const RESUME_TRANSCRIPT_MAX_BYTES = MANAGED_ARTIFACT_MAX_FILE_BYTES;
|
|
|
2162
2163
|
export const BOUNDED_RESUME_TRANSCRIPT_MAX_BYTES = 2 * 1024 * 1024 * 1024 + 1024 * 1024;
|
|
2163
2164
|
const EAGER_RESUME_TRANSCRIPT_MAX_BYTES = MANAGED_ARTIFACT_MAX_FILE_BYTES;
|
|
2164
2165
|
|
|
2166
|
+
/**
|
|
2167
|
+
* Recovery actions offered when a live transcript is at or past the managed
|
|
2168
|
+
* per-file cap.
|
|
2169
|
+
*
|
|
2170
|
+
* Constrained three ways, and every command named here satisfies all three:
|
|
2171
|
+
*
|
|
2172
|
+
* 1. **It must exist.** `gjc export <session-file>` was advised for both limits
|
|
2173
|
+
* and is not a subcommand, so following it literally started a fresh agent
|
|
2174
|
+
* that read "export" as a prompt while the session stayed unwritable. (The
|
|
2175
|
+
* root `--export` flag renders HTML and exits; it yields no resumable
|
|
2176
|
+
* session.)
|
|
2177
|
+
* 2. **It must be reachable on every surface that renders the message.** These
|
|
2178
|
+
* strings reach ACP/text consumers through `AgentSession`, and the ACP
|
|
2179
|
+
* registry is filtered to definitions carrying `handle`
|
|
2180
|
+
* (`slash-commands/acp-builtins.ts`), so a `handleTui`-only command such as
|
|
2181
|
+
* `/new` is a different dead end rather than a fix.
|
|
2182
|
+
* 3. **It must not drop the retained near-limit entries.** A failed append is
|
|
2183
|
+
* kept in memory with a full rewrite armed, and a rejected managed rewrite
|
|
2184
|
+
* likewise retains its resident entries; both messages promise the work
|
|
2185
|
+
* persists on the next successful write. `/compact` and `/clear` keep the
|
|
2186
|
+
* manager — and therefore that pending debt — alive; a session switch closes
|
|
2187
|
+
* the writer without paying it.
|
|
2188
|
+
*
|
|
2189
|
+
* Every near-limit error class MUST interpolate this constant rather than
|
|
2190
|
+
* restate the advice. `SessionNearLimitRewriteError` restated it and thereby
|
|
2191
|
+
* reintroduced `gjc export <session-file>` after #5621 removed it (#5691).
|
|
2192
|
+
*
|
|
2193
|
+
* `session-recovery-guidance.test.ts` pins all three constraints against the
|
|
2194
|
+
* CLI, builtin, and ACP registries, for every near-limit error class.
|
|
2195
|
+
*/
|
|
2196
|
+
export const SESSION_LIMIT_RECOVERY_ACTIONS =
|
|
2197
|
+
"compact the session (`/compact`), or clear its context (`/clear`) if compaction cannot reclaim enough";
|
|
2198
|
+
|
|
2199
|
+
/**
|
|
2200
|
+
* Oversized-resume guidance. This surface is reached *before* a session is
|
|
2201
|
+
* opened, so it deliberately does not reuse {@link SESSION_LIMIT_RECOVERY_ACTIONS}:
|
|
2202
|
+
* an in-session command there would act on whichever session the user resumes
|
|
2203
|
+
* next, never on the transcript that was just rejected.
|
|
2204
|
+
*/
|
|
2165
2205
|
export const SESSION_OVERSIZED_RECOVERY_MESSAGE =
|
|
2166
|
-
"The selected session transcript is too large to resume safely.
|
|
2206
|
+
"The selected session transcript is too large to resume safely. Resume a different session, or remove/archive this transcript after confirming its content is no longer needed.";
|
|
2167
2207
|
|
|
2168
2208
|
export class SessionAppendPersistenceError extends Error {
|
|
2169
2209
|
readonly phase: SessionAppendPersistenceFailurePhase;
|
|
@@ -2210,8 +2250,8 @@ export class SessionNearLimitAppendError extends Error {
|
|
|
2210
2250
|
[
|
|
2211
2251
|
`near_limit_append: entry (${details.entryBytes} B) plus live transcript (${details.liveBytes} B) exceeds the managed per-file limit (${details.capBytes} B).`,
|
|
2212
2252
|
details.entryRetained
|
|
2213
|
-
?
|
|
2214
|
-
:
|
|
2253
|
+
? `The appended entry is retained in memory; its effect (including any committed source edit) is recorded and will persist on the next successful write. To continue, ${SESSION_LIMIT_RECOVERY_ACTIONS}.`
|
|
2254
|
+
: `The appended entry was rolled back from memory; ${SESSION_LIMIT_RECOVERY_ACTIONS}, then re-issue it.`,
|
|
2215
2255
|
].join(" "),
|
|
2216
2256
|
);
|
|
2217
2257
|
this.name = "SessionNearLimitAppendError";
|
|
@@ -2222,6 +2262,54 @@ export class SessionNearLimitAppendError extends Error {
|
|
|
2222
2262
|
}
|
|
2223
2263
|
}
|
|
2224
2264
|
|
|
2265
|
+
/**
|
|
2266
|
+
* Typed near-limit rewrite outcome (follow-up to #4566).
|
|
2267
|
+
*
|
|
2268
|
+
* A managed atomic rewrite (`#writeEntriesAtomicallySync`) whose serialized
|
|
2269
|
+
* transcript crosses the per-file cap cannot be published: the managed store
|
|
2270
|
+
* rejects the bytes with `content_too_large`. Unlike the append path there is
|
|
2271
|
+
* no appended entry here — the whole live transcript is the payload — so this
|
|
2272
|
+
* error carries the rejected rewrite size and the cap instead of append
|
|
2273
|
+
* fields, and every rewrite/flush/close caller can point at compaction or
|
|
2274
|
+
* export as the continuation path.
|
|
2275
|
+
*/
|
|
2276
|
+
export class SessionNearLimitRewriteError extends Error {
|
|
2277
|
+
readonly code = "near_limit_rewrite" as const;
|
|
2278
|
+
/** Serialized transcript size (bytes) rejected by the managed store. */
|
|
2279
|
+
readonly transcriptBytes: number;
|
|
2280
|
+
/** Managed per-file cap in force when the rewrite was rejected. */
|
|
2281
|
+
readonly capBytes: number;
|
|
2282
|
+
|
|
2283
|
+
constructor(details: { transcriptBytes: number; capBytes: number }) {
|
|
2284
|
+
super(
|
|
2285
|
+
[
|
|
2286
|
+
`near_limit_rewrite: live transcript (${details.transcriptBytes} B) exceeds the managed per-file limit (${details.capBytes} B).`,
|
|
2287
|
+
// Must reuse SESSION_LIMIT_RECOVERY_ACTIONS rather than restate the advice:
|
|
2288
|
+
// this error class reintroduced `gjc export <session-file>` (#5691) after
|
|
2289
|
+
// #5621 removed it, because it hardcoded its own copy of the guidance.
|
|
2290
|
+
`The rewrite was rejected and the resident entries are retained in memory; the transcript persists again once you ${SESSION_LIMIT_RECOVERY_ACTIONS}.`,
|
|
2291
|
+
].join(" "),
|
|
2292
|
+
);
|
|
2293
|
+
this.name = "SessionNearLimitRewriteError";
|
|
2294
|
+
this.transcriptBytes = details.transcriptBytes;
|
|
2295
|
+
this.capBytes = details.capBytes;
|
|
2296
|
+
}
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2299
|
+
/**
|
|
2300
|
+
* Near-limit rejection from the managed transcript store.
|
|
2301
|
+
*
|
|
2302
|
+
* The store raises a bare `content_too_large` for any payload above the per-file
|
|
2303
|
+
* cap, and `#writeEntriesAtomicallySync` converts that same condition on the
|
|
2304
|
+
* rewrite lane into `SessionNearLimitRewriteError`. Both shapes mean the live
|
|
2305
|
+
* transcript crossed the cap and share one recovery path in `_persist`.
|
|
2306
|
+
*/
|
|
2307
|
+
function isManagedNearLimitFailure(error: unknown): boolean {
|
|
2308
|
+
return (
|
|
2309
|
+
(error instanceof Error && error.message === "content_too_large") || error instanceof SessionNearLimitRewriteError
|
|
2310
|
+
);
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2225
2313
|
export class SessionManagedStorageError extends Error {
|
|
2226
2314
|
readonly code = "managed_storage_unsupported";
|
|
2227
2315
|
|
|
@@ -2418,6 +2506,8 @@ export interface SessionInfo {
|
|
|
2418
2506
|
firstMessage: string;
|
|
2419
2507
|
allMessagesText: string;
|
|
2420
2508
|
}
|
|
2509
|
+
|
|
2510
|
+
const pickerDiskIdentities = new WeakMap<SessionInfo, SessionStorageStat>();
|
|
2421
2511
|
// =============================================================================
|
|
2422
2512
|
// Strict ACP authorization inventory (fail-closed, never partial authority)
|
|
2423
2513
|
// =============================================================================
|
|
@@ -3619,6 +3709,38 @@ function sameResumeStat(left: SessionStorageStat, right: SessionStorageStat): bo
|
|
|
3619
3709
|
);
|
|
3620
3710
|
}
|
|
3621
3711
|
|
|
3712
|
+
function pickerDiskIdentityMatchesStorageStat(expected: SessionStorageStat, observed: SessionStorageStat): boolean {
|
|
3713
|
+
return (
|
|
3714
|
+
expected.isFile &&
|
|
3715
|
+
observed.isFile &&
|
|
3716
|
+
expected.dev === observed.dev &&
|
|
3717
|
+
expected.ino === observed.ino &&
|
|
3718
|
+
expected.nlink === observed.nlink &&
|
|
3719
|
+
expected.size === observed.size &&
|
|
3720
|
+
expected.mtimeNs === observed.mtimeNs &&
|
|
3721
|
+
expected.ctimeNs === observed.ctimeNs
|
|
3722
|
+
);
|
|
3723
|
+
}
|
|
3724
|
+
|
|
3725
|
+
function pickerDiskIdentityMatchesManagedSnapshot(
|
|
3726
|
+
expected: SessionStorageStat,
|
|
3727
|
+
observed: Pick<SessionStorageStat, "dev" | "ino" | "nlink" | "size" | "mtimeNs" | "ctimeNs">,
|
|
3728
|
+
): boolean {
|
|
3729
|
+
return (
|
|
3730
|
+
expected.isFile &&
|
|
3731
|
+
expected.dev === observed.dev &&
|
|
3732
|
+
expected.ino === observed.ino &&
|
|
3733
|
+
expected.nlink === observed.nlink &&
|
|
3734
|
+
expected.size === observed.size &&
|
|
3735
|
+
expected.mtimeNs === observed.mtimeNs &&
|
|
3736
|
+
expected.ctimeNs === observed.ctimeNs
|
|
3737
|
+
);
|
|
3738
|
+
}
|
|
3739
|
+
|
|
3740
|
+
function pickerDiskIdentityMatchesDescriptor(expected: SessionStorageStat, observed: DescriptorSnapshot): boolean {
|
|
3741
|
+
return pickerDiskIdentityMatchesManagedSnapshot(expected, observed);
|
|
3742
|
+
}
|
|
3743
|
+
|
|
3622
3744
|
function resumeIdentityMatchesDescriptor(identity: ResumeSessionIdentity, descriptor: DescriptorSnapshot): boolean {
|
|
3623
3745
|
return (
|
|
3624
3746
|
identity.dev === descriptor.dev &&
|
|
@@ -6948,7 +7070,7 @@ async function collectSessionFromFile(
|
|
|
6948
7070
|
firstMessage ||= extractFirstUserMessageFromPrefix(content) ?? "";
|
|
6949
7071
|
const messageCount = Math.max(parsedMessageCount, countMessageMarkers(content));
|
|
6950
7072
|
const stats = storage.statSync(file);
|
|
6951
|
-
|
|
7073
|
+
const session: SessionInfo = {
|
|
6952
7074
|
path: file,
|
|
6953
7075
|
id: header.id,
|
|
6954
7076
|
cwd: header.cwd ?? "",
|
|
@@ -6963,6 +7085,8 @@ async function collectSessionFromFile(
|
|
|
6963
7085
|
firstMessage: firstMessage || "(no messages)",
|
|
6964
7086
|
allMessagesText: allMessages.length > 0 ? allMessages.join(" ") : firstMessage,
|
|
6965
7087
|
};
|
|
7088
|
+
pickerDiskIdentities.set(session, stats);
|
|
7089
|
+
return session;
|
|
6966
7090
|
} catch {
|
|
6967
7091
|
return undefined;
|
|
6968
7092
|
}
|
|
@@ -7081,6 +7205,7 @@ export const SessionManagerTestHooks: {
|
|
|
7081
7205
|
afterForkTranscriptPublished?: () => void | Promise<void>;
|
|
7082
7206
|
beforeEphemeralArtifactManagerInstall?: (dir: string) => void | Promise<void>;
|
|
7083
7207
|
beforePersistPatchFence?: (attempt: number) => void;
|
|
7208
|
+
beforeLivePickerStarFence?: () => void;
|
|
7084
7209
|
beforeStrictMissingCheck?: (filePath: string, storage: SessionStorage) => void;
|
|
7085
7210
|
beforeManagedResumeAcceptance?: (filePath: string, storage: SessionStorage) => void;
|
|
7086
7211
|
beforeManagedResumeReturn?: (filePath: string, storage: SessionStorage) => void;
|
|
@@ -15615,18 +15740,36 @@ export class SessionManager {
|
|
|
15615
15740
|
const bytes = Buffer.from(`${entries.map(entry => JSON.stringify(entry)).join("\n")}\n`, "utf8");
|
|
15616
15741
|
const store = this.#managedTranscriptStore(sessionFile);
|
|
15617
15742
|
const relativePath = path.basename(sessionFile);
|
|
15618
|
-
|
|
15619
|
-
|
|
15620
|
-
|
|
15621
|
-
|
|
15622
|
-
|
|
15623
|
-
|
|
15624
|
-
|
|
15625
|
-
|
|
15626
|
-
|
|
15627
|
-
|
|
15743
|
+
try {
|
|
15744
|
+
if (this.#managedPersistExpectedIdentity) {
|
|
15745
|
+
try {
|
|
15746
|
+
store.replaceExpectedIdentitySync(relativePath, bytes, this.#managedPersistExpectedIdentity);
|
|
15747
|
+
} catch (err) {
|
|
15748
|
+
// A confirmed missing predecessor can be recreated from the complete
|
|
15749
|
+
// resident transcript. Any present-but-different identity still fails
|
|
15750
|
+
// closed so a concurrent successor is never overwritten.
|
|
15751
|
+
if (!isEnoent(err)) throw err;
|
|
15752
|
+
this.#managedPersistExpectedIdentity = undefined;
|
|
15753
|
+
store.replaceSync(relativePath, bytes);
|
|
15754
|
+
}
|
|
15755
|
+
} else store.replaceSync(relativePath, bytes);
|
|
15756
|
+
} catch (err) {
|
|
15757
|
+
// A live transcript that no longer fits the managed per-file cap is
|
|
15758
|
+
// a typed near-limit outcome, not an unclassified abort: surface a
|
|
15759
|
+
// rewrite-scoped error instead of leaking a raw `content_too_large`
|
|
15760
|
+
// rejection to callers with no entry context. Resident entries stay
|
|
15761
|
+
// in memory, so the transcript persists again once the user runs one
|
|
15762
|
+
// of the actions in SESSION_LIMIT_RECOVERY_ACTIONS. Do not name the
|
|
15763
|
+
// commands here — a second copy of the advice is how `gjc export`
|
|
15764
|
+
// came back (#5732).
|
|
15765
|
+
if (err instanceof Error && err.message === "content_too_large") {
|
|
15766
|
+
throw new SessionNearLimitRewriteError({
|
|
15767
|
+
transcriptBytes: bytes.byteLength,
|
|
15768
|
+
capBytes: MANAGED_ARTIFACT_MAX_FILE_BYTES,
|
|
15769
|
+
});
|
|
15628
15770
|
}
|
|
15629
|
-
|
|
15771
|
+
throw err;
|
|
15772
|
+
}
|
|
15630
15773
|
const descriptor = store.descriptorExpected(relativePath);
|
|
15631
15774
|
if (!descriptor) throw new Error("managed_replace_identity_unavailable");
|
|
15632
15775
|
this.#managedPersistExpectedIdentity = this.#captureManagedPersistIdentity(sessionFile);
|
|
@@ -17316,6 +17459,190 @@ export class SessionManager {
|
|
|
17316
17459
|
}
|
|
17317
17460
|
}
|
|
17318
17461
|
|
|
17462
|
+
async setSessionStarredForPicker(session: SessionInfo, starred: boolean): Promise<void> {
|
|
17463
|
+
if (this.#sessionFile && canonicalizeTrustedPath(session.path) === canonicalizeTrustedPath(this.#sessionFile)) {
|
|
17464
|
+
await this.#setLivePickerStar(session, starred);
|
|
17465
|
+
return;
|
|
17466
|
+
}
|
|
17467
|
+
await SessionManager.setSessionStarredForPicker(
|
|
17468
|
+
session,
|
|
17469
|
+
starred,
|
|
17470
|
+
this.destination.kind === "explicit" ? this.destination.directory : undefined,
|
|
17471
|
+
);
|
|
17472
|
+
}
|
|
17473
|
+
|
|
17474
|
+
#assertPickerSessionIsCurrent(session: SessionInfo): void {
|
|
17475
|
+
if (
|
|
17476
|
+
!this.#sessionFile ||
|
|
17477
|
+
canonicalizeTrustedPath(session.path) !== canonicalizeTrustedPath(this.#sessionFile) ||
|
|
17478
|
+
session.id !== this.#sessionId
|
|
17479
|
+
)
|
|
17480
|
+
throw new Error("Selected session identity changed. Reopen the session picker.");
|
|
17481
|
+
if (session.cwd !== this.cwd) throw new Error("Selected session workspace changed. Reopen the session picker.");
|
|
17482
|
+
const expectedDiskIdentity = pickerDiskIdentities.get(session);
|
|
17483
|
+
if (expectedDiskIdentity) {
|
|
17484
|
+
let observed: SessionStorageStat;
|
|
17485
|
+
try {
|
|
17486
|
+
observed = this.#storage.statSync(this.#sessionFile);
|
|
17487
|
+
} catch {
|
|
17488
|
+
throw new Error("Selected session identity changed. Reopen the session picker.");
|
|
17489
|
+
}
|
|
17490
|
+
if (!pickerDiskIdentityMatchesStorageStat(expectedDiskIdentity, observed))
|
|
17491
|
+
throw new Error("Selected session identity changed. Reopen the session picker.");
|
|
17492
|
+
}
|
|
17493
|
+
}
|
|
17494
|
+
|
|
17495
|
+
async #setLivePickerStar(session: SessionInfo, starred: boolean): Promise<void> {
|
|
17496
|
+
this.#assertRecoveryHydrationWritable();
|
|
17497
|
+
this.#assertPickerSessionIsCurrent(session);
|
|
17498
|
+
const operation = this.#persistChain.then(() => {
|
|
17499
|
+
if (this.#persistError) throw this.#persistError;
|
|
17500
|
+
SessionManagerTestHooks.beforeLivePickerStarFence?.();
|
|
17501
|
+
this.#withSessionPersistenceFenceSync(() => {
|
|
17502
|
+
this.#assertRecoveryHydrationWritable();
|
|
17503
|
+
this.#assertPickerSessionIsCurrent(session);
|
|
17504
|
+
const sessionFile = this.#sessionFile!;
|
|
17505
|
+
const directory = path.dirname(sessionFile);
|
|
17506
|
+
const relativePath = path.basename(sessionFile);
|
|
17507
|
+
const authority = new ManagedSessionDescendantStore(managedDirectoryRoot(directory), directory);
|
|
17508
|
+
try {
|
|
17509
|
+
const descriptor = authority.descriptorExpected(relativePath);
|
|
17510
|
+
if (!descriptor) throw new Error("Selected session no longer exists.");
|
|
17511
|
+
if (descriptor.size > EAGER_RESUME_TRANSCRIPT_MAX_BYTES)
|
|
17512
|
+
throw new Error("Session is too large to star from the picker. Use /star or /unstar in the session.");
|
|
17513
|
+
const snapshot = authority.readExpected(relativePath);
|
|
17514
|
+
if (!snapshot) throw new Error("Selected session no longer exists.");
|
|
17515
|
+
const expectedDiskIdentity = pickerDiskIdentities.get(session);
|
|
17516
|
+
if (
|
|
17517
|
+
expectedDiskIdentity &&
|
|
17518
|
+
!pickerDiskIdentityMatchesManagedSnapshot(expectedDiskIdentity, snapshot.identity)
|
|
17519
|
+
)
|
|
17520
|
+
throw new Error("Selected session identity changed. Reopen the session picker.");
|
|
17521
|
+
const content = snapshot.bytes.toString("utf8");
|
|
17522
|
+
const newline = content.indexOf("\n");
|
|
17523
|
+
const rawHeader: unknown = JSON.parse(newline === -1 ? content : content.slice(0, newline));
|
|
17524
|
+
if (!isRecord(rawHeader) || rawHeader.type !== "session" || rawHeader.id !== session.id)
|
|
17525
|
+
throw new Error("Selected session identity changed. Reopen the session picker.");
|
|
17526
|
+
const entries = parseSessionEntries(content);
|
|
17527
|
+
const diskHeader = entries[0];
|
|
17528
|
+
if (diskHeader?.type !== "session" || diskHeader.cwd !== session.cwd)
|
|
17529
|
+
throw new Error("Selected session workspace changed. Reopen the session picker.");
|
|
17530
|
+
if ((diskHeader.version ?? 1) < 4)
|
|
17531
|
+
throw new Error("Session format requires an upgrade. Resume it before changing its star.");
|
|
17532
|
+
if ((diskHeader.starredPatchVersion === 1 && diskHeader.starred === true) === starred) return;
|
|
17533
|
+
|
|
17534
|
+
const patch = `${JSON.stringify({ type: "header_patch", patch: { starred } })}\n`;
|
|
17535
|
+
const appendOnly = rawHeader.starredPatchVersion === 1 && Number(rawHeader.version) >= 4;
|
|
17536
|
+
let bytes: Uint8Array;
|
|
17537
|
+
if (appendOnly) bytes = Buffer.from(`${content.endsWith("\n") ? "" : "\n"}${patch}`);
|
|
17538
|
+
else {
|
|
17539
|
+
rawHeader.starredPatchVersion = 1;
|
|
17540
|
+
delete rawHeader.starred;
|
|
17541
|
+
const body = newline === -1 ? "" : content.slice(newline + 1);
|
|
17542
|
+
bytes = Buffer.from(
|
|
17543
|
+
`${JSON.stringify(rawHeader)}\n${body}${body && !body.endsWith("\n") ? "\n" : ""}${patch}`,
|
|
17544
|
+
);
|
|
17545
|
+
}
|
|
17546
|
+
|
|
17547
|
+
this.#closePersistWriterInternalSync();
|
|
17548
|
+
if (appendOnly) authority.appendExpectedIdentitySync(relativePath, bytes, snapshot.identity);
|
|
17549
|
+
else authority.replaceExpectedIdentitySync(relativePath, bytes, snapshot.identity);
|
|
17550
|
+
const committedDescriptor = authority.descriptorExpected(relativePath);
|
|
17551
|
+
if (!committedDescriptor) throw new Error("Selected session no longer exists after star mutation.");
|
|
17552
|
+
const committedIdentity = this.#storage.statSync(sessionFile);
|
|
17553
|
+
if (!pickerDiskIdentityMatchesDescriptor(committedIdentity, committedDescriptor))
|
|
17554
|
+
throw new Error("Selected session identity changed after star mutation.");
|
|
17555
|
+
pickerDiskIdentities.set(session, committedIdentity);
|
|
17556
|
+
const liveHeader = this.#fileEntries.find(entry => entry.type === "session") as
|
|
17557
|
+
| SessionHeader
|
|
17558
|
+
| undefined;
|
|
17559
|
+
if (!liveHeader) throw new Error("Selected session identity changed. Reopen the session picker.");
|
|
17560
|
+
liveHeader.starredPatchVersion = 1;
|
|
17561
|
+
applyHeaderPatch(liveHeader, { starred });
|
|
17562
|
+
this.#headerExportRevision++;
|
|
17563
|
+
if (this.destination.kind === "managed") {
|
|
17564
|
+
this.#adoptManagedPersistIdentity(sessionFile);
|
|
17565
|
+
this.#publishCommitMarkerFromCurrentTranscriptSync();
|
|
17566
|
+
}
|
|
17567
|
+
} finally {
|
|
17568
|
+
authority.close();
|
|
17569
|
+
}
|
|
17570
|
+
});
|
|
17571
|
+
});
|
|
17572
|
+
this.#persistChain = operation.catch(() => {});
|
|
17573
|
+
await operation;
|
|
17574
|
+
}
|
|
17575
|
+
|
|
17576
|
+
/** Update only a picker candidate's metadata, without opening a runtime or publishing a resume breadcrumb. */
|
|
17577
|
+
static async setSessionStarredForPicker(
|
|
17578
|
+
session: SessionInfo,
|
|
17579
|
+
starred: boolean,
|
|
17580
|
+
explicitSessionDir?: string,
|
|
17581
|
+
): Promise<void> {
|
|
17582
|
+
if (isStagedSessionPath(session.path)) throw new Error("Staged session paths are not resumable");
|
|
17583
|
+
const directory = await fs.promises.realpath(path.dirname(session.path));
|
|
17584
|
+
const sessionPath = path.join(directory, path.basename(session.path));
|
|
17585
|
+
if (explicitSessionDir) {
|
|
17586
|
+
const root = await fs.promises.realpath(explicitSessionDir);
|
|
17587
|
+
if (!pathIsWithin(root, sessionPath)) throw new Error("Session is outside the configured session directory.");
|
|
17588
|
+
} else if (
|
|
17589
|
+
!isProjectSessionTranscriptPath(path.join(canonicalizeTrustedPath(session.cwd), ".gjc"), sessionPath)
|
|
17590
|
+
) {
|
|
17591
|
+
const sessionsRoot = path.dirname(directory);
|
|
17592
|
+
const resolved = resolveManagedScope({ cwd: session.cwd, agentDir: path.dirname(sessionsRoot), sessionsRoot });
|
|
17593
|
+
if (resolved.kind === "error") throw new Error(`Could not resolve managed session scope: ${resolved.message}`);
|
|
17594
|
+
const listing = listManagedCandidates(resolved.scope);
|
|
17595
|
+
if (
|
|
17596
|
+
listing.kind !== "complete" ||
|
|
17597
|
+
!listing.owned.some(candidate => path.resolve(candidate.path) === sessionPath)
|
|
17598
|
+
)
|
|
17599
|
+
throw new Error("Session is not an authorized managed candidate.");
|
|
17600
|
+
}
|
|
17601
|
+
const authority = new ManagedSessionDescendantStore(managedDirectoryRoot(directory), directory);
|
|
17602
|
+
try {
|
|
17603
|
+
const relativePath = path.basename(session.path);
|
|
17604
|
+
const descriptor = authority.descriptorExpected(relativePath);
|
|
17605
|
+
if (!descriptor) throw new Error("Selected session no longer exists.");
|
|
17606
|
+
if (descriptor.size > EAGER_RESUME_TRANSCRIPT_MAX_BYTES)
|
|
17607
|
+
throw new Error("Session is too large to star from the picker. Use /star or /unstar in the session.");
|
|
17608
|
+
const snapshot = authority.readExpected(relativePath);
|
|
17609
|
+
if (!snapshot) throw new Error("Selected session no longer exists.");
|
|
17610
|
+
const content = snapshot.bytes.toString("utf8");
|
|
17611
|
+
const newline = content.indexOf("\n");
|
|
17612
|
+
const rawHeader: unknown = JSON.parse(newline === -1 ? content : content.slice(0, newline));
|
|
17613
|
+
if (!isRecord(rawHeader) || rawHeader.type !== "session" || rawHeader.id !== session.id)
|
|
17614
|
+
throw new Error("Selected session identity changed. Reopen the session picker.");
|
|
17615
|
+
const entries = parseSessionEntries(content);
|
|
17616
|
+
const header = entries[0];
|
|
17617
|
+
if (header?.type !== "session" || header.cwd !== session.cwd)
|
|
17618
|
+
throw new Error("Selected session workspace changed. Reopen the session picker.");
|
|
17619
|
+
if ((header.version ?? 1) < 4)
|
|
17620
|
+
throw new Error("Session format requires an upgrade. Resume it before changing its star.");
|
|
17621
|
+
if ((header.starredPatchVersion === 1 && header.starred === true) === starred) return;
|
|
17622
|
+
|
|
17623
|
+
const patch = `${JSON.stringify({ type: "header_patch", patch: { starred } })}\n`;
|
|
17624
|
+
const appendOnly = rawHeader.starredPatchVersion === 1 && Number(rawHeader.version) >= 4;
|
|
17625
|
+
let bytes: Uint8Array;
|
|
17626
|
+
if (appendOnly) {
|
|
17627
|
+
bytes = Buffer.from(`${content.endsWith("\n") ? "" : "\n"}${patch}`);
|
|
17628
|
+
} else {
|
|
17629
|
+
rawHeader.starredPatchVersion = 1;
|
|
17630
|
+
delete rawHeader.starred;
|
|
17631
|
+
const body = newline === -1 ? "" : content.slice(newline + 1);
|
|
17632
|
+
bytes = Buffer.from(
|
|
17633
|
+
`${JSON.stringify(rawHeader)}\n${body}${body && !body.endsWith("\n") ? "\n" : ""}${patch}`,
|
|
17634
|
+
);
|
|
17635
|
+
}
|
|
17636
|
+
if (appendOnly) authority.appendExpectedIdentitySync(relativePath, bytes, snapshot.identity);
|
|
17637
|
+
else authority.replaceExpectedIdentitySync(relativePath, bytes, snapshot.identity);
|
|
17638
|
+
const committedDescriptor = authority.descriptorExpected(relativePath);
|
|
17639
|
+
if (!committedDescriptor) throw new Error("Selected session no longer exists after star mutation.");
|
|
17640
|
+
pickerDiskIdentities.set(session, descriptorSnapshotAsStorageStat(committedDescriptor));
|
|
17641
|
+
} finally {
|
|
17642
|
+
authority.close();
|
|
17643
|
+
}
|
|
17644
|
+
}
|
|
17645
|
+
|
|
17319
17646
|
async #ensureStarPatchCapability(header: SessionHeader): Promise<void> {
|
|
17320
17647
|
if (header.starredPatchVersion === 1) return;
|
|
17321
17648
|
const previousStarred = header.starred;
|
|
@@ -17363,6 +17690,7 @@ export class SessionManager {
|
|
|
17363
17690
|
const sanitized = SessionManager.#sanitizeName(name);
|
|
17364
17691
|
if (!sanitized) return false;
|
|
17365
17692
|
|
|
17693
|
+
invalidateSessionTitleGeneration(this);
|
|
17366
17694
|
this.#sessionName = sanitized;
|
|
17367
17695
|
this.#titleSource = source;
|
|
17368
17696
|
await this.#appendHeaderPatch({ title: sanitized, titleSource: source });
|
|
@@ -17558,21 +17886,24 @@ export class SessionManager {
|
|
|
17558
17886
|
if (publishResumeBreadcrumb && persisted) writeTerminalBreadcrumb(this.cwd, this.#sessionFile);
|
|
17559
17887
|
if (persisted) this.#readOnlyResume = false;
|
|
17560
17888
|
} catch (err) {
|
|
17561
|
-
//
|
|
17562
|
-
// transcript
|
|
17563
|
-
//
|
|
17564
|
-
//
|
|
17565
|
-
//
|
|
17566
|
-
//
|
|
17567
|
-
//
|
|
17568
|
-
//
|
|
17569
|
-
|
|
17570
|
-
|
|
17571
|
-
|
|
17572
|
-
|
|
17573
|
-
|
|
17574
|
-
|
|
17575
|
-
//
|
|
17889
|
+
// Near-limit failure on the managed append hot path. Two shapes reach here,
|
|
17890
|
+
// both meaning the managed transcript can no longer grow at its per-file cap:
|
|
17891
|
+
// - the appended bytes alone exceed the cap (`content_too_large` from the
|
|
17892
|
+
// store's append, #4566); or
|
|
17893
|
+
// - the ENOENT / missing-predecessor recovery rewrite inside
|
|
17894
|
+
// #appendManagedRecordsSync raised SessionNearLimitRewriteError because the
|
|
17895
|
+
// resident transcript itself no longer fits.
|
|
17896
|
+
// Recovery is the same for both: rewrite only the live in-memory entries (the
|
|
17897
|
+
// append-only file grew past the limit even though the in-memory entry list
|
|
17898
|
+
// may be much smaller, since compaction evicts old content but the file never
|
|
17899
|
+
// shrinks until a full rewrite) and verify the just-appended entry is actually
|
|
17900
|
+
// durable. The entry has already been added to #fileEntries by
|
|
17901
|
+
// #appendEntryWithinPersistenceFence.
|
|
17902
|
+
if (isManagedNearLimitFailure(err)) {
|
|
17903
|
+
// Typed near-limit contract (#4566): when even the rewrite cannot fit the
|
|
17904
|
+
// entry (live content alone is at the cap), surface the typed near-limit
|
|
17905
|
+
// outcome instead of silently succeeding without the receipt for an effect
|
|
17906
|
+
// that already committed (e.g. a source edit).
|
|
17576
17907
|
const entryBytes = (() => {
|
|
17577
17908
|
try {
|
|
17578
17909
|
const materialized = materializeResidentEntryForPersistenceSync(
|
|
@@ -17588,7 +17919,23 @@ export class SessionManager {
|
|
|
17588
17919
|
return 0;
|
|
17589
17920
|
}
|
|
17590
17921
|
})();
|
|
17591
|
-
const
|
|
17922
|
+
const nearLimit = (liveBytes: number) =>
|
|
17923
|
+
new SessionNearLimitAppendError({
|
|
17924
|
+
entryBytes,
|
|
17925
|
+
liveBytes,
|
|
17926
|
+
capBytes: MANAGED_ARTIFACT_MAX_FILE_BYTES,
|
|
17927
|
+
entryRetained: this.#byId.has(entry.id),
|
|
17928
|
+
});
|
|
17929
|
+
// A rewrite-scoped overflow reports the rejected live-transcript size and
|
|
17930
|
+
// proves the rewrite already ran and failed, so it is not repeated: the
|
|
17931
|
+
// on-disk size is authoritative only for a rejected append, where the
|
|
17932
|
+
// recovery rewrite has not run yet.
|
|
17933
|
+
const liveBytesBefore =
|
|
17934
|
+
err instanceof SessionNearLimitRewriteError ? err.transcriptBytes : this.getTranscriptFileBytes();
|
|
17935
|
+
if (err instanceof SessionNearLimitRewriteError) {
|
|
17936
|
+
this.#needsFullRewriteOnNextPersist = true;
|
|
17937
|
+
throw nearLimit(liveBytesBefore);
|
|
17938
|
+
}
|
|
17592
17939
|
try {
|
|
17593
17940
|
this.#rewriteFileSync();
|
|
17594
17941
|
} catch (rewriteError) {
|
|
@@ -17596,14 +17943,9 @@ export class SessionManager {
|
|
|
17596
17943
|
// the resident list (its effect, including any committed source
|
|
17597
17944
|
// edit, is not lost), but the receipt is not durable yet: report
|
|
17598
17945
|
// the typed near-limit outcome instead of an unclassified abort.
|
|
17599
|
-
if (rewriteError instanceof
|
|
17946
|
+
if (rewriteError instanceof SessionNearLimitRewriteError) {
|
|
17600
17947
|
this.#needsFullRewriteOnNextPersist = true;
|
|
17601
|
-
throw
|
|
17602
|
-
entryBytes,
|
|
17603
|
-
liveBytes: liveBytesBefore,
|
|
17604
|
-
capBytes: MANAGED_ARTIFACT_MAX_FILE_BYTES,
|
|
17605
|
-
entryRetained: this.#byId.has(entry.id),
|
|
17606
|
-
});
|
|
17948
|
+
throw nearLimit(liveBytesBefore);
|
|
17607
17949
|
}
|
|
17608
17950
|
throw rewriteError;
|
|
17609
17951
|
}
|
|
@@ -17616,12 +17958,7 @@ export class SessionManager {
|
|
|
17616
17958
|
const entryRetained = liveBytesAfter <= MANAGED_ARTIFACT_MAX_FILE_BYTES && this.#byId.has(entry.id);
|
|
17617
17959
|
if (!entryRetained) {
|
|
17618
17960
|
this.#needsFullRewriteOnNextPersist = true;
|
|
17619
|
-
throw
|
|
17620
|
-
entryBytes,
|
|
17621
|
-
liveBytes: liveBytesAfter || liveBytesBefore,
|
|
17622
|
-
capBytes: MANAGED_ARTIFACT_MAX_FILE_BYTES,
|
|
17623
|
-
entryRetained: this.#byId.has(entry.id),
|
|
17624
|
-
});
|
|
17961
|
+
throw nearLimit(liveBytesAfter || liveBytesBefore);
|
|
17625
17962
|
}
|
|
17626
17963
|
return;
|
|
17627
17964
|
}
|
|
@@ -17887,6 +18224,7 @@ export class SessionManager {
|
|
|
17887
18224
|
| PythonExecutionMessage
|
|
17888
18225
|
| FileMentionMessage,
|
|
17889
18226
|
): string {
|
|
18227
|
+
if (message.role === "user") invalidateSessionTitleGeneration(this);
|
|
17890
18228
|
const entry: SessionMessageEntry = {
|
|
17891
18229
|
type: "message",
|
|
17892
18230
|
id: this.#generateEntryId(),
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One authoritative, reference-counted lease for work that still needs a
|
|
3
|
+
* session host. Each admitted work state owns one handle and releases that
|
|
4
|
+
* handle at its terminal boundary; the host only observes whether any handle
|
|
5
|
+
* remains held.
|
|
6
|
+
*/
|
|
7
|
+
export interface SessionWorkLeaseHandle {
|
|
8
|
+
release(): void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface SessionWorkLease {
|
|
12
|
+
acquire(): SessionWorkLeaseHandle;
|
|
13
|
+
isHeld(): boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function createSessionWorkLease(): SessionWorkLease {
|
|
17
|
+
let holders = 0;
|
|
18
|
+
return {
|
|
19
|
+
acquire(): SessionWorkLeaseHandle {
|
|
20
|
+
holders += 1;
|
|
21
|
+
let released = false;
|
|
22
|
+
return {
|
|
23
|
+
release(): void {
|
|
24
|
+
if (released) return;
|
|
25
|
+
released = true;
|
|
26
|
+
holders = Math.max(0, holders - 1);
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
isHeld(): boolean {
|
|
31
|
+
return holders > 0;
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -25,8 +25,18 @@ const MAX_SESSION_INDEX_LINE_BYTES = 64 * 1024;
|
|
|
25
25
|
const MAX_SESSION_TITLE_CHARACTERS = 200;
|
|
26
26
|
const SENSITIVE_KEY = /(?:api[-_]?key|authorization|cookie|credential|password|secret|token)/iu;
|
|
27
27
|
const CODEX_SESSION_ID = /^[A-Za-z0-9-]{1,128}$/u;
|
|
28
|
+
// Kept in step with the shape catalogue the sibling importer scrubber carries
|
|
29
|
+
// (`./redact.ts`). Both read the same class of external transcript, so a shape
|
|
30
|
+
// either importer recognizes has to be recognized by the other.
|
|
31
|
+
// Stripe restricted keys use `rk_`, which no other alternative reaches: the
|
|
32
|
+
// generic `sk[-_]` branch covers `sk_live_`/`sk_test_` only because they share
|
|
33
|
+
// the `sk` prefix. The sibling lists `(?:sk|rk)_(?:live|test)_` explicitly.
|
|
28
34
|
const SECRET_VALUE =
|
|
29
|
-
/(?:Bearer\s+[A-Za-z0-9._~+/=-]{8,}|\b(?:sk[-_]|
|
|
35
|
+
/(?:Bearer\s+[A-Za-z0-9._~+/=-]{8,}|\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b|\b(?:sk[-_]|gh[opsur]_|github_pat_)[A-Za-z0-9_-]{12,}|\b(?:AKIA|ASIA|ABIA|ACCA)[A-Z0-9]{16}\b|\bAIza[0-9A-Za-z_-]{35}\b|\bxox[baprs]-[A-Za-z0-9-]{10,}|\bnpm_[A-Za-z0-9]{20,}\b|\bglpat-[A-Za-z0-9_-]{20,}|\bhf_[A-Za-z0-9]{20,}\b)/giu;
|
|
36
|
+
// Cheap pre-check that gates the scan above. It must stay a superset of
|
|
37
|
+
// SECRET_VALUE's prefixes or a widened pattern would never run.
|
|
38
|
+
const SECRET_VALUE_HINT =
|
|
39
|
+
/(?:Bearer\s|sk[-_]|rk_(?:live|test)_|gh[opsur]_|github_pat_|AKIA|ASIA|ABIA|ACCA|AIza|xox[baprs]-|npm_|glpat-|hf_)/iu;
|
|
30
40
|
const JWT_VALUE = /\b[A-Za-z0-9_-]{8,2048}\.[A-Za-z0-9_-]{8,8192}\.[A-Za-z0-9_-]{8,2048}\b/gu;
|
|
31
41
|
const SECRET_ASSIGNMENT =
|
|
32
42
|
/(\b(?:password|passwd|secret|token|api[-_]?key|access[-_]?key)\b\s*[:=]\s*)(?:"[^"\r\n]{4,4096}"|'[^'\r\n]{4,4096}'|[^\s,;]{4,4096})/giu;
|
|
@@ -181,8 +191,7 @@ export function sanitizeImportedString(value: string): { value: string; redacted
|
|
|
181
191
|
return `${prefix}${scheme ? `${scheme} ${REDACTED_CREDENTIAL}` : REDACTED_CREDENTIAL}`;
|
|
182
192
|
});
|
|
183
193
|
}
|
|
184
|
-
if (
|
|
185
|
-
next = replace(next, SECRET_VALUE, "[redacted-secret]");
|
|
194
|
+
if (SECRET_VALUE_HINT.test(next)) next = replace(next, SECRET_VALUE, "[redacted-secret]");
|
|
186
195
|
if (next.includes(".")) next = replace(next, JWT_VALUE, "[redacted-secret]");
|
|
187
196
|
if (/(?:password|passwd|secret|token|api[-_]?key|access[-_]?key)\s*[:=]/iu.test(next)) {
|
|
188
197
|
next = next.replace(SECRET_ASSIGNMENT, (_match, prefix: string) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { formatProviderCredentialHint } from "@gajae-code/ai/stream";
|
|
2
2
|
|
|
3
3
|
export const MODEL_ONBOARDING_API_PROVIDER_COMMAND =
|
|
4
|
-
"/provider add --compat <openai|anthropic> --provider <id> --base-url <url> --api-key-env <ENV> --model <model>";
|
|
4
|
+
"/provider add --compat <openai|anthropic> --provider <id> --base-url <url> --api-key-env <ENV> (--model <model> | --discover)";
|
|
5
5
|
export const MODEL_ONBOARDING_PROVIDER_PRESET_COMMAND = "/provider add --preset <id>";
|
|
6
6
|
|
|
7
7
|
export const MODEL_ONBOARDING_SETUP_COMMAND = "gjc setup provider";
|