@gajae-code/coding-agent 0.14.2 → 0.15.0
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 +120 -1
- package/dist/types/async/job-manager.d.ts +4 -1
- package/dist/types/autoresearch/dashboard.d.ts +29 -4
- package/dist/types/autoresearch/data-context.d.ts +25 -0
- package/dist/types/autoresearch/git.d.ts +65 -7
- package/dist/types/autoresearch/harness.d.ts +58 -0
- package/dist/types/autoresearch/prompts.d.ts +51 -0
- package/dist/types/autoresearch/runs.d.ts +173 -0
- package/dist/types/autoresearch/types.d.ts +9 -139
- package/dist/types/capability/types.d.ts +20 -0
- package/dist/types/cli/accounts-cli.d.ts +1 -0
- package/dist/types/commands/{rlm.d.ts → autoresearch.d.ts} +2 -4
- package/dist/types/config/autorouting-contract.d.ts +146 -0
- package/dist/types/config/autorouting-generator.d.ts +40 -0
- package/dist/types/config/autorouting-tier-map.d.ts +16844 -0
- package/dist/types/config/autorouting.d.ts +35 -0
- package/dist/types/config/model-discovery-manager.d.ts +8 -1
- package/dist/types/config/model-registry.d.ts +30 -1
- package/dist/types/config/provider-selection-policy.d.ts +23 -0
- package/dist/types/config/settings-schema.d.ts +61 -3
- package/dist/types/config/settings.d.ts +3 -0
- package/dist/types/coordinator/contract.d.ts +1 -1
- package/dist/types/coordinator-mcp/durability.d.ts +29 -0
- package/dist/types/coordinator-mcp/event-webhook.d.ts +50 -0
- package/dist/types/coordinator-mcp/question-state.d.ts +1 -1
- package/dist/types/coordinator-mcp/server.d.ts +14 -0
- package/dist/types/crash/index-store.d.ts +3 -0
- package/dist/types/crash/record-loader.d.ts +2 -0
- package/dist/types/crash/upstream/relay.d.ts +5 -3
- package/dist/types/cursor.d.ts +12 -0
- package/dist/types/defaults/gjc-defaults.d.ts +35 -1
- package/dist/types/defaults/gjc-skills.generated.d.ts +1 -1
- package/dist/types/discovery/helpers.d.ts +1 -1
- package/dist/types/edit/index.d.ts +2 -1
- package/dist/types/eval/py/executor.d.ts +2 -0
- package/dist/types/extensibility/extensions/loader.d.ts +32 -0
- package/dist/types/extensibility/gjc-plugins/types.d.ts +1 -1
- package/dist/types/extensibility/skills.d.ts +2 -0
- package/dist/types/gjc-runtime/autoresearch-runtime.d.ts +194 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +1 -1
- package/dist/types/gjc-runtime/psmux-detect.d.ts +1 -1
- package/dist/types/gjc-runtime/python-transcript.d.ts +19 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +2 -8
- package/dist/types/gjc-runtime/session-layout.d.ts +6 -3
- package/dist/types/gjc-runtime/state-renderer.d.ts +0 -22
- package/dist/types/gjc-runtime/state-schema.d.ts +7 -7
- package/dist/types/gjc-runtime/tmux-common.d.ts +2 -2
- package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +38 -1
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +2 -24
- package/dist/types/gjc-runtime/ultragoal-validation-policy.d.ts +73 -0
- package/dist/types/gjc-runtime/workflow-recovery-projection.d.ts +100 -0
- package/dist/types/hooks/skill-keywords.d.ts +1 -1
- package/dist/types/main.d.ts +0 -10
- package/dist/types/modes/acp/acp-agent.d.ts +13 -6
- package/dist/types/modes/components/model-selector.d.ts +14 -0
- package/dist/types/modes/components/skill-hud/render.d.ts +6 -1
- package/dist/types/modes/components/smart-routing-panel.d.ts +82 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +14 -0
- package/dist/types/modes/interactive-mode.d.ts +3 -0
- package/dist/types/modes/types.d.ts +9 -0
- package/dist/types/rlm/artifacts.d.ts +4 -4
- package/dist/types/runtime/optional-runtime-services.d.ts +2 -1
- package/dist/types/runtime/workspace-tree-service.d.ts +5 -1
- package/dist/types/runtime-mcp/config.d.ts +6 -0
- package/dist/types/runtime-mcp/manager.d.ts +10 -0
- package/dist/types/sdk/acp/adapter.d.ts +0 -4
- package/dist/types/sdk/broker/broker.d.ts +1 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +57 -2
- package/dist/types/sdk/broker/session-index.d.ts +15 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +6 -0
- package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +4 -0
- package/dist/types/sdk/bus/index.d.ts +8 -0
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +2 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +12 -2
- package/dist/types/sdk/client/client.d.ts +39 -4
- package/dist/types/sdk/host/host.d.ts +1 -0
- package/dist/types/sdk/host/internal-autorouting-state.d.ts +3 -0
- package/dist/types/sdk/host/model-pin.d.ts +29 -0
- package/dist/types/sdk/host/session-runtime.d.ts +2 -1
- package/dist/types/sdk/lifecycle-session.d.ts +13 -0
- package/dist/types/sdk/router/session-router.d.ts +27 -1
- package/dist/types/sdk/session.d.ts +11 -1
- package/dist/types/session/agent-session.d.ts +36 -6
- package/dist/types/session/artifacts.d.ts +16 -1
- package/dist/types/session/internal/managed-session-storage.d.ts +7 -0
- package/dist/types/session/session-manager.d.ts +84 -2
- package/dist/types/session/session-staging-paths.d.ts +3 -0
- package/dist/types/skill-state/canonical-skills.d.ts +1 -1
- package/dist/types/skill-state/workflow-hud.d.ts +12 -21
- package/dist/types/skill-state/workflow-mutation-guard.d.ts +1 -0
- package/dist/types/slash-commands/helpers/autorouting-status.d.ts +8 -0
- package/dist/types/system-prompt.d.ts +23 -0
- package/dist/types/task/executor.d.ts +35 -4
- package/dist/types/task/index.d.ts +17 -3
- package/dist/types/task/receipt.d.ts +3 -1
- package/dist/types/task/types.d.ts +144 -0
- package/dist/types/tools/atomic-file-write.d.ts +72 -0
- package/dist/types/tools/descriptors.d.ts +1 -0
- package/dist/types/tools/index.d.ts +36 -1
- package/dist/types/tools/move-session.d.ts +40 -0
- package/dist/types/tools/python.d.ts +12 -0
- package/dist/types/tools/resource-gc.d.ts +0 -3
- package/dist/types/tools/search.d.ts +8 -1
- package/dist/types/tools/skill.d.ts +9 -4
- package/dist/types/tools/subagent.d.ts +33 -3
- package/dist/types/utils/xml-escape.d.ts +2 -0
- package/dist/types/workflow/workflow-intent-diff.d.ts +2 -2
- package/dist/types/workflow/workflow-intent-report-consumer.d.ts +1 -1
- package/package.json +11 -12
- package/scripts/build-sdk-package-smoke.ts +7 -5
- package/scripts/check-autorouting-tier-map.ts +183 -0
- package/scripts/generate-sdk-operation-inventory.ts +12 -0
- package/scripts/generate-tool-catalog.ts +40 -5
- package/src/async/job-manager.ts +5 -2
- package/src/autoresearch/dashboard.ts +134 -314
- package/src/autoresearch/data-context.ts +35 -0
- package/src/autoresearch/git.ts +186 -26
- package/src/autoresearch/{helpers.ts → harness.ts} +65 -40
- package/src/autoresearch/{prompt-setup.md → prompts/prompt-setup.md} +7 -7
- package/src/autoresearch/prompts/prompt.md +103 -0
- package/src/autoresearch/prompts.ts +126 -0
- package/src/autoresearch/runs.ts +708 -0
- package/src/autoresearch/types.ts +9 -150
- package/src/capability/index.ts +4 -4
- package/src/capability/types.ts +20 -0
- package/src/cli/accounts-cli.ts +21 -5
- package/src/cli/config-cli.ts +25 -1
- package/src/cli/fast-help.ts +1 -2
- package/src/cli/list-models.ts +5 -8
- package/src/cli/setup-cli.ts +8 -0
- package/src/cli.ts +2 -21
- package/src/commands/autoresearch.ts +33 -0
- package/src/commands/coordinator.ts +15 -0
- package/src/commands/launch.ts +2 -4
- package/src/commands/quick-lane.ts +1 -1
- package/src/commands/sdk.ts +24 -0
- package/src/commands/state.ts +1 -1
- package/src/config/autorouting-contract.ts +474 -0
- package/src/config/autorouting-generator.ts +238 -0
- package/src/config/autorouting-tier-map.ts +6108 -0
- package/src/config/autorouting.ts +120 -0
- package/src/config/model-discovery-manager.ts +42 -11
- package/src/config/model-registry.ts +348 -68
- package/src/config/provider-selection-policy.ts +60 -9
- package/src/config/settings-schema.ts +195 -6
- package/src/config/settings.ts +62 -1
- package/src/config.ts +2 -3
- package/src/coordinator/contract.ts +0 -1
- package/src/coordinator-mcp/codex-handoff.ts +68 -42
- package/src/coordinator-mcp/durability.ts +200 -0
- package/src/coordinator-mcp/event-webhook.ts +278 -0
- package/src/coordinator-mcp/question-state.ts +109 -46
- package/src/coordinator-mcp/server.ts +932 -504
- package/src/crash/index-store.ts +126 -22
- package/src/crash/record-loader.ts +7 -0
- package/src/crash/upstream/relay.ts +82 -34
- package/src/cursor.ts +84 -1
- package/src/debug/index.ts +26 -0
- package/src/defaults/gjc/skills/autoresearch/SKILL.md +116 -0
- package/src/defaults/gjc/skills/autoresearch/auto-critic.md +46 -0
- package/src/defaults/gjc/skills/autoresearch/auto-iterate.md +42 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +17 -37
- package/src/defaults/gjc/skills/ralplan/SKILL.md +21 -18
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +6 -17
- package/src/defaults/gjc/skills/ultragoal/ai-slop-cleaner.md +2 -2
- package/src/defaults/gjc-defaults.ts +89 -2
- package/src/defaults/gjc-skills.generated.ts +24 -17
- package/src/discovery/builtin.ts +18 -5
- package/src/discovery/helpers.ts +6 -6
- package/src/edit/index.ts +5 -2
- package/src/eval/py/executor.ts +8 -0
- package/src/extensibility/extensions/loader.ts +135 -13
- package/src/extensibility/extensions/wrapper.ts +1 -2
- package/src/extensibility/gjc-plugins/schema.ts +1 -1
- package/src/extensibility/runtime-skill-discovery.ts +2 -2
- package/src/extensibility/skill-management.ts +2 -3
- package/src/extensibility/skills.ts +16 -8
- package/src/gjc-runtime/autoresearch-runtime.ts +1509 -0
- package/src/gjc-runtime/gc-render.ts +0 -1
- package/src/gjc-runtime/gc-runtime.ts +1 -11
- package/src/gjc-runtime/launch-tmux.ts +3 -3
- package/src/gjc-runtime/psmux-detect.ts +2 -2
- package/src/gjc-runtime/python-transcript.ts +58 -0
- package/src/gjc-runtime/ralplan-runtime.ts +6 -18
- package/src/gjc-runtime/session-layout.ts +18 -7
- package/src/gjc-runtime/session-state-sidecar.ts +58 -9
- package/src/gjc-runtime/state-renderer.ts +0 -38
- package/src/gjc-runtime/state-runtime.ts +43 -22
- package/src/gjc-runtime/state-schema.ts +1 -1
- package/src/gjc-runtime/tmux-common.ts +7 -7
- package/src/gjc-runtime/ultragoal-change-set.ts +232 -11
- package/src/gjc-runtime/ultragoal-runtime.ts +309 -114
- package/src/gjc-runtime/ultragoal-validation-policy.ts +247 -0
- package/src/gjc-runtime/workflow-command-ref.ts +15 -11
- package/src/gjc-runtime/workflow-manifest.generated.json +413 -532
- package/src/gjc-runtime/workflow-manifest.ts +43 -83
- package/src/gjc-runtime/workflow-recovery-projection.ts +616 -0
- package/src/hooks/skill-keywords.ts +3 -3
- package/src/hooks/skill-state.ts +2 -2
- package/src/internal-urls/docs-index.generated.ts +26 -24
- package/src/lsp/index.ts +25 -5
- package/src/main.ts +2 -27
- package/src/modes/acp/acp-agent.ts +200 -75
- package/src/modes/components/model-selector.ts +216 -13
- package/src/modes/components/oauth-selector.ts +5 -5
- package/src/modes/components/skill-hud/render.ts +173 -35
- package/src/modes/components/smart-routing-panel.ts +531 -0
- package/src/modes/components/tool-status-header.ts +6 -3
- package/src/modes/controllers/command-controller.ts +17 -7
- package/src/modes/controllers/input-controller.ts +44 -9
- package/src/modes/controllers/runtime-mcp-command-controller.ts +4 -0
- package/src/modes/controllers/selector-controller.ts +298 -65
- package/src/modes/interactive-mode.ts +8 -1
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +1 -1
- package/src/modes/types.ts +9 -1
- package/src/prompts/agents/planner.md +1 -1
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/prompts/system/volatile-project-context.md +2 -1
- package/src/prompts/tools/move-session.md +15 -0
- package/src/prompts/tools/python.md +12 -0
- package/src/prompts/tools/skill-discovery.md +1 -1
- package/src/prompts/tools/skill.md +2 -2
- package/src/prompts/tools/task-summary.md +1 -0
- package/src/prompts/tools/task.md +6 -0
- package/src/rlm/artifacts.ts +12 -10
- package/src/runtime/optional-runtime-services.ts +3 -2
- package/src/runtime/workspace-tree-service.ts +11 -3
- package/src/runtime-mcp/config.ts +8 -1
- package/src/runtime-mcp/manager.ts +33 -0
- package/src/sdk/acp/adapter.ts +48 -1
- package/src/sdk/broker/broker.ts +30 -2
- package/src/sdk/broker/discovery.ts +217 -2
- package/src/sdk/broker/lifecycle.ts +385 -59
- package/src/sdk/broker/session-index.ts +243 -48
- package/src/sdk/broker/transport.ts +1 -0
- package/src/sdk/bus/chat-daemon-control.ts +8 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +5 -0
- package/src/sdk/bus/index.ts +208 -11
- package/src/sdk/bus/kind-aware-reconciliation.ts +16 -0
- package/src/sdk/bus/reconciliation-store.ts +27 -5
- package/src/sdk/bus/telegram-daemon-contract.ts +12 -2
- package/src/sdk/client/client.ts +204 -22
- package/src/sdk/host/host.ts +16 -0
- package/src/sdk/host/internal-autorouting-state.ts +13 -0
- package/src/sdk/host/model-pin.ts +101 -0
- package/src/sdk/host/reverse-leases.ts +2 -1
- package/src/sdk/host/session-runtime.test.ts +224 -1
- package/src/sdk/host/session-runtime.ts +125 -37
- package/src/sdk/lifecycle/service.ts +3 -1
- package/src/sdk/lifecycle-session.ts +42 -1
- package/src/sdk/protocol/operation-inventory.generated.json +66 -0
- package/src/sdk/router/session-router.ts +375 -36
- package/src/sdk/session.ts +547 -47
- package/src/session/account-inventory.ts +1 -1
- package/src/session/agent-session.ts +765 -189
- package/src/session/artifacts.ts +234 -4
- package/src/session/internal/managed-session-storage.ts +91 -4
- package/src/session/provider-safety-stop-hint.ts +3 -2
- package/src/session/session-manager.ts +775 -16
- package/src/session/session-staging-paths.ts +8 -0
- package/src/setup/hermes/templates/operator-instructions.v1.md +0 -1
- package/src/setup/host-plugin-setup.ts +2 -2
- package/src/skill-state/active-state.ts +2 -2
- package/src/skill-state/canonical-skills.ts +1 -1
- package/src/skill-state/initial-phase.ts +1 -1
- package/src/skill-state/workflow-hud.ts +41 -41
- package/src/skill-state/workflow-mutation-guard.ts +26 -7
- package/src/slash-commands/builtin-registry.ts +77 -0
- package/src/slash-commands/helpers/autorouting-status.ts +55 -0
- package/src/system-prompt.ts +102 -5
- package/src/task/discovery.ts +2 -3
- package/src/task/executor.ts +617 -43
- package/src/task/index.ts +262 -15
- package/src/task/receipt.ts +30 -0
- package/src/task/render.ts +11 -1
- package/src/task/types.ts +316 -0
- package/src/tools/atomic-file-write.ts +594 -0
- package/src/tools/bisect.ts +3 -2
- package/src/tools/descriptors.test.ts +3 -0
- package/src/tools/descriptors.ts +24 -0
- package/src/tools/image-gen.ts +5 -1
- package/src/tools/index.ts +62 -5
- package/src/tools/move-session.ts +107 -0
- package/src/tools/python.ts +205 -0
- package/src/tools/read.ts +88 -15
- package/src/tools/renderers.ts +2 -0
- package/src/tools/resource-gc.ts +8 -177
- package/src/tools/search.ts +30 -6
- package/src/tools/skill-discovery.ts +1 -0
- package/src/tools/skill.ts +37 -7
- package/src/tools/subagent-render.ts +119 -13
- package/src/tools/subagent.ts +91 -74
- package/src/tools/tool-catalog.generated.ts +64 -2
- package/src/tools/write.ts +16 -12
- package/src/utils/xml-escape.ts +14 -0
- package/src/web/search/providers/codex.ts +2 -2
- package/src/workflow/workflow-intent-diff.ts +13 -12
- package/src/workflow/workflow-intent-report-consumer.ts +6 -6
- package/dist/types/autoresearch/helpers.d.ts +0 -24
- package/dist/types/autoresearch/index.d.ts +0 -2
- package/dist/types/autoresearch/state.d.ts +0 -17
- package/dist/types/autoresearch/storage.d.ts +0 -142
- package/dist/types/autoresearch/tools/init-experiment.d.ts +0 -31
- package/dist/types/autoresearch/tools/log-experiment.d.ts +0 -23
- package/dist/types/autoresearch/tools/run-experiment.d.ts +0 -8
- package/dist/types/autoresearch/tools/update-notes.d.ts +0 -12
- package/dist/types/commands/team.d.ts +0 -24
- package/dist/types/gjc-runtime/team-gc.d.ts +0 -7
- package/dist/types/gjc-runtime/team-launch.d.ts +0 -61
- package/dist/types/gjc-runtime/team-notify.d.ts +0 -45
- package/dist/types/gjc-runtime/team-runtime.d.ts +0 -336
- package/dist/types/gjc-runtime/team-store.d.ts +0 -168
- package/dist/types/gjc-runtime/team-worker-heartbeat.d.ts +0 -43
- package/dist/types/gjc-runtime/team-worker-memory-guard.d.ts +0 -92
- package/dist/types/gjc-runtime/team-workers.d.ts +0 -83
- package/dist/types/rlm/index.d.ts +0 -47
- package/dist/types/rlm/preset.d.ts +0 -23
- package/dist/types/rlm/python-tool.d.ts +0 -16
- package/src/autoresearch/command-resume.md +0 -14
- package/src/autoresearch/index.ts +0 -536
- package/src/autoresearch/prompt.md +0 -103
- package/src/autoresearch/resume-message.md +0 -10
- package/src/autoresearch/state.ts +0 -273
- package/src/autoresearch/storage.ts +0 -699
- package/src/autoresearch/tools/init-experiment.ts +0 -272
- package/src/autoresearch/tools/log-experiment.ts +0 -524
- package/src/autoresearch/tools/run-experiment.ts +0 -475
- package/src/autoresearch/tools/update-notes.ts +0 -109
- package/src/commands/rlm.ts +0 -19
- package/src/commands/team.ts +0 -238
- package/src/defaults/gjc/skills/deep-interview/auto-research-greenfield.md +0 -42
- package/src/defaults/gjc/skills/team/SKILL.md +0 -467
- package/src/gjc-runtime/team-gc.ts +0 -49
- package/src/gjc-runtime/team-launch.ts +0 -404
- package/src/gjc-runtime/team-notify.ts +0 -326
- package/src/gjc-runtime/team-runtime.ts +0 -5923
- package/src/gjc-runtime/team-store.ts +0 -878
- package/src/gjc-runtime/team-worker-heartbeat.ts +0 -149
- package/src/gjc-runtime/team-worker-memory-guard.ts +0 -363
- package/src/gjc-runtime/team-workers.ts +0 -1123
- package/src/prompts/system/rlm-report-command.md +0 -1
- package/src/prompts/system/rlm-research.md +0 -23
- package/src/rlm/index.ts +0 -365
- package/src/rlm/preset.ts +0 -76
- package/src/rlm/python-tool.ts +0 -68
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,102 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.15.0] - 2026-08-22
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- Added an SDK `automationTools` option for host-owned `browser` and `computer` implementations. External automation retains built-in activation and provenance, receives the normal abort signal, works independently of the default browser/platform gates, and fails closed on custom, extension, or MCP name collisions (#4809).
|
|
11
|
+
- Added opt-in Task autorouting that derives deterministic `fast`, `balanced`, and `strong` model tiers from declared provider priority. Tier generation is catalog-scoped and auth-independent, `/routing` opens the smart-routing setup panel, and bounded preflight evidence records candidate skips and acceptance outcomes.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Upgrade note: restart the SDK broker after upgrading (`gjc sdk` callers pick this up automatically on the next launch; an already-running daemon does not). This release adds the `model.resolve` broker operation, and the broker discovery record carries no package-generation fence — a daemon left running from an earlier version keeps serving and answers the new operation with `unknown_operation`, so coordinator `model` pins (`gjc_coordinator_start_session`) fail until it is replaced. Verified against a live daemon: the failure is loud, no session is created, and one broker restart clears it.
|
|
16
|
+
- Closed the remaining Sentry triage approval races: `gh` is resolved only from trusted system paths, acknowledgement rechecks and records under the creation lock, pending-create reconciliation uses a per-attempt nonce with compare-and-clear, and malformed timestamp suffixes fail ingestion before rendering. Regression coverage pins the retry/apply interleaving so a newer unresolved create cannot be cleared by an older `--retry-pending` request.
|
|
17
|
+
|
|
18
|
+
- The SDK session index now self-repairs on append when it finds a corrupt suffix, instead of throwing `Cannot append to corrupt session index log` until an operator runs `gjc gc --repair-session-index`. The inline path reuses the same quarantine-backed repair (evidence preserved under `sessions/quarantine`, valid prefix republished), so a single poisoned row — e.g. a stale long-lived broker signing a `lifecycle_terminal` event against an outdated `indexSeq` — no longer wedges every subsequent session launch in that agent dir; the manual `gc` flag remains for non-converging damage.
|
|
19
|
+
- Breaking: removed `task.autorouting.preset` and the autorouting preset layer. Preset-only autorouting configuration is inactive until tiers are generated from smart-routing setup; public routing contracts no longer expose preset/source fields, and routing notes contain tier, fallback, and resume components only.
|
|
20
|
+
- Autorouting selectors now use one shared provider-qualified grammar and 256-character bound across runtime validation, generated tiers, routing evidence, and JSON Schema. Settings-derived tiers report provenance only when the provenance is valid; malformed provenance fails closed as hand-authored tiers.
|
|
21
|
+
- Workflow handoffs out of autoresearch now transit properly: `/skill:deep-interview`, `/skill:ralplan`, and `/skill:ultragoal` chain from any live autoresearch phase (`intake`/`research`/`verdict`), and ralplan's `final` phase chains via its manifest terminal states; ralplan/ultragoal live phases still require the explicit write-to-handoff step. `gjc autoresearch clear` remains the finalize-only exit.
|
|
22
|
+
- Breaking: autoresearch spec intake is now the explicit `intake` verb (`gjc autoresearch intake --spec <path>`; the bare `--spec` flag form still works). The ambiguous `handoff` verb token is rejected with disambiguation hints instead of silently becoming a cold-intake goal — workflow handoff lives at `/skill:<callee>` (or `gjc state autoresearch handoff --to <callee>`).
|
|
23
|
+
- Subagents now start correctly on Bun 1.4 when extension tools provide custom renderers. `RegisteredToolAdapter` installs its renderer adapters before proxying the definition, avoiding a write through the proxy's getter-only property while preserving the no-renderer fallback path.
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- Fixed long skill-HUD entries corrupting bottom-pinned TUI row accounting by emitting at most two independent width-bounded rows and preserving severity glyphs through narrow truncation.
|
|
28
|
+
|
|
29
|
+
- SDK `goal.list/get` no longer reports `resource_gone` when an in-flight session's live goal projection is temporarily unavailable. It recovers the latest authoritative goal mode state from the current session branch after runtime recreation or session replacement, while goal-less sessions return an explicit `no_active_goal` diagnostic instead of being confused with snapshot-store loss (#4824).
|
|
30
|
+
|
|
31
|
+
- `/logout` (and `gjc accounts logout`) now remove stored API-key credentials, not just OAuth rows. The interactive logout and the CLI only enumerated `oauth` inventory, so API-key logins (OpenCode Go/Zen, Cursor, Venice, DeepSeek, …) were rejected with `API-key credentials are not managed here`; both paths now remove stored credentials of either kind.
|
|
32
|
+
- Sanitized autorouting selectors and routing-summary attributes before persistence and `noEscape` prompt interpolation, including control characters and Unicode line separators. Generated tier scope now retains multimodal text-capable models while excluding only image-generation-only catalog entries.
|
|
33
|
+
- Autorouting preflight now uses a staged publication when durable session/artifact authority exists and an explicit artifact-only acceptance path when it does not, so a valid synchronous Task without a child session file is not rejected at the acceptance fence.
|
|
34
|
+
- Queued SDK prompts now retain their dispatch-time ownership across selection fences instead of reclassifying from a contradictory later streaming snapshot. Fresh promotion, earlier follow-up ordering, and terminal-abort cancellation are reachable again, while `/btw` test fixtures now model the user-drainable queue count required by the current empty-submit contract.
|
|
35
|
+
- Coordinator stop and idle-reap now initialize canonical namespace state before reading durable deletion recovery. Fresh or upgraded projection-only sessions were misreported as `state_corrupt` before the broker close was attempted, and completed deletion receipts were excluded from the idempotent missing-session lookup; both paths now preserve strict malformed-state rejection while allowing safe cleanup and replay.
|
|
36
|
+
- Fixed provider safety-stop classification being lost before session persistence (#4777). The managed provider-envelope boundary now preserves only the allowlisted `provider_safety_stop` kind, typed safety stops remain terminal even with transport facts on a multi-model fallback chain, and the regression e2e test is selected both by the focused affected-path route and exactly one normal coding-agent shard.
|
|
37
|
+
- Retained-publication acquisition diagnostics now name the stage that actually refused instead of always claiming a failed open, and the native errno vocabulary is closed over the actionable `open(2)` refusal set (#4764). `nativeRetainedObstruction()` rendered every unmatched reason as "could not be opened", so a `broker.json` FIFO — which opens fine `O_RDWR | O_NOFOLLOW | O_NONBLOCK` and then refuses `read_to_end` with `WouldBlock` — was reported as an open failure that had in fact succeeded, `clone`/`metadata` refusals of already-open descriptors were reported as opens, and `unsupported-platform` read as an open error. The structured native reason now drives the prose: `errno-`/`io-` render as open refusals, `read-<kind>` as a read failure, `clone-`/`metadata[-kind]` as inspection failures of an already-open object, `unsupported-platform` as unimplemented authority, and any future reason falls back to a stable "withheld publication authority (reason)" sentence instead of misnaming the stage; the structured reason is preferred whenever the native message parses, so the observed-state fallback is never entered for a stage-named refusal. The Rust `errno_name()` vocabulary adds `EAGAIN`, `EMFILE`, `ENAMETOOLONG`, `ENFILE`, `ENOMEM`, `EOVERFLOW`, and `EPERM` alongside the existing six — descriptor exhaustion and LSM/seccomp denial no longer collapse to `UNKNOWN` — while every unlisted errno still renders as `UNKNOWN` and no errno number or OS string ever leaves the native layer.
|
|
38
|
+
- `move_session` no longer lets a cwd transition commit inside an in-flight tool's execution (#4629). The previous fence re-checked the cwd generation and then yielded, so a move that started inside a tool's first `await` retargeted work already admitted against the launcher root: bash computed `commandCwd` from the live session cwd only after `expandInternalUrls`, running a root-A command in root B. Relative-path tools now hold a shared read lease on the session cwd for their whole execution (`SessionManager.runWithCwdReadLease`), and writers drain outstanding leases before committing, so the cwd observed at admission stays authoritative until the tool finishes. Announced writers also block newly arriving readers, so a stream of tool calls cannot starve a queued move, and async bash jobs stay bound to the cwd they were admitted for.
|
|
39
|
+
- `move_session` re-roots the state the model is shown at the new cwd (#4629). The volatile per-turn message reported the post-move cwd while attaching the launch-bound workspace tree, and subagents launched at the live cwd inherited startup `contextFiles`, `skills`, and `workspaceTree` — pairing the new cwd with the retired root's files and project instructions, which drives wrong path selection rather than merely stale display. Project context files and skills are now re-discovered after a committed move, the workspace-tree service resolves the live cwd per scan instead of capturing the launch root, the cached tree is retired so the next turn re-scans, and the system prompt is rebuilt from the live cwd.
|
|
40
|
+
- `move_session` now completes every fallible step before committing (#4629). Process chdir, capability reset, LSP shutdown, and plugin/MCP/Python authority rebinding previously ran *after* `moveTo` committed and the one-move budget was consumed, with rebind failures swallowed — so the tool could reject after the session had already moved, or succeed holding launch-root tool authority. Those steps now run ahead of the commit and restore launch-root authority on failure, so a rejected rescope leaves the session exactly where it was and does not consume the one-move budget.
|
|
41
|
+
- `move_session` now verifies process-cwd identity against the pinned directory handle (#4629). The no-follow handle was only ever `stat()`ed while the commit and `setProjectDir` resolved the target *name*, so a replacement after the last comparison escaped the validated descendant — the confinement the tool exists to enforce. Node exposes no `fchdir`, so the post-`chdir` cwd's inode identity is now compared against the pinned handle and the chdir is rolled back on mismatch.
|
|
42
|
+
- Process-cwd authority is now an explicit claim rather than an inference (#4629). `move_session` decided it owned the process cwd when `process.cwd() === session.cwd`, which two sessions launched at the same root both satisfy, so rescoping one called process-global `setProjectDir`, `resetCapabilities`, `shutdownAllLspClients`, and plugin-cache clearing underneath its sibling — reaching real execution through consumers such as browser tab work. `SessionManager.claimProcessCwdOwnership` grants the claim to one live manager; a session without it moves its own cwd and leaves process-global state untouched.
|
|
43
|
+
- Conventional MCP autoload now reads the user scope from the agent directory instead of a home-relative `<home>/.gjc/agent` path (#4767). Every writer and denylist reader already resolved user scope through `getMCPConfigPath("user")` (the agent directory), while native discovery derived it from the load context's home, so the two disagreed the moment an agent-directory profile was in play: `GJC_CODING_AGENT_DIR=<profile> gjc mcp add <name>` wrote `<profile>/mcp.json` and reported the server as loaded by ordinary sessions at startup, but startup read `~/.gjc/agent/mcp.json` — the profile's own registrations never loaded and the default profile's servers loaded into the profile instead. `loadAllMCPConfigs` accepts an `agentDir`, sessions created with their own `agentDir` pass it, and the `disabledServers` denylist follows the same scope, so discovery, `gjc mcp add`, the `/mcp` wizard, and `gjc customize doctor` all name one file. This also restores isolation for the MCP autoload suites, which established their temp user scope with `setAgentDir()`: after the trusted-home provenance rework their `os.homedir()` mock no longer reached discovery, so on a developer machine the red-team suite read the real `~/.gjc/agent/mcp.json` and in CI it found nothing.
|
|
44
|
+
- Immediate `turn.prompt` after `turn.abort` acknowledgement now starts exactly one successor turn instead of being silently consumed by aborted-turn teardown. Abort unwind no longer classifies the successor as steering into the dying loop, delayed `agent_end` is paired with the aborted invocation rather than the successor, and abort acknowledgement waits for that serialized terminal transition — no client-side sleep (#4749). Retention is bounded in the other direction too: abort stays terminal across overlapping aborts. Every abort request advances an abort-admission epoch synchronously, including one that shares an already in-flight unwind, so a prompt admitted between two aborts is refused as a cancelled preflight instead of refreshing its generation and starting work the user already aborted twice. An abort sharing another abort's unwind also applies its own request-scoped effects (preflight cancellation, and abort visibility so a later real abort is not silenced by an earlier `silent: true` abort). A prompt admitted after an abort settles still starts a normal successor turn.
|
|
45
|
+
- Esc/Ctrl+C now recover a WSL/basic-terminal session whose busy indicator outlived its turn (#4741). Both the global interrupt/clear listener and the editor escape handler treated any mounted working loader as unconditionally cancellable work: after a completed turn left the loader mounted, every press ran a no-op abort, consumed the key, and reset the escape gestures, so the composer stayed `working [esc]`/`Fetching … [esc]` and Esc, Ctrl+C, and Ctrl+X never did anything (Ctrl+Z suspend/resume was the only escape). The loading branch now stops the stale indicator via the shared activity-indicator stop and lets the key fall through to idle semantics when there is no pending submission (including a started one still inside prompt preflight, exposed through the new `hasPendingSubmission()` context query), no queued steering/follow-up/compaction messages, and the session is neither streaming nor compacting; active work (streaming, queued messages, pending optimistic or started-preflight submission) still aborts exactly as before. Recovery gates on drainable queues only: the new `AgentSession.drainableQueuedMessageCount` counts exactly the steering and follow-up entries that `clearQueue()`/`popLastQueuedMessage()`/`getQueuedMessageEntries()` return, whereas the aggregate `queuedMessageCount` also counts hidden next-turn context that no key press can drain and that deliberately survives turn completion (a `todo_write` failure reminder queued with `deliverAs: "nextTurn"` and no `triggerTurn`) — gating on the aggregate left a permanently nonzero count that reproduced the same lockout while idle. Hidden next-turn ordering and delivery are unchanged; recovery neither clears nor delivers those entries. The same correction applies to the two adjacent gates whose handlers are also visible-queue-only: `app.message.sendNow` no longer advertises itself when only hidden context is queued (its only outcome was "No visible queued message to send"), and an empty submit while streaming no longer aborts the live turn to flush a queue that holds nothing drainable.
|
|
46
|
+
- Bare-default Codex and Anthropic provider-overload retries now honor the configured retry ceiling instead of entering the unbounded transient path, and every replay still requires a clean retry scope after extension lifecycle handlers participate.
|
|
47
|
+
- `gjc accounts` command errors no longer escape as uncaught exceptions in text mode. `accounts pin` resolves its target through `resolveOAuthPinTarget`, which throws a typed `OAuthCredentialSelectorError` for user-correctable selector problems (API-key rows, active overrides, disabled or missing accounts, ambiguity), and `runAccountsCommand` rendered `AccountsCommandError` only in `--json` mode — text mode rethrew everything, so even the command's own "Provider … is not configured; no pin was written" surfaced as a stack trace plus a `gjc-crash.log` entry. Selector failures now map to `AccountsCommandError` with the message preserved (so `--json` reports `accounts-error` instead of `internal-error`), and text mode prints one clean stderr line with exit code 1. The framework's `CliParseError` handling and the JSON machine contract (exactly one document, never stacks or secrets) are unchanged.
|
|
48
|
+
- Fixed interactive steering stalling during post-prompt unwind when the terminal transcript message is a bash or Python execution; the auto-continue gate now treats all three terminal roles consistently and regression coverage pins delivery for assistant, bash, and Python tails (#4739).
|
|
49
|
+
- Restored live subagent progress in the `subagent await` panel through a bounded public DTO. #4748 dropped the `progress` field believing it exposed model-generated deltas and tool output to the parent model; the renderer needs live status, tool names, output counts, fast mode, and retry state, but raw progress must not enter tool-result, ACP, or telemetry envelopes. The await signature now tracks only those approved fields and ignores countdown-only churn, so the panel updates without exposing model deltas, tool arguments, arbitrary output, or nested task payloads.
|
|
50
|
+
- Dev CI no longer re-runs the entire code-validation matrix when a pull request body is edited. The verdict line lives in the PR body, so every review verdict update fired a `pull_request: edited` event; with `cancel-in-progress: true` that cancelled the run already validating the identical tree and re-queued the whole matrix against it. Cancelled runs surfaced as confusing downstream failures rather than clean cancellations -- a job would report `Artifact not found for name: dev-affected-plan-<run>` while that artifact existed and its producer had succeeded. `edited` stays in the trigger list because `pr-contract-bootstrap` must re-check the body, but it is now the only job that runs for it; every code-validation root carries an explicit guard and the remaining jobs inherit it through their dependency on a skipped plan.
|
|
51
|
+
- Dev CI no longer re-runs the entire code-validation matrix when a pull request body is edited. The verdict line lives in the PR body, so every review verdict update fired a `pull_request: edited` event; with `cancel-in-progress: true` that cancelled the run already validating the identical tree and re-queued the whole matrix against it. Cancelled runs surfaced as confusing downstream failures rather than clean cancellations -- a job would report `Artifact not found for name: dev-affected-plan-<run>` while that artifact existed and its producer had succeeded. `edited` stays in the trigger list because `pr-contract-bootstrap` must re-check the body, but it is now the only job that runs for it; every code-validation root carries an explicit guard and the remaining jobs inherit it through their dependency on a skipped plan. Body-only edited runs are also partitioned into their own concurrency group so they cannot cancel the validation run already covering the identical tree, and the exemption is keyed on a body-only payload -- `edited` also fires for title and base changes, and a base retarget can be the first validation event for a new head/base pair.
|
|
52
|
+
- Retained-publication refusal diagnostics now carry the native acquisition object and reason into the surfaced error before any later pathname inspection. POSIX acquisition rejects non-regular owner records before granting authority, while fallback probes are labeled as current observed state and escape the complete Unicode control/format surface.
|
|
53
|
+
- Coordinator `gjc_coordinator_start_session`, `gjc_delegate_plan`, and `gjc_delegate_execute` now accept an optional `model` argument that pins an explicit `provider/model` for the started session with the same grammar as `gjc --model` (#4707). An external orchestrator could previously select only a model *profile* through `mpreset`; writing a model name into the prompt is not model selection, so a reserved start could never prove it was on the requested model (every Cursor id — `cursor/claude-fable-5-xhigh`, `cursor/composer-2.5`, `cursor/default`, … — worked from the CLI but not through Coordinator start). The pin is resolved through the same staged selector resolver the CLI uses (against the full registry, not the authenticated-only subset, so a valid id is never rejected up front for a missing local credential) and fails closed with the CLI's `Model "..." not found. Use --list-models` error before any broker mutation or idempotency record — no session is ever created on a different model. The resolved pin threads through the SDK lifecycle launch request as `modelId` and the session host applies it exactly like a CLI `--model` selection, so when both `mpreset` and `model` are given the explicit model wins, matching `gjc --mpreset <p> --model <m>`. The delegate tools' previous prompt-metadata `model` hint is upgraded to this real pin, and reusing a delegate session with a conflicting pin is rejected like the existing `mpreset_conflict`. The effective precedence is `model` > `mpreset` > configured `modelProfile.default` > role/resume/default. The pin is a guarantee rather than a preference: the coordinator validates against its own registry while the child owns the registry that actually serves requests, so on drift (a model removed, a provider disabled, an extension that failed to register) the child now fails session construction before readiness and before any profile application, disposes the partial session, and reports an error naming the pinned selector — previously the discarded fallback warning let startup profile application activate `modelProfile.default`/`mpreset` while the coordinator still reported the requested pin. Coordinator validation also refreshes its registry per request (offline, from the on-disk discovery cache) instead of caching one snapshot for the process lifetime, so ids added or removed after an earlier pin are judged against current contents.
|
|
54
|
+
- Custom-provider `apiKeyEnv` credentials sourced from the trusted agent `.env` now follow rotations on the next request instead of remaining pinned to the process-start value. Long-running sessions previously kept sending a revoked OAuth access token after an external account synchronizer atomically replaced the agent `.env`; the registry now re-reads only that trusted regular file and updates or removes the config credential override, failing closed for a symlink or read error after agent ownership is established. Presence in the agent file makes it authoritative over an older value inherited from the launching shell, while names absent from the agent file keep their existing environment precedence. Explicit `apiKey` values remain authoritative when both forms are configured, and caller-project `.env` files remain excluded from credential resolution.
|
|
55
|
+
- Coordinator event journal rows can now be pushed to one opt-in webhook (#4706). External orchestrators that cannot stay attached to `gjc_coordinator_watch_events` long-poll (a 300s `await_turn` timeout is not session death) had no push of **existing** journal rows; they can now set `GJC_COORDINATOR_MCP_EVENT_WEBHOOK_URL` to receive each row as an authenticated POST whose body is the exact native `watch_events` record — same `seq`, same stable `id`, at-least-once so sinks dedupe on `id`. The feature is env-only and default-off (no MCP tool can set or read it), destinations are allowlisted (`https:` anywhere, `http:` loopback only, no redirects), the bearer token comes from a secret file path rather than env, an optional session-id scope restricts delivery to authorized sessions, and delivery runs through a durable per-row outbox off the journal append path with bounded attempts, exponential backoff, and a bounded request timeout — a dead sink never delays or rewrites terminal turn/session persistence. The five `GJC_COORDINATOR_MCP_EVENT_WEBHOOK_*` variables resolve through the trusted credential environment (`$credentialEnv`, the same provenance as the crash-relay DSN) rather than raw `process.env`, so a checkout's `.env` cannot select the egress destination or the token file. `watch_events` long-poll is unchanged and remains the source of truth; `gjc coordinator doctor` reports the resolved webhook state.
|
|
56
|
+
- Extension activation is now transactional. `pi.registerFlag(..., { default })` and `pi.registerProvider(...)` used to mutate the shared `ExtensionRuntime` state directly with no rollback, so a factory that threw midway was discarded while its side effects leaked: the flag default stayed readable via `getFlag`/`getFlagValues` and the provider registration stayed queued for the ModelRegistry drain in `sdk/session.ts` and `runListModelsCommand`, activating providers from an extension that never activated. Each factory invocation now stages its shared-state writes in an `ExtensionActivationScope` (stage → factory completes without throwing → commit into the shared runtime); rollback discards the staged writes so a failed extension leaves no flag default and no provider registration behind, and earlier extensions' committed state is untouched. After commit the shared runtime is authoritative for `getFlag`, so runtime-side writes (CLI flag overrides, a later extension's committed default) stay observable to retained extension API objects exactly as before the transaction (#4718). Commit itself is transactional: prior flag entries and the provider-queue length are journaled before publication, so a throw partway through commit is undone before it escapes and the scope only becomes terminal once publication fully succeeds — a failed extension leaves nothing behind even when the failure happens during publication.
|
|
57
|
+
- A content-free Anthropic capacity overload no longer ends the turn under the default retry configuration. Anthropic can answer with its typed `overloaded_error` as a statusless stream error, and session retry already classifies that as transient, but the bare-default admission list only covered watchdog timeouts and the Codex `server_is_overloaded` event — so the turn surfaced the raw provider envelope and went idle, leaving the operator to resend or switch models by hand for a failure the provider says to retry. The admission now also accepts Anthropic's own overload code, recognized by parsing the error envelope and requiring both the outer `type` and the nested `error.type` to match exactly. Nothing else changes: the attempt must still carry no assistant text, thinking, or tool call and no conflicting transport facts (a status-bearing or otherwise typed failure keeps failing closed), overload prose alone can never authorize a replay, and the existing capped exponential backoff and `retry.enabled: false` opt-out are untouched.
|
|
58
|
+
- File tools no longer lose a just-written path or leave a 0-byte target when a write fails (#4734). `writethroughNoop` and LSP writethrough now publish through a sibling temp + rename (`writeFileAtomically`) so a permission/IO error cannot truncate the destination; `EACCES`/`EPERM`/`EROFS` surface as an actionable `ToolError` that says the original file is unchanged. Read tries the ACP `readTextFile` bridge when disk stat misses, and bridge failures fail closed: only an explicit `transport_unavailable`/`bridge_unavailable` code falls back to the agent host's disk, while structured denials and raw OS errno values such as `EPERM` do not, so a local read cannot bypass a remote client's access decision. Successful writes invalidate `fileReadCache`. Compaction-state now lists recent successful `write`/`edit`/`apply_patch`/`ast_edit` paths so a long-session compact does not silently drop in-flight file-tool context. This is independent of Windows directory-fsync `EPERM` (#4457) and of workflow-validation compaction (#4560).
|
|
59
|
+
- Closed exact-head review findings on the #4734 atomic write path: the session-local trust boundary is now validated **before** any parent directory is created (a dangling symlink inside a trusted root resolves outside it, so creating parents first materialized an attacker-selected tree outside the sandbox before publication was refused, and the boundary is re-checked after `mkdir -p` follows existing symlinked ancestors); the publication parent is pinned by device/inode rather than realpath string, so a parent unlinked and replaced by a different directory at the same path is detected instead of published into; and the Windows in-place sharing fallback revalidates destination inode identity before mutating by pathname, refusing with `destUnchanged: true`/`not_published` when a concurrent writer substituted a successor during rename backoff. Read's ACP bridge fail-closed policy is now documented accurately: only explicit `transport_unavailable`/`bridge_unavailable` codes fall back to disk, never structured denials or raw OS errno.
|
|
60
|
+
- Hardened the #4734 atomic write path after review: LSP writethrough awaited its `BunFile` write again (an unawaited call escaped the surrounding `try`, so a rejecting write was recorded as published and surfaced as an unhandled rejection), the Windows in-place sharing fallback now writes replacement and rollback bytes at absolute position 0 (`handle.writeFile()` resumes from the handle offset, so a partially accepted replacement left interleaved bytes while the result still reported `destUnchanged: true`), and the module contract no longer claims crash atomicity it does not provide. Publication is documented as last-writer-wins in `docs/tools/write.md`: identity is revalidated before the rename, but `rename(2)` commits against the pathname.
|
|
61
|
+
|
|
62
|
+
- Fixed idle ACP sessions burning sustained CPU with no active turn (#4689). Every attached session's SessionRouter ran a 2s reconcile that unconditionally re-acquired the machine-global index lock and re-read, re-parsed, and re-checksummed the entire session index, then re-projected every historical row — O(total index history) per 2s tick per live session, forever, scaling a fresh session from ~2% (small index) to a sustained 30-70% of a core on an aged machine and degrading concurrent session creation through lock contention. The index now carries a change stamp (size/mtime/ctime over log + snapshot) so a poller proves "nothing changed" with two stats and reloads append-only changes through the tail reader, and the router's idle tick is just that stamp check plus a cheap transport-revive no-op: the full attach/retire body runs only on index changes, pending adoptions, or a 30s liveness sweep (well inside the index's own 2x60s heartbeat-freshness window). Measured idle CPU on a 15k-row index drops from a sustained ~30% to ~1% mean, with prompts unaffected (the pre-send reconcile is unchanged work when state actually changed). Because a running chat daemon builds its own SessionRouter, a pre-upgrade owner would keep the old hot polling loop: the Telegram daemon generation advances to 172 and its serving epoch to 88 (generation alone does not force replacement), and Discord/Slack generations 67/70 fence the same rollout.
|
|
63
|
+
- Runtime skill discovery now scans `skills.customDirectories`. Session startup already loaded those directories through `loadSkills`, but `discoverRuntimeSkills` and `findRuntimeSkillByName` searched only the canonical project and user roots, so a configured custom skill was invocable by exact name yet absent from every `skill_discovery` search -- usable only by someone who already knew it existed. Both discovery entry points now scan the configured directories at user level (so project-scoped queries exclude them), deduplicated and tilde-expanded the same way `loadSkills` does. Naming a directory is explicit consent, so custom directories are not gated on `skills.trustUserSkills` -- matching the startup rule -- while the `skills.enabled` master switch still suppresses them.
|
|
64
|
+
- A broker that cannot retain its own publication now names the object that withheld authority. The native layer opens `sdk`, `sdk/broker.lock`, `sdk/broker.lock/owner.json`, and `sdk/broker.json` no-follow and reports every refusal as one opaque `Retained broker publication authority is unavailable.`, so `gjc sdk` died with nothing to act on and the precondition could only be learned from the native source — a shared multi-account layout that symlinks the agent directory's `sdk` entry crashed every broker start this way. The failure is still fatal and still rolls back its publication; it now appends the first obstruction (missing entry, symlinked entry, wrong file kind, unreadable entry, or a non-fixed-width `heartbeatAt`) ahead of a bounded agent directory, so the named object survives the 512-character startup-failure reason, and stays verbatim when every precondition holds so a named condition is never invented. Each object is probed with the native's own access mode — the lock record read-only, only the published record read/write — and a file kind is only ever named through the open the native itself refuses, so a layout the native accepts is never reported as an obstruction; the published record is read through the descriptor the no-follow open already verified, never reopened by name. When rollback fails too, the aggregate message now carries the acquisition diagnostic, since the durable startup-failure marker persists only that message.
|
|
65
|
+
- Retained broker publication probing now opens POSIX objects non-blocking, diagnoses exact-buffer malformed records, escapes control and bidi characters in persisted agent-directory diagnostics, and covers native-rejected wrong-kind objects without inventing a condition the native layer accepts.
|
|
66
|
+
- `subagent await` now ends its wait when the busy session queues a user steer, so the message is handled at the tool boundary instead of after the full wait window; the awaited children keep running.
|
|
67
|
+
- Crash relay provenance now rejects repository-declared credential and agent-directory overrides even when dotenv expansion changes their runtime values, preserves trusted XDG state while refusing checkout-controlled XDG state, and accepts maximum-sized legacy index entries during relay-watermark upgrade (#4715).
|
|
68
|
+
- A subagent whose provider stream fails with a self-declared `errorKind: "local_buffer_overflow"` is no longer reported to the parent as a staging-buffer overflow (#4618). The agent runtime now derives the terminal message's `errorKind` and `bufferOverflow` from one identity check, so a forged label reaches neither `TaskResultReceipt.preview` nor `errorSummary` and the receipt falls back to the ordinary provider-error text instead of pointing the reader at the wrong subsystem.
|
|
69
|
+
- Hardened the parent-facing local-failure boundary for subagent receipts (#4618). The executor now runtime-validates the structured `bufferOverflow` diagnostic before interpolating anything — closed `stage`/`exceeded` literals only, finite non-negative safe-integer counters, and internal consistency with the cap claimed to have tripped — so a foreign provider payload that smuggles the field through a message snapshot degrades to a fixed neutral sentence instead of embedding arbitrary text in `TaskResultReceipt.preview`/`errorSummary`. `local_snapshot_failure` and plain `local` failures likewise render fixed sentences rather than regex-redacted free-form child message text (redaction never covered prompt text, tool arguments, or file content). The render path is pinned end to end by a genuinely tripped staged-buffer overflow carried through the executor boundary, the async job/snapshot chain, and both streaming and cached renderers with per-line width/tab bounds.
|
|
70
|
+
- Fixed the reusable composer being terminally disposed at the remaining overlay-open paths: the generic selector boundary (`SelectorController.showSelector`, reached by bare `/login` and the `/debug` entry), the queued-message selector (`app.message.dequeue` / `alt+up` / `alt+down`), the jobs overlay (`alt+j` / `/monitors`), the tasks pane (`alt+t`), the OAuth API-key paste input, the `/mcp add` wizard, and the `/share` loaders, plus the `/debug` log + raw-SSE viewers. Each open called `editorContainer.clear()` with the live composer attached, so the first round-trip silently killed the editor's tab-width listener and runtime tab-width changes stopped re-deriving composer layout. Every open now detaches the reusable editor before clearing — the `Container.detachChild` detach-then-readd reuse contract (#4657). The `/debug` log viewer now binds its async completion to the exact composer child and `editorContainer` mount revision captured before log I/O, so a stale completion cannot misidentify a newer RawSSE/Input owner or reclaim the same composer after an intervening overlay cycle; the RawSSE/Input race regressions prove that it discards stale viewers instead of clearing and disposing whatever UI now owns the container.
|
|
71
|
+
- Added maintainer-side issue filing to the Sentry crash triage script (`scripts/sentry-crash-issues.ts`), restored behind an operator approval model (#4716). The script's earlier review history established that the `gjc.fingerprint` tag is stamped client-side with the public DSN ingestion key and is forgeable, so no fingerprint alone can ever authorize a write: `--apply` now files only rows whose exact reviewed content is recorded in a local single-use approval store (`~/.gjc/sentry-triage-approvals.json`, 0600), written by `--approve <batch-digest>` after a human reviews the dry-run listing. Each approval manifest binds repository, org, project, immutable Sentry group id, fingerprint, and a canonical rendered-content digest, is recomputed at `--apply` time and rejected on drift, and is consumed on use — so a later row from a different upstream context or with different rendered content can never inherit an earlier approval. Issue-body marker hits remain untrusted candidates that suppress nothing until `--acknowledge` binds the operator's decision to the exact issue URL, with successful local creations tracked as a separate `filed` idempotence record; the final duplicate-check-plus-create runs inside a lock-directory critical section so two concurrent approved invocations cannot double-file, and same-fingerprint group collisions stay withheld for manual reconciliation. Upstream rows failing ingestion validation are now reported as a distinct incomplete-run condition (non-zero exit) instead of being miscounted as groups without fingerprints.
|
|
72
|
+
- Hardened the Sentry triage approval boundary after review: lock takeover/release is owner-token checked, filed manifests remain idempotent across local-store crashes, public issue matches require explicit URL acknowledgement, GitHub child processes receive no Sentry credentials and have their host pinned to github.com (an inherited `GH_HOST` override can no longer redirect issue lookups, creates, or the credential to an arbitrary host), saturated pages and malformed tags fail closed, approval succeeds with a zero exit when durable, and the file-backed store has atomic/fsync and integration coverage. An `--apply` attempt that reaches `gh issue create` and never confirms a canonical issue URL — timeout, thrown error, non-zero exit, or malformed stdout — is now treated as an unresolved remote commit rather than a safe retry: the in-flight record it writes is read back inside the same creation lock, and the row is withheld from every later `--apply` because GitHub's marker search is eventually consistent and therefore cannot prove the issue was not created. Reconciliation is explicit — `--acknowledge <url>` for the issue that did land, or `--retry-pending <fingerprint>` after confirming none did, which clears the record only for the exact currently rendered manifest so a drifted row cannot inherit it.
|
|
73
|
+
- Fixed notify recovery feeding native exact-unlink exchange debris back through the exchange, which made a fresh notification daemon spin at ~100% CPU and accumulate unbounded `.gjc-delete-notification-endpoint-*` and `.gjc-exact-unlink-placeholder-*` artifacts (#4701). The native exchange always detaches a target into a fresh quarantine destination and leaves durable retained records, so recovery's endpoint scan re-classified those `.json` quarantine targets as live endpoints and re-quarantined them, while the debris sweep removed each aged stub by re-running the same exchange — manufacturing fresh quarantine/placeholder debris every pass and re-attacking a live endpoint publication. Recovery now treats native exchange debris paths as non-endpoint (never routed through the exchange) and `sweepNotificationDebris` removes positively-stale inert debris by a direct identity-checked unlink (re-verified at mutation time so a live successor is retained, never unlinked). Live endpoints stay published and matchable, health's endpoint census is no longer inflated by debris, and repeated recovery is a fixed point.
|
|
74
|
+
- Fixed ralplan run fragmentation by writer cwd: `gjc ralplan` and `gjc ralplan --write` now accept an additive `--worktree-root <path>` that binds run state, stage artifacts, the `index.jsonl` ledger, HUD state, stuck markers, and review-budget accounting to one canonical git worktree regardless of the writer's ambient cwd (#4693). Without the flag, cwd-based behavior is unchanged. An explicit target is canonicalized and must be an existing git worktree root (missing, non-directory, non-git, and subdirectory targets fail before any filesystem mutation); explicit-target mode requires exact canonical `worktreeRoot` equality with the seeded run's `repository_binding` — a linked worktree that merely shares `commonDir` is rejected — and refuses to write where no seeded run state exists, so a role lane can never stamp fragmented authority in a second `.gjc` tree. A relative `--artifact` file is still resolved from the invoking cwd, and the bundled ralplan skill now instructs Planner/Architect/Critic/revision/disposition/post-interview/ADR/final writes to propagate the seed receipt's `repository_binding.worktreeRoot`. This removes the false terminal `PLANNING-STUCK` a dispatcher-cwd re-review hit when its local ledger lacked the target worktree's `revision` opener.
|
|
75
|
+
- Windows ACP `session.create` no longer leaks `terminal_uncertain` when a lifecycle child publishes ready/endpoint evidence and then exits before the broker's final live admission (#4709). The ready-then-exit tolerance and rollback-receipt relaxation are win32-only through a testable host-platform seam; other platforms keep the prior fail-closed `terminal_uncertain` path. The typed broker/ACP code is `ready_then_exited` so MCP launch remapping cannot misattribute it as an MCP unavailable failure. A session host that rejects its liveness watcher after readiness writes no startup receipt, so the exact ready-plus-death classification is never preempted; a host that tore down gracefully is proven ready through the broker's own host registration when its endpoint file is gone. The ready-authority probe is typed (`matched`/`absent_indexed`/`absent_unindexed`/`not_published`/`malformed`/`io_error`): the broker-index fallback fires only on a proven-absent (ENOENT) endpoint, and a corrupt or unreadable endpoint surfaces as the new honest terminal `endpoint_unreadable` instead of a false `spawn_failed` claim about the child. Owned-artifact deletion re-checks the lifecycle marker immediately before unlinking the endpoint so a PID-reusing successor's endpoint can never be removed. Detached-host stderr is discarded by the OS (`stdio: "ignore"`): a parent-owned stderr pipe is not detached by `unref()` and would retain the broker process for the host's lifetime, and the host handles launch configuration and credential-bearing environment material, so its output is never captured, persisted, or included in caller-visible error strings. A dead child is never admitted as live.
|
|
76
|
+
- Added an agent-invokable session rescope: a `move_session` tool the model can call when a session launched from a broad multi-repo root has converged on one repository (#4629). It runs the same sequence as the text/ACP `/move` handler (`flush` → `moveTo` → `setProjectDir` → plugin/capability cache reset → SSH tool refresh), so tool path resolution, the bash default cwd, and relative-path resolution all follow the move. The tool is exposed through an optional `ToolSession.rescopeSessionCwd` accessor wired only for top-level sessions (`taskDepth 0`) without a restricted bash surface — subagents and read-only profiles never see it — and rejects non-directory targets without moving. Unlike the user-driven `/move`, the model-invoked accessor only narrows: the canonical (realpath) target must be a strict descendant of the canonical current cwd; the committed move revalidates a no-follow directory identity so a swapped symlink cannot escape; SessionManager serializes model and SDK/ACP moves and dispose joins the in-flight transition; restored-or-live workflow skills refuse the move; and cwd-capturing MCP/custom/Python/project authority is revoked and recreated at the target. Like the user-driven `/move`, the volatile workspace-tree snapshot itself still follows the launch cwd until #4628 lands.
|
|
77
|
+
- Handled tool failures are now recorded and relayed alongside fatal crashes. A tool that throws and is caught never reaches the `uncaughtException` path, so those errors were invisible to both `gjc crash list` and the upstream relay. They are captured at `finishExecuteToolSpan`, which already holds the live `Error` with an intact stack, and only when `status === "error"` with a non-empty stack -- aborted calls, blocked calls, and non-`Error` throws are excluded because without a stack the v1 fingerprint degrades to `<no-app-frame>` and unrelated failures collapse into one group. Hooking `logger.error` was rejected for the same reason: nearly every call site passes `String(error)` or `error.message`, so the stack is already gone. Handled errors use their own files rather than the fatal store, since they are high-volume and would otherwise evict the rare fatal records under a shared cap; record format, redaction, fingerprint, and the `sanitizeExternalCrashV1` egress contract are reused verbatim. Upstream they differ only by `level` (`error` rather than `fatal`), and fatal signatures relay first so a noisy handled class cannot starve them at the per-run cap.
|
|
78
|
+
- Closed remaining crash-relay egress and durability holes from #4658/#4698: automatic fatal and handled stores resolve through a non-XDG trusted agent path so a checkout `.env` that sets `XDG_STATE_HOME` and creates `$XDG_STATE_HOME/gjc` cannot feed forged state; the relay comparison watermark follows journal append order independently of display-time `lastSeen`; legacy `relayedAt` indexes keep coverage across upgrade and downgrade/re-upgrade; a 2xx POST whose local watermark append fails is a failed send and retries with the same event id.
|
|
79
|
+
- `gjc config set` and `gjc config reset` now fail instead of reporting success when the setting cannot be durably written to disk.
|
|
80
|
+
- Fixed a steer sent while a tool was running leaving the session idle with nothing but `Operation aborted`. Interrupting an in-flight tool aborts the run's signal without ending the agent loop, so the loop unwound the tool and then drained the queued steering inside that same aborted run: the steer was delivered as a user message, the turn opened on the already-aborted signal, and the provider rejected it before the first token. The operator saw the tool marked `Tool execution was aborted.`, their steer echoed back, and then a bare `Operation aborted` with the session going idle — the instruction was consumed and answered by nothing. An aborted run now hands queued steering back and ends, so the existing resume path starts a fresh run and delivers it, which is what already happened when no tool was in flight. The phantom empty assistant turn is gone with it. The same guard covers the tool-execution steering drain (the immediate-interrupt path), which could otherwise reopen a born-aborted turn when the interrupt landed while the steer-interrupted tools unwound, and the tool-execution steering check no longer consumes steering once the run's signal is aborted — a tool task still unwinding could otherwise dequeue steering the drain had just handed back, orphaning it in an execution result nobody reads.
|
|
81
|
+
- Fixed broker-managed SDK prompts that run longer than the process-local submission TTL (`PROMPT_SUBMISSION_TTL_MS`, 5 min) reaching durable `terminal_ok` without their `agent_end`/`agent_failed` ever entering the positioned event ring (#4691). `terminalizePrompt()` committed the durable outcome and marked the submission terminal, then `emitPromptLifecycle()` ran `cleanupPromptRecords()` first, which age-evicted the just-terminalized record, so publication returned before the correlated terminal frame was emitted and attached subscribers (and replays) never saw it. Age-based and capacity eviction now apply only after terminal publication has settled (`delivered`) or for fatal transport-level closures, which never publish by design; records stranded without a runtime or after a publication throw are expired immediately with their dedupe tombstone so bounded retention is preserved. Regression coverage pins past-TTL terminal delivery, the mid-terminalization cleanup race, exactly-once re-drive, and registry health after a past-TTL terminal.
|
|
82
|
+
- Session-scoped reasoning (`Shift+Tab`, `/effort`) now survives the next user prompt when the default fallback chain carries an explicit suffix such as `xai/grok-4.6:high`. Re-resolving that chain used to re-apply the suffix and snap the live effort back to `:high` before the request left, so the UI could show `xhigh` while the turn ran at `high`.
|
|
83
|
+
- Session thinking scope now distinguishes operator-chosen effort from model defaults (#4695). `thinking_level_change` entries carry operator-intent provenance set only by the control surfaces (`/effort`, Shift+Tab); appends driven by a model's own `defaultLevel` on model switch, temporary model switches, context clears, or re-applies after model cycling no longer mint `session` scope, so a suffixed default-chain entry such as `xai/grok-4.6:high` is no longer suppressed when the operator never chose an effort.
|
|
84
|
+
- Session restore now applies the same chain-resolution thinking-level rule as the two per-turn re-resolution sites. The restore site still wrote the resolved selector's suffix level through `setThinkingLevel`, which appended a stray `thinking_level_change` entry; that entry flipped the restore recompute's `hasThinkingEntry` probe and restored the wrong effort (a session with no thinking history of its own came back at the switching session's level instead of `defaultThinkingLevel`). The block is removed in favor of the unconditional recompute, so all three sites share one rule (#4697).
|
|
85
|
+
- A Round-0 deep-interview `ask` whose `deepInterview` object is present with topology identity (`round: 0`, `component: "review-topology"`, `dimension: "topology"`) but omits required topology fields (`ambiguity`, `intent_contract`) is now rejected before coercion with a targeted correction naming the omitted fields and the exact `intent_contract` shape, instead of generic zod issues plus a full payload echo. The incomplete object is not a retired-pair recovery candidate, so it fell through to schema validation whose message named neither the contract nor what a corrected retry must contain — and a metadata-only retry repeated the same invalid shape (#4649). Recovery stays fail-closed: `intent_contract` is never synthesized (items and affirmative labels are the locked-intent evidence), and the recorder still locks intent only on an affirmative user answer. Valid contract-only Round 0 and post-Round-0 payloads validate unchanged.
|
|
86
|
+
- Added an opt-in crash upstream so local crash signatures can be aggregated across installs, which the agent-dir-scoped index could never answer on its own. `crashReport.upstream` (default `off`) plus `crashReport.upstreamDsn` (or `GJC_CRASH_SENTRY_DSN`) gate a hand-rolled Sentry envelope POST; no DSN literal is compiled into the binary, so a build has no destination to fall back to and `off` costs one settings read and no IO. The relay never runs on the fatal path — a crashing process still performs exactly one `O_APPEND` journal write, and relaying happens at the next startup after index compaction, bounded to 8 signatures per run with a 10s timeout. A Sentry SDK is deliberately not used: SDK defaults attach breadcrumbs, environment, and argv, which would defeat the point of the outbound sanitizer. Every crash-derived field must pass `sanitizeExternalCrashV1` and a refusal drops that signature outright rather than falling back to a less-sanitized payload; the emitted payload is a fixed key set (`event_id`, `timestamp`, `platform`, `level`, `logger`, `release`, `environment`, `fingerprint`, `exception`, `tags`, `extra`, `sdk`) with `user`, `server_name`, `contexts`, `breadcrumbs`, `request`, `modules`, env vars, argv, and hostname structurally absent. The gjc fingerprint is sent as Sentry's `fingerprint` array so grouping is ours rather than Sentry's heuristics — one upstream issue per gjc signature, verified by two events with disjoint stack frames merging into a single group. `gjc crash relay` exposes the same batch as a loud, non-zero-on-refusal command. A new `relayed` journal event stamps `relayedAt` monotonically so re-runs do not resend unless `lastSeen` advanced, and `relayedAt` is deliberately not an input to index eviction. The `gjc crash report` GitHub flow keeps its per-invocation, digest-confirmed consent boundary unchanged; the two egress channels are separate with separate rules.
|
|
87
|
+
- The builtin `python` tool is now a mission-free persistent per-session REPL. It no longer requires an active autoresearch mission, owns its kernel as `python:<session-id>` (distinct from the eval kernel), always runs in the session cwd, and appends every execute call to an append-only transcript at `.gjc/_session-{sessionid}/ipykernels/{datetime}-{kernelid}/transcript.jsonl` (rotated per real kernel lifetime via a new acquisition-time `onKernelStart` executor callback). The `clear` action and per-agent isolation are preserved; the managed research-venv seeding is dropped.
|
|
88
|
+
- `gjc autoresearch` is now ledger-only: the `report` verb is removed (explicit unknown-verb rejection, exit 2), the mission-notebook concept is deleted (`src/autoresearch/{python-tool,session,report}.ts`), CLI `clear` no longer disposes any kernel, and the `kernel_cleared` ledger event is renamed `mission_cleared`. Ledger verbs keep their existing mission guards unchanged.
|
|
89
|
+
- `gjc team` worker auto-checkpoints no longer commit and merge root-level worker runtime state (`.gjc/state/**`, e.g. SDK broker endpoints like `.gjc/state/sdk/<session-id>.json` and settings migration markers) into the leader repo's default branch. The checkpoint classifier's protected prefixes now cover both GJC runtime roots — `.gjc/_session-*/` and `.gjc/state/` — while user-owned `.gjc/` content (config, agents, skills) stays eligible as reviewable worker work. The worker-runtime-state e2e guard now asserts absence at the actual leader merge-target path instead of an unrelated session-scoped path, and matcher boundary cases (`.gjc/state` bare entry vs `.gjc/state-*` siblings) are pinned (#4603).
|
|
90
|
+
- Terminal `provider_safety_stop` errors now surface a bounded diagnostics hint after the raw provider refusal in the TUI assistant-message render and text-mode (`gjc -p`) stderr: safety stops are often specific to the (model, context) pair, the accumulated context does not need to be discarded, and the session continues after a manual model switch. When the default role's configured `modelRoles` chain contains another model that the authoritative model selector resolver accepts (`allowInvalidThinkingSelectorFallback: false`, so malformed suffixes like `:bogus` fail closed while route-suffixed IDs keep their exact-ID semantics) and that is not the model that refused, the hint names it and shows the canonical `/model <provider/model>` command; otherwise it falls back to static `/model` guidance. Purely presentation-only: the stop stays terminal, no second provider dispatch, retry, fallback switch, quarantine, or state mutation occurs (#2069/#2077 invariants preserved), the raw refusal text is retained verbatim, alternates are never claimed to be guaranteed, and unrelated error kinds get no hint (#4650).
|
|
91
|
+
- A gjc session running inside a Herdr pane no longer disappears from Herdr's agent list — permanently, until restarted — the first time it shells out to `gjc`. Herdr's pane variables are inherited, so a nested invocation (an agent running `gjc doctor`, a scripted `gjc -p`, any child that creates a session) claimed the very same `custom:gjc` authority on the parent's `HERDR_PANE_ID`, and on exit released it and cleared the pane title. Herdr keeps the accepted sequence per source as a monotonic watermark, and since 0.14.1 each process seeds its own from the wall clock, so the child's watermark sat minutes ahead of the parent's counter — which advances by one per state transition and can never catch up. The pane lost its agent row and its title while the session was still running, and every later `working`/`idle` report was silently dropped. The process that installs the reporter now stamps `GJC_HERDR_PANE_OWNER=<pane-id>:<pid>` into its environment; descendants inherit it, see the pane already owned, and report nothing at all. A claim naming a different pane is ignored rather than trusted, so a marker inherited across panes cannot silence a legitimate session.
|
|
92
|
+
- Codex-backed web search and OpenAI hosted image generation now sanitize OS-derived `User-Agent` components, preventing non-ASCII Android kernel release names (e.g. `4.4.302-Minimal™-EAS-QTI_Haptic-R26`) from being rejected as invalid HTTP headers before the request is sent — the same crash class fixed for the Codex model provider.
|
|
93
|
+
|
|
94
|
+
- The Herdr pane ownership marker is now versioned and bound to the installing process's OS incarnation plus a process-local token. Descendants remain silent only while the recorded owner is live; stale or reused-PID markers are reclaimed, same-process environment spoofing is discarded, and release clears only the installer's own claim.
|
|
95
|
+
|
|
96
|
+
- ACP `session/new` can reduce latency by overlapping the active-provider walk with model-catalog continuation pages. The provider enumeration (`providers.list/active`) starts only after the first `models.list/current` page has resolved: assembling that page is what finalizes host-side credential state (expired OAuth credentials are refreshed or disabled while it is built, and continuation pages replay the frozen revision), so the provider snapshot can never mix pre- and post-refresh credential state with the catalog it filters. The saving is limited to the actual overlap and is zero for a single-page catalog; the dominant catalog enumeration itself is unchanged and remains per-session (#4682).
|
|
97
|
+
- A resident blob that goes missing on a path that still fails closed now says which session it belonged to and leaves a record. `ResidentBlobMissingError` already received `sessionId` and `sessionFile` from every construction site but rendered neither, so an abort surfaced a bare 64-hex hash naming no transcript to inspect and not even identifying the resident cache as the subsystem; the message now carries both. The three remaining throw paths (staged-reference verification before commit, cold-spill promotion, and materialization under the fail-closed policy) also emitted nothing at all, while the *non-fatal* legacy image resolvers warn on every miss and the demotion salvage reports each placeholder it substitutes: the recoverable cases were observable and the turn-killing ones were not. Each fail-closed throw now logs one bounded line naming the phase, kind, hash, and session, and the resident-transition materialize lane now actually supplies the binding it was already given everywhere else — its inline stores were the one construction that dropped `sessionId`/`sessionFile`, so its record would have logged both as undefined. The corrupted boundary one lane over — a staged sentinel whose ref does not even parse — failed just as closed with a bare `Error` that named no ref, kind, or session and left no record; it now logs the same style of bounded record (with the ref truncated, since a corrupted boundary is exactly where unbounded input appears) and throws a message carrying the binding. Placeholder substitution stays silent on that channel, since it is self-evidencing in the transcript and already has its own report.
|
|
98
|
+
- Fixed an SDK broker that stops publishing while its process stays alive blocking every client permanently (#4704). The publication tick is a chain of awaited IO -- the machine-global session-index lock, the retained heartbeat write, the host checkpoint -- and a fence is only reachable from an observation that returned or an error that was thrown, so a stall that does neither armed neither the loss nor the ambiguity deadline. The broker then held its port and its lock with a published heartbeat aging far past its TTL, and peers refused to reclaim a lock whose owner pid is alive, so every `paseo run --provider gjc`, `gjc sdk`, and ACP launch failed with `Broker lock is held by a live owner (pid N)` until the process was killed by hand (observed: 13.6 hours, after a `session index "heartbeat checkpoint" still holds the index lock after 10s` warning that escalated to nothing). Liveness is now measured from the last *successful* publication rather than from being scheduled: a broker that has published nothing within `max(60s, heartbeatTtl * 4)` terminates itself as `lost-root`, so the dead-owner reclaim from #3963 hands the lock to its successor. The check runs before any await in the tick and on every tick even while an earlier one is still in flight, so a stalled chain cannot starve the path that ends it, and ticks no longer stack behind a stalled predecessor. The retained heartbeat's positional write and its `fsync` also moved off the JS thread onto the libuv blocking pool (`heartbeatAsync`/`syncAsync`, replacing the synchronous bindings): both are unbounded, and running them on the JS thread let a wedged filesystem stop the timers, signal handlers, and broker completion along with the write they were blocking -- a process that could not even notice it had stopped publishing. The retained handle is split accordingly, so shutdown is not merely moved behind the same stall: the single-writer lock is taken only on the pool, while `observe()` and `close()` need the descriptors rather than the write ordering and take a lock held for a pointer read alone. Closing therefore detaches immediately -- an unresolved worker keeps its own reference, observes the closed flag, and returns without committing a heartbeat against authority the owner has already given up -- so `lost-root` completion finishes and the lock stays reclaimable by the successor while the stalled write is still outstanding.
|
|
99
|
+
- The per-turn project context now carries the host's local date, clock time, UTC offset, and IANA zone (`Today is 2026-08-19 (Wed), the local time is 21:04 UTC+09:00 (Asia/Seoul)`) instead of `new Date().toISOString().slice(0, 10)`. The agent previously received a bare UTC calendar date with no clock and no zone, so it reported every timestamp it read from files, logs, git, and APIs in UTC — forcing users outside UTC to convert by hand — and was a full day wrong for east-of-UTC users during their local morning (00:00–09:00 KST rendered as the previous date). The same block now instructs the agent to convert timestamps explicitly identified as UTC to that local zone while preserving timestamps that already include an offset or timezone. Rendering is derived from a single `Intl.DateTimeFormat` pass over one resolved zone, with deterministic UTC fallback for invalid clock values, so date, clock, offset, and zone can never disagree, and the vestigial UTC `date`/`dateTime` fields are dropped from the cached stable system prefix, which never rendered them. The zone follows runtime process configuration and may be influenced by project environment settings; it is display context, not trusted location evidence. Full IANA-zone output can reveal geographic information, and date/time overrides are accepted only in the strict derived formats used by deterministic tests.
|
|
100
|
+
|
|
5
101
|
## [0.14.2] - 2026-08-20
|
|
6
102
|
|
|
7
103
|
### Added
|
|
@@ -10,6 +106,8 @@
|
|
|
10
106
|
- Shift+Tab effort cycling now wins over profile suffixes, and operator-intent effort is distinguished from model-default appends, so explicit effort choices are no longer silently overridden at restore time (#4695, #4700).
|
|
11
107
|
|
|
12
108
|
### Fixed
|
|
109
|
+
- Session lifecycle events no longer disappear when a prompt's post-prompt resource lease cannot be acquired. Assistant and tool messages still append to the canonical transcript while stale events remain fenced to the current run, preventing long tool loops from billing turns that are invisible and unrecoverable (#4788).
|
|
110
|
+
- Fixed a self-locking managed-session scope that eventually forced repeated emergency heap compaction, which users experience as a long session abruptly losing its earlier context. Two defects combined into a deadlock: the per-mutation receipt reconciliation counted **every** dirent in the bound directory against `REPLACEMENT_CLEANUP_RECEIPT_SCAN_LIMIT` rather than the receipts it actually reconciles, and the remnant reaper that is the only mechanism able to shrink such a directory was scheduled *after* that scan. On macOS every managed replacement leaks zero-byte write-protocol remnants (no retained directory authority), so a long-lived scope drifted past the limit, began throwing `managed_replace_cleanup_receipt_limit_exceeded` on every mutation, and could never reach the cleanup that would have fixed it. Tool-output eviction persists through that same store, so each eviction failed closed and retained its original output in memory until the non-disableable emergency floor cut the session (observed in one scope: 50,003 dirents of which 47,043 were inert zero-byte remnants and **zero** were receipts, 55 failed evictions, and 9 heap compactions in a single day). Reaping is now scheduled before reconciliation, and the scan limit counts only receipt entries, so unbounded receipt growth is still rejected while an inert-remnant backlog resolves itself instead of wedging the scope permanently.
|
|
13
111
|
- Fixed worker runtime state under `.gjc/state` being swept into leader-repository auto-checkpoints during team runs; checkpoint classification now excludes it (#4603).
|
|
14
112
|
- Fixed ralplan explicit-target worktree handling end to end: runs bind to the explicit `--worktree-root` instead of fragmenting by writer cwd, a trailing `--worktree-root` with no operand is rejected, HEAD objects are verified and explicit-target writes contained, mutation fails closed on review findings, and `.gjc` is confined before migration and fails closed without procfs.
|
|
15
113
|
- Fixed SDK broker liveness (#4704): a broker that stops publishing while its process stays alive is now terminated; the retained broker heartbeat and watchdog observation run off the JS thread; shutdown and observation no longer hold the retained writer lock; stale watchdog observations are fenced during teardown; startup admission reopens after authority recheck; and retained work is fenced across broker restarts.
|
|
@@ -28,14 +126,19 @@
|
|
|
28
126
|
- Fixed postmortem tests writing into the real crash store; they now isolate into a fresh store.
|
|
29
127
|
## [0.14.1] - 2026-08-18
|
|
30
128
|
- Fixed a resident-cache demotion aborting the turn it was supposed to rescue. The demotion is the salvage that runs *because* the cache already failed, so it materializes a store that is by definition missing blobs — yet it was the one materialize call site that never passed `missingPolicy`, inheriting the fail-closed default. It therefore threw `ResidentBlobMissingError` on the very entry it existed to rescue, the transition never committed, the store stayed broken, and every later turn repeated the same throw out of the `message_end` handler. That handler only `logger.warn`s, so the operator saw a bare `Operation aborted` with every pending tool marked `Tool execution was aborted.` and nothing naming a cache, a blob, or an errno — permanently, until the session was restarted. The salvage now substitutes the same placeholder the persistence path already uses, which never emits a `blob:sha256:` ref, so the invariant the fail-closed policy protects is preserved while the session stays usable. Unrecoverable substitutions are counted in `residentBlobPlaceholderCount` and logged once each with their kind, hash, and the demotion's errno, so silent content loss is visible rather than inferred.
|
|
129
|
+
- Parent task receipts now surface a terminal subagent `local_buffer_overflow`/`local_snapshot_failure` actionably instead of the generic `Task failed; error recorded.`: the executor retains a bounded, redaction-safe `localErrorSummary` (kind + summary) and the receipt preview, `errorSummary`, subagent tool output, and await renderer all name the local kind and carry the diagnostic so a staging-buffer failure can no longer be misread as a provider or context-window problem. Overflow summaries are built ONLY from the agent runtime's structured, identity-checked `bufferOverflow` shape (closed-vocabulary stage/`exceeded` literals and numbers) — never from the free-form `errorMessage`, which a foreign self-labeled error could fill with arbitrary text; a self-labeled overflow without the shape degrades to a neutral sentence. Renderer guidance is kind-conditional (staging limit that reproduces on re-issue vs. serialization defect that is safe to retry) (#4618).
|
|
31
130
|
- Fixed the resident-cache GC reaping the cache of a *running* session, which made long-lived sessions abort every turn with an unexplained `Operation aborted`. The owner lease stores a process start time purely to detect PID reuse, and derived it from `ps -o lstart=` — a zoneless local wall clock rendered from `/etc/localtime` by `ps`, then bound to a zone by `Date.parse` in the reading runtime. Those are two independent zone resolutions: pinning `LC_ALL`/`LANG` stabilized the *format* but not the *instant*, so a reader whose runtime resolved UTC while `ps` rendered UTC+9 computed a start time nine hours off for the same live PID, declared the owner reused, and deleted its instance directory. Observed in the wild: a test process reaped the live instance directories of two running sessions in the same sweep (captured at 32,400,000 ms of skew, exactly the host offset). Each victim then lost the externalized bodies of its large `edit` snapshots, and every later turn died materializing them. The probe now pins `TZ=UTC` on the `ps` child and parses the render explicitly as GMT, so the value is absolute in any reader; leases carry `startTimeBasis: "utc"`, and a lease written before the pin leaves reuse unproven instead of stale, so upgrading cannot reap a live owner once on the way through.
|
|
32
131
|
- A resident text cache demotion now names the OS failure behind it. `ResidentCacheTrustError` lifts the wrapped errno into `causeCode` and a bounded, single-line `causeSummary`; `SessionManager` logs both on `Resident cache trust rejection` and reports `residentCacheDegradedCauseCode` in observability stats; the managed-sidecar disposal warning gains the path-free `causeCode` only, so the cache path it deliberately withholds stays withheld. Previously the record carried `reason` alone, which collapses distinct failures: `blob_create_failed` reads identically whether the instance directory vanished under a live store (`ENOENT`), the process exhausted descriptors (`EMFILE`), or the tree turned read-only (`EACCES`). That mattered in practice — long-running sessions that had externalized a large edit snapshot began aborting every turn once their cache went missing, and the demotion record could not distinguish that from a hostile-path rejection without attaching a debugger to a process that was already failing.
|
|
33
132
|
- Fixed `/usage` and `/usage check` omitting provider limits for stored OAuth accounts. Cache-only snapshots now use the provider's resolved base URL when reading usage, and explicit checks render the successful probe report directly instead of depending on a cache-key-identical readback (#4634).
|
|
34
133
|
- `/usage` shows quota resets again. Canonicalizing multi-account management (`364f14022`) rewired the interactive `/usage` handler from the graphical panel to the account-inventory text view, and that view rendered only `label: N% used (M% left)` — no bars, no reset countdown — leaving the command unable to answer when a quota comes back and stranding `handleUsageCommand`/`renderUsageReports` as unreachable code. Plain `/usage` in the TUI renders the panel again, sourced from the same cache-only inventory snapshot the text view reads, so the cache-only contract is preserved and no fetch or probe is reintroduced; `/usage check` keeps the text path, where the per-credential health verdict is the point. Account rows on every surface (TUI, ACP, Telegram) now carry `resets in <countdown> (<absolute time>)`, and the panel itself gained multi-account reset lines, hour-precision countdowns past 48h (`6d 14h`, previously rounded to a bare `7d` at anywhere from 6.6 to 7.4 days), and set-aware account-label truncation so pooled credentials sharing a domain no longer collapse into identical columns.
|
|
134
|
+
- A Round-0 deep-interview `ask` whose `deepInterview` object is present with topology identity (`round: 0`, `component: "review-topology", "dimension": "topology") but omits required topology fields (`ambiguity`, `intent_contract`) is now rejected before coercion with a targeted correction naming the omitted fields and the exact `intent_contract` shape, instead of generic zod issues plus a full payload echo. The incomplete object is not a retired-pair recovery candidate, so it fell through to schema validation whose message named neither the contract nor what a corrected retry must contain — and a metadata-only retry repeated the same invalid shape (#4649). Recovery stays fail-closed: `intent_contract` is never synthesized (items and affirmative labels are the locked-intent evidence), and the recorder still locks intent only on an affirmative user answer. Valid contract-only Round 0 and post-Round-0 payloads validate unchanged.
|
|
135
|
+
- Added an opt-in crash upstream so local crash signatures can be aggregated across installs, which the agent-dir-scoped index could never answer on its own. `crashReport.upstream` (default `off`) plus `crashReport.upstreamDsn` (or `GJC_CRASH_SENTRY_DSN`) gate a hand-rolled Sentry envelope POST; no DSN literal is compiled into the binary, so a build has no destination to fall back to and `off` costs one settings read and no IO. The relay never runs on the fatal path — a crashing process still performs exactly one `O_APPEND` journal write, and relaying happens at the next startup after index compaction, bounded to 8 signatures per run with a 10s timeout. A Sentry SDK is deliberately not used: SDK defaults attach breadcrumbs, environment, and argv, which would defeat the point of the outbound sanitizer. Every crash-derived field must pass `sanitizeExternalCrashV1` and a refusal drops that signature outright rather than falling back to a less-sanitized payload; the emitted payload is a fixed key set (`event_id`, `timestamp`, `platform`, `level`, `logger`, `release`, `environment`, `fingerprint`, `exception`, `tags`, `extra`, `sdk`) with `user`, `server_name`, `contexts`, `breadcrumbs`, `request`, `modules`, env vars, argv, and hostname structurally absent. The gjc fingerprint is sent as Sentry's `fingerprint` array so grouping is ours rather than Sentry's heuristics — one upstream issue per gjc signature, verified by two events with disjoint stack frames merging into a single group. `gjc crash relay` exposes the same batch as a loud, non-zero-on-refusal command. A new `relayed` journal event stamps `relayedAt` monotonically so re-runs do not resend unless `lastSeen` advanced, and `relayedAt` is deliberately not an input to index eviction. The `gjc crash report` GitHub flow keeps its per-invocation, digest-confirmed consent boundary unchanged; the two egress channels are separate with separate rules.
|
|
136
|
+
- Managed-fallback sessions now steer escaped-non-ASCII retries instead of re-issuing them blind, and bound them. The agent loop's `escaped_arguments_discarded` outcome reports whether the discarded attempt still lacked a transient recovery instruction, and the session's retry continuation attaches exactly one such instruction (naming the `\uXXXX` defect and demanding literal UTF-8) through the new `transientRecoveryMessage` prompt option, so the deterministic Hangul-escaping failure observed on `ask` payloads is corrected on the retry instead of exhausting the budget every turn. Because those retries are deliberately un-charged (the defect is not provider evidence), each continuation is a fresh loop with a fresh in-loop resample budget and the fallback chain never exhausts on them — so a deterministic escaper previously looped forever under managed fallback (measured: 2,940 provider calls in 5s before an external timeout). The session now bounds escaped retries per logical run (steered retry + blind retry, reset each user turn) and fails closed through the terminal exhaustion message. The instruction is transient: sent only to the provider, never committed to durable history, never riding a later request; the terminal per-call rejection stays fail-closed.
|
|
35
137
|
- Fixed resume listing scaling its read-syscall count with total transcript bytes. The trailing `header_patch` scan walks back to BOF whenever `cwd`/`title` stay unresolved (#3633), which is the common case because only `/rename` and workspace moves ever emit a patch; because the scan borrowed the caller's 4 KiB prefix buffer, that walk cost one `read` per 4 KiB of every candidate transcript on each `--resume`, `--continue`, and picker open. The scan now owns a 64 KiB buffer, so the same bytes are covered in ~16x fewer syscalls. Measured on a real 31-session workspace holding 105 MB of transcripts (largest 41 MB): 25,715 reads / 61.9 s before, 1,652 reads / 0.5 s after, with all 24 recovered titles unchanged. Buried-title recovery, the bytes examined, the `header_patch` marker prefilter, and listing results are unchanged.
|
|
36
138
|
|
|
37
139
|
- Fixed Telegram forum topics freezing after the identity header: an attached, trusted session whose topic-host lease expired (20 s `HEARTBEAT_TTL_MS`) could never renew it, because `renewActiveTopicLeases` only renewed sessions that already passed the trusted-lease gate, so every later `turn_stream`/`context_update`/tool frame was rejected pre-send with "trusted attachment lease is stale" and the topic never updated again (#4647). A live attachment that still owns its exact logical session and holds an authorized recovery lease may now re-arm its own expired host lease — from the ownership heartbeat and once more before the publication gate — mirroring `acquireLease` admission (expired-but-owned active lease, or a same-owner resume inside the disconnect-grace window, which also covers the incident's persisted `disconnect_grace` record). Dropped sessions, closed endpoints, foreign lease owners, archive-fenced/inactive topics, malformed bindings, and cross-session ownership checks all still fail closed. Daemon generation bumped 169→170.
|
|
38
140
|
- A tool call the agent loop refuses now reports why it was refused. The loop attaches its own failure envelope (`{ failureKind }`) in place of the tool's details, and the TUI still dispatched that envelope at the tool's renderer, which owns only its own detail shape: `search_tool_bm25` threw on `details.tools`, `task` printed `Task result details unavailable`, `resolve` printed `Failed: pending action`, and `write` painted its success card — in every case the rejection text (for example the `\uXXXX`-escaped-arguments rejection that ends a Korean `task` call) never reached the screen. Such a result now renders the same error card a tool without a renderer already produces: the failed status line plus the reason. Results a tool produced itself keep their renderer, including `todo_write`'s own `failureKind`.
|
|
141
|
+
- `SdkClient` requests accept `beforeDispatch`/`onDispatch` boundary callbacks (`SdkRequestOptions`), giving dispatch-aware consumers a synchronous post-send boundary without owning the raw transport lifecycle (#4640). `onDispatch` fires immediately after the frame is handed to the socket — never before — with the exact request identity (`frame.id`), `connectionId`, and transport generation; a throwing observer cannot displace settlement, so the request still settles through its response, deadline, or `uncertain_after_send` retirement on transport close. `beforeDispatch` runs before the write and its throw aborts the dispatch with nothing on the wire (no sent record, caller's own error, retryable). This replaces the only previous alternative — a raw `send()` + `onFrame()` request that could never settle on a close after handoff and waited for its own timeout — while keeping pending-request ownership inside the client.
|
|
39
142
|
- Discovered oMLX models now keep thinking metadata (`reasoning: true`, `supportsReasoningEffort`, `thinkingFormat: qwen-chat-template`) so `macos-omlx-*` role suffixes (`:low`/`:medium`/`:high`) survive clamp and reach oMLX as `chat_template_kwargs.reasoning_effort`.
|
|
40
143
|
- Added built-in `MACOS LOCAL (OMLX)` model profiles (`macos-omlx-fast`, `macos-omlx-balanced`, `macos-omlx-quality`, `macos-omlx-abliterated-fast`, `macos-omlx-abliterated-balanced`) for oMLX local inference on Apple Silicon Macs with native full context support and single-LLM thinking effort role mappings to eliminate model swap latency.
|
|
41
144
|
- Fixed an HTTP 400 that killed every deep-interview session on the `google-antigravity` provider before the first assistant turn. The Round-0 topology `ask` schema pinned `round` with `z.literal(0)`, which zod serializes as `const: 0` and the Cloud Code Assist normalizer rewrites to a numeric `enum: [0]` — a shape CCA rejects (`TYPE_STRING`). `round` is now pinned with an integer range `[0, 0]` instead, so the wire schema carries `type: integer` with the bounds spilled into the description (the same treatment `ambiguity` already gets) and no numeric enum remains. Runtime contract unchanged: only `0` validates (#4606).
|
|
@@ -46,9 +149,13 @@
|
|
|
46
149
|
- A cold-spilled tool call whose payload can no longer be recovered no longer breaks the whole session. `rehydrateColdSpillRef` reports an unreadable, hash-mismatched, or unparseable blob by returning the human-readable `[Cold-spill blob unavailable: …]` sentence, and that string landed directly on `toolCall.arguments`. Providers forward it verbatim — Anthropic serializes it into `tool_use.input` and rejects the entire request with `tool_use.input: Input should be a valid dictionary` — so one missing blob made a compacted session permanently unresumable, with an error naming a message index rather than the lost payload. Rehydration now enforces the object invariant at the session boundary and degrades just that call to the existing `incompleteArguments` / `"malformed"` contract, preserving the recovered text under `recoveryNotice`, so the agent loop refuses that one call with retryable guidance while the rest of the transcript still loads.
|
|
47
150
|
- `/copy` no longer throws when the most recent assistant turn carries a persisted `toolCall.arguments` of `null` (written by an earlier cold-spill eviction path). The last-bash-command lookup read `arguments.command` unguarded, the same shape that made compaction pruning fatal.
|
|
48
151
|
|
|
152
|
+
- The `/model` selector no longer freezes the UI thread for hundreds of milliseconds on open, selection, tab refresh, and every catalog change. Canonical catalog queries rebuilt a 4k-entry candidate-key set (and a full provider policy + catalog order) once per canonical record — 1,243× per query — and the selector then re-resolved every record through the same per-record path again. `getCanonicalModels` now builds the variant filter plan once per query, and a new batch `getCanonicalModelSelections` resolves every record with one shared candidate set, policy, and catalog order; the selector and `--list-models` consume the batch form. On the bundled 4,276-model catalog with credentials: canonical listing 348ms→1.5ms, batch resolution ~700ms→7.5ms, post-selection refresh 351ms→40ms, per-catalog-change refresh 345ms→40ms. Per-record `resolveCanonicalModel` semantics are unchanged and pinned by a whole-catalog equivalence test.
|
|
153
|
+
- Provider tabs in the `/model` selector no longer force a network round-trip on every visit. The selector now refreshes the selected provider with the same cache-respecting strategy the rest of the app uses (`online-if-uncached`), and configured discovery now publishes a credential-safe SHA-256 provenance fingerprint of the reusable effective discovery request context (credential evidence, normalized endpoint, non-secret effective request headers, and the semantic request shape: discovery type, provider api, per-prefix api routing, models.dev catalog key) with its cache rows; secret-bearing header and endpoint query/userinfo contexts persist only sanitized model metadata with secret-independent one-shot provenance and cannot reuse cached discovery — previously the row stored discovered model ids without provenance, so every network-strategy refresh treated its own cache as suspect and re-fetched on each provider-tab visit. A provider tab whose discovery cache is fresh now renders instantly with zero fetches (the discovery state also reports the cache row's real fetch time instead of "now"); stale (24h TTL), missing, or request-context-changed caches (credential, endpoint, or e.g. a tenant/project header) still re-fetch, pinned by a zero-fetch revisit test, a constant-credential header-change invalidation test, and fetch-count probes. Raw header values are never persisted or logged — only the one-way digest is stored.
|
|
154
|
+
- The `/model` selector and `gjc --list-models` no longer surface another discovery context's cached models when a provenance-forced refetch fails. Stale-while-error fallback previously merged cached dynamic rows unconditionally on a failed/absent fetch; now those rows only serve while the cache's provenance fingerprint still matches the current request context, so a credential, endpoint, tenant-header, or request-shape change whose validation re-fetch errors (e.g. a 503) fails closed — the foreign rows are omitted from the catalog and the discovery state reports the error instead. Same-context stale-while-error and explicit `offline` reuse remain supported. The synchronous constructor-time cache load applies the same provenance gate, so a rebooted registry cannot briefly expose mismatched rows before refresh. A failed fetch with no prior row now persists a non-authoritative tombstone that preserves a concurrent insert (`ON CONFLICT DO NOTHING`) and a same-context row is downgraded only by compare-and-swap on its exact contents, so a concurrent foreign writer can never be overwritten or last-writer-win; the retry cadence for those non-authoritative rows follows the standard 5-minute backoff instead of a network attempt on every provider-tab visit. Opening the `/model` picker in an offline pass now also clears previously published dynamic models for discovery contexts that can never be cache-reused (secret-bearing headers, endpoint query/userinfo, malformed URLs), so another context's rows cannot linger in the live catalog until the next successful online discovery; bundled/static/custom models with the same id are preserved.
|
|
155
|
+
- Configured-discovery providers whose cache row is served at startup (constructor load, fresh-cache revisits) now re-derive the provider transport override — including non-secret request headers such as `OpenAI-Organization` — from the same source the live publish path uses. Persisted cache rows deliberately store sanitized model metadata without headers, so without this the models surfaced from a warm cache lost their provider headers until the next successful online discovery.
|
|
156
|
+
- Re-added the `commandcode-goat` model profile changelog entry (default GLM-5.3 / execution DeepSeek V4 Flash / planning Kimi K3 / criticism GLM-5.2 / architecture DeepSeek V4 Pro) under Unreleased; the rebase had dropped it.
|
|
49
157
|
## [0.14.0] - 2026-08-17
|
|
50
158
|
- Documented how to run GJC inside external agent shells. `README.md` gains a support-rated integration table for [Paseo](https://paseo.sh) (★★★★★ — `gjc setup paseo` writes a conformance-tested ACP provider), [Orca](https://onorca.dev) (★★★★ — GJC runs as a custom CLI agent per worktree), and [T3 Code](https://t3.codes) (★★★ experimental — no GJC harness exists upstream yet), and [`docs/terminal-app-integrations.md`](../../docs/terminal-app-integrations.md) carries the per-host setup, verification, cancel-semantics, and troubleshooting detail. The T3 Code row is deliberately marked unsupported rather than advertising an install command for a bridge that does not exist.
|
|
51
|
-
- Added the `commandcode-goat` model profile for the Command Code GOAT provider, assigning GLM-5.3 to the default role, DeepSeek V4 Flash to execution, Kimi K3 to planning, GLM-5.2 to criticism, and DeepSeek V4 Pro to architecture.
|
|
52
159
|
|
|
53
160
|
- Session endpoints hosted on the notification-adapter transport now deliver every ring-retained session event live to attached SDK subscribers as the same positioned `event` envelope (`generation`/`seq`) that `event_replay` returns, sent per connection over the validated directed leg with the same capability gating replay applies. Previously the live leg only pushed raw side-channel frames — the native broadcast enum reduced non-native kinds (including terminal `agent_end` lifecycle) to empty `unknown` frames, and correlated lifecycle reached only the submitting connection — so an already-attached direct SDK subscriber could observe a later positioned event, including a turn's terminal lifecycle, only by issuing another replay. Each connection's directed writer now bounds queued host frames to the replay-ring capacity; a lagged subscriber rejects additional best-effort live sends and recovers through replay (including the existing sequence-gap contract) instead of growing an unbounded backlog. Ring persistence, replay ordering, event positions, correlated requester delivery, and native notification frames are unchanged.
|
|
54
161
|
- Persisted edit results no longer inline complete pre/post-edit file snapshots. `EditToolDetails.oldText`/`newText` (and each `perFileResults[]` copy) larger than 16 KiB are replaced at the session-persistence boundary by a fixed-size digest receipt (`oldTextDigest`/`newTextDigest`: UTF-8 byte length + SHA-256) plus an externalization marker, so a tiny `apply_patch` to a large file costs bytes proportional to its diff instead of ~2x the file size in the managed transcript (#4566). Live in-process results still carry full bodies for ACP `diff` ToolCallContent and editors; sub-16 KiB snapshots persist verbatim; diffs, paths, ops, first-changed-line, diagnostics, and meta are unchanged. A regression suite pins bounding, inline sub-cap behavior, multi-file copies, and the near-limit committed-edit durability contract.
|
|
@@ -57,6 +164,16 @@
|
|
|
57
164
|
|
|
58
165
|
- Managed fallback local snapshot failures now surface their one producer-boundary diagnostic immediately instead of re-issuing the identical request up to three times. The failure still never charges the provider fallback chain, advances models, or mutates credentials.
|
|
59
166
|
|
|
167
|
+
### Added
|
|
168
|
+
|
|
169
|
+
- Ultragoal boundary validation is now risk-proportional and runtime-authoritative (#4560): a deterministic applicability policy selects expensive boundary lanes from trusted change-set facts, plan shape, open review blockers, and a runtime-computed source-basis digest exposed by `gjc ultragoal quality-gate source-hash`. Low-risk single-goal boundaries may omit redundant cleaner and architect review only through an exact runtime-validated `validationLaneSelection` proof; QA, targeted verification, frozen `sourceHash` binding, receipts, provenance, and join-before-repair remain mandatory. Workflow-enforcement changes classify themselves as high-risk, untracked files are content/identity hashed without following symlinks, and CI changed-path metadata is bound only when the inspected Git root is the authoritative `GITHUB_WORKSPACE`, so outer workflow metadata cannot poison an independent nested/test repository. High-risk, multi-goal, computer/shared-registry, migration, security, native, SDK/public-contract, agent-wire, incomplete, and untrusted changes retain the full cohort. Terminal-critic reuse additionally requires the current and prior cohort hashes to match the authoritative digest.
|
|
170
|
+
|
|
171
|
+
### Changed
|
|
172
|
+
|
|
173
|
+
- Ralplan now performs a persisted material-intent reconciliation stage before Architect/Critic consensus (#4560), asking only about unresolved decisions that can change objective, scope, non-goals, acceptance criteria, architecture, or verification. Material corrections revise the Planner artifact before review; the post-consensus interview is retained as a delta gate for assumptions introduced by review.
|
|
174
|
+
- Hardened the #4560 risk-proportional validation and compaction recovery surfaces after review. Multi-goal risk is now derived from the durable aggregate shape rather than remaining goals, so a two-goal run stays high-risk through its final aggregate boundary; security classification adds `packages/ai/src/auth-storage.ts`, `runtime-mcp/oauth-flow.ts`, and `commands/auth-broker.ts` plus filename-marker detection so credential surfaces cannot grade low-risk wherever they live; `gjc ultragoal quality-gate lane-selection` exposes the authoritative computed proof so a reduced cohort is obtainable without running the omitted lanes, and architect evidence is no longer demanded when the verified selection omits that lane. Ralplan recovery resumes a planner-only run at intent reconciliation instead of consensus review; recovery verifies every path component with no-follow semantics and hashes the same bytes it projects; and checkpoint change-set capture re-checks a repository state witness so a snapshot raced by concurrent mutation fails closed as incomplete.
|
|
175
|
+
- Compaction recovery for active Ralplan and Ultragoal runs now reloads a bounded structured projection from canonical durable workflow state instead of relying on summary prose alone (#4560). Active Ralplan consensus restores its confined, digest-verified Planner/revision artifact and exact review/revision/reconciliation action; Ultragoal restores goals, blockers, joined cohort evidence, and next action. Continuation enforces scope reload, latest-user-intent precedence, explicit classification of scope expansion, unchanged-basis rerun avoidance, and zero-progress escalation counted once per actual compaction; malformed, stale, tampered, paused, terminal, and unrecognized state safely retains the existing generic behavior.
|
|
176
|
+
|
|
60
177
|
- Managed fallback local buffer overflows (`local_buffer_overflow`) now surface immediately with the original local diagnostic instead of entering the bounded `unknown` retry class: re-streaming the same request reproduces the same oversized response, and a local staging failure must never charge or advance the provider fallback chain, emit `model_fallback_switched`, or rotate credentials.
|
|
61
178
|
|
|
62
179
|
- Fixed Windows startup lock starvation in the SDK session index (#4544): a live detached broker holding `index.jsonl.lock` across a wedged Windows sync-family await exhausted every later launch's full 600-attempt lock budget. OS process-incarnation probes (which can spawn `powershell.exe` on Windows) now run before the machine-global index lock is taken in the heartbeat checkpoint pass, the conditional-unregister pass (now routed through the shared slow-operation choke point), and the own-pid registration derivation; the PowerShell fallback is time-bounded (`timeout` + `SIGKILL` enforcement); locked index transactions log an actionable slow-operation warning after 10s naming the exact operation; and lock exhaustion errors now identify the live owner (pid, liveness, lock path) instead of a bare attempt count. The heartbeat pass also rechecks probe freshness after the locked replay on the monotonic clock: a pid reused while the replay re-reads the log fails closed (no heartbeat this cycle) instead of checkpointing the wrong host, and a backward wall-clock step (NTP slew, manual fix, VM restore) cannot defeat the bound. A lock record carrying a foreign `owner_host_id` (shared-volume topic registry) reports its owner host with unknown liveness instead of probing a coincidental local pid. Stale-lock safety is unchanged: a proven-live owner's lock is never stolen.
|
|
@@ -79,8 +196,10 @@
|
|
|
79
196
|
### Changed
|
|
80
197
|
- Updated every bundled GLM model profile (`glm-eco`, `glm-medium`, and `glm-pro`) from ZAI GLM-5.2 to GLM-5.3.
|
|
81
198
|
- `browser` Chrome profile mode (`app.browser: "chrome"`) now defaults an omitted `path` to installed Chrome/Chromium and an omitted `profile_directory` to `"Default"`. `user_data_dir` remains explicit and must be non-default because Chrome 136+ disables remote debugging for default Chrome data directories; Stable/Beta/Dev/Canary/Chromium roots (including trusted Linux environment, Flatpak, and Snap defaults), symlink aliases to them, and non-Chrome browser executables are rejected with remediation instead of timing out or risking cross-brand profile access. Profile and Windows executable discovery use trusted environment sources that ignore repository `.env`, platform path semantics are preserved, executable/profile-root canonicalization races stalled filesystem work against cancellation, the canonical Snap Chromium launcher is admitted only when it resolves to `/usr/bin/snap`, and wrapper-launched Linux Chrome processes are reused through an abort-aware asynchronous `/proc` scan only after kernel executable identity plus exact profile/loopback-CDP guards pass. Cancellation propagates before, during, and after CDP probing, and omitted profile names render as `Default` in the TUI.
|
|
199
|
+
- Breaking: replaced the bundled `team` workflow and `gjc team` command with the research-only `autoresearch` workflow and its session-scoped mission ledger. `gjc rlm` is removed; existing Team/RLM automation must move to the supported workflow and approval-gated execution surfaces.
|
|
82
200
|
- Defense-in-depth: when an Anthropic-origin assistant transcript message carrying directly adjacent `thinking`/`redacted_thinking` blocks is persisted, a single bounded warn is emitted per session manager instance — but only in development/test builds, never in production. The diagnostic names only the envelope shape (block count, adjacency presence, provider), never raw thinking text, signatures, redacted payloads, or transcript-path metadata. Storage is never mutated — the send-boundary collapse remains the wire source of truth; this is a read-only observation that helps surface upstream producers of the rejected shape (#4443).
|
|
83
201
|
### Fixed
|
|
202
|
+
- Coordinator durable writes now tolerate only Windows directory-handle `EPERM`/`EACCES` at the directory open/sync barrier. File fsync, atomic rename, cleanup, and all other filesystem failures remain fail-closed; on Windows, the renamed directory entry has weaker crash durability because the operating system does not support the directory barrier.
|
|
84
203
|
- The Herdr integration no longer disappears from Herdr's sidebar after a session restarts in the same pane. Herdr keeps the highest report sequence it has accepted per source on the pane's terminal, and that watermark outlives the gjc process that set it, so a counter restarting at zero made every report of the next session look stale: the pane showed no agent at all until the new session happened to out-count its predecessor, which for an idle resumed session could be never. Lifecycle-state and pane-title sequences now start from wall-clock milliseconds (with room for 1000 reports per millisecond), matching what Herdr's own bundled integrations do.
|
|
85
204
|
- Ouroboros now keeps its own authored base, idle, work-transition, work, and burst frames across Sixel, Kitty, and iTerm2 rendering. Marked iTerm pet drag paths are also discarded in Bash and Python composer modes instead of being inserted literally.
|
|
86
205
|
- iTerm2 pet animation now stays silent while the TUI is temporarily stopped for an external editor or todo edit, preventing raster writes from corrupting the child terminal session. Its unavailable-capability deadline now begins after iTerm probing starts rather than during initialization work.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentProgress, AgentSource } from "../task/types";
|
|
1
|
+
import type { AgentProgress, AgentSource, LocalErrorSummary } from "../task/types";
|
|
2
2
|
export interface AsyncJob {
|
|
3
3
|
id: string;
|
|
4
4
|
readonly generation: string;
|
|
@@ -19,6 +19,8 @@ export interface AsyncJob {
|
|
|
19
19
|
errorText?: string;
|
|
20
20
|
/** Safe, bounded cause when session setup failed before the LLM began work. */
|
|
21
21
|
setupFailureSummary?: string;
|
|
22
|
+
/** Safe, bounded summary of a terminal local (non-provider) failure kind. */
|
|
23
|
+
localErrorSummary?: LocalErrorSummary;
|
|
22
24
|
metadata?: AsyncJobMetadata;
|
|
23
25
|
/**
|
|
24
26
|
* Registry id of the agent that registered the job (e.g. "0-Main",
|
|
@@ -62,6 +64,7 @@ export type SubagentRunOutcome = {
|
|
|
62
64
|
kind: "failed";
|
|
63
65
|
text: string;
|
|
64
66
|
setupFailureSummary?: string;
|
|
67
|
+
localErrorSummary?: LocalErrorSummary;
|
|
65
68
|
} | {
|
|
66
69
|
kind: "paused";
|
|
67
70
|
note?: string;
|
|
@@ -1,4 +1,29 @@
|
|
|
1
|
-
import type
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { type AutoresearchExperimentState } from "./runs";
|
|
2
|
+
export interface AutoresearchDashboardRuntime {
|
|
3
|
+
modeOn: boolean;
|
|
4
|
+
running?: {
|
|
5
|
+
command: string;
|
|
6
|
+
startedAt: number;
|
|
7
|
+
} | null;
|
|
8
|
+
pendingRun?: {
|
|
9
|
+
runNumber: number;
|
|
10
|
+
passed: boolean;
|
|
11
|
+
parsedPrimary: number | null;
|
|
12
|
+
} | null;
|
|
13
|
+
}
|
|
14
|
+
export interface AutoresearchDashboardInput {
|
|
15
|
+
state: AutoresearchExperimentState;
|
|
16
|
+
runtime?: AutoresearchDashboardRuntime;
|
|
17
|
+
}
|
|
18
|
+
/** Collapsed one-line summary. */
|
|
19
|
+
export declare function renderCollapsedLine(input: AutoresearchDashboardInput, width?: number): string;
|
|
20
|
+
/** Expanded header line. */
|
|
21
|
+
export declare function renderExpandedHeader(input: AutoresearchDashboardInput, width?: number): string;
|
|
22
|
+
/**
|
|
23
|
+
* Expanded dashboard body: segment summary, baseline/best lines, then the run
|
|
24
|
+
* table. `maxRows > 0` keeps only the last `maxRows` rows of the current
|
|
25
|
+
* segment (with an ellipsis for hidden earlier runs); `0` shows all.
|
|
26
|
+
*/
|
|
27
|
+
export declare function renderDashboardLines(input: AutoresearchDashboardInput, width?: number, maxRows?: number): string[];
|
|
28
|
+
/** The full expanded dashboard (header + body). */
|
|
29
|
+
export declare function renderExpandedDashboard(input: AutoresearchDashboardInput, width?: number, maxRows?: number): string;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Autoresearch data context: RLM data-description loading, GATED to explicit
|
|
3
|
+
* `data` or `mixed` mission mode only.
|
|
4
|
+
*
|
|
5
|
+
* This is the AC-16 no-inference rule at the consumption end: a mission mode is
|
|
6
|
+
* never inferred from the presence of a data file, so in `web` mode a data
|
|
7
|
+
* description must NOT be loaded — not an explicit `--data` flag, and not a
|
|
8
|
+
* project-root `DATA.md` auto-load. `data` and `mixed` modes may load one.
|
|
9
|
+
*/
|
|
10
|
+
import { type RlmDataContext } from "../rlm/data-context";
|
|
11
|
+
export type { RlmDataContext };
|
|
12
|
+
export type AutoresearchDataContextMode = "web" | "mixed" | "data";
|
|
13
|
+
export interface AutoresearchDataContextInput {
|
|
14
|
+
cwd: string;
|
|
15
|
+
mode: AutoresearchDataContextMode;
|
|
16
|
+
/** Explicit data path (resolved against `cwd`). Optional. */
|
|
17
|
+
dataFlag?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Load the data description for a mission, or return null when the mode does
|
|
21
|
+
* not permit one. `web` mode always returns null without touching the
|
|
22
|
+
* filesystem; `data`/`mixed` delegate to the RLM loader (explicit `--data`
|
|
23
|
+
* path wins over project-root `DATA.md`, which auto-loads when present).
|
|
24
|
+
*/
|
|
25
|
+
export declare function loadAutoresearchDataContext(input: AutoresearchDataContextInput): Promise<RlmDataContext | null>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
export declare const AUTORESEARCH_BRANCH_PREFIX = "autoresearch/";
|
|
2
2
|
export interface EnsureAutoresearchBranchFailure {
|
|
3
3
|
error: string;
|
|
4
4
|
ok: false;
|
|
@@ -10,20 +10,33 @@ export interface EnsureAutoresearchBranchSuccess {
|
|
|
10
10
|
warning?: string;
|
|
11
11
|
}
|
|
12
12
|
export type EnsureAutoresearchBranchResult = EnsureAutoresearchBranchFailure | EnsureAutoresearchBranchSuccess;
|
|
13
|
-
export declare function getCurrentAutoresearchBranch(
|
|
13
|
+
export declare function getCurrentAutoresearchBranch(workDir: string): Promise<string | null>;
|
|
14
|
+
/**
|
|
15
|
+
* True when `rawPath` is one of the mission's own research artifacts: the
|
|
16
|
+
* `autoresearch.sh` harness at the working-directory root. This is the complete
|
|
17
|
+
* agent-writable surface for an active mission — product code, manifests,
|
|
18
|
+
* dependencies, and every other path stay blocked by the research-only
|
|
19
|
+
* mutation guard regardless of branch name.
|
|
20
|
+
*
|
|
21
|
+
* Mission state under `.gjc/**` is intentionally NOT listed here: it is
|
|
22
|
+
* runtime-owned and only the sanctioned `gjc autoresearch` CLI writes it.
|
|
23
|
+
*/
|
|
24
|
+
export declare function isAutoresearchAuthorizedResearchPath(_cwd: string, rawPath: string): boolean;
|
|
14
25
|
/**
|
|
15
26
|
* Ensure the working tree is on an `autoresearch/*` branch when possible.
|
|
16
27
|
*
|
|
17
|
-
*
|
|
18
|
-
* `{ ok: true, branchName: null, warning }` rather than failing
|
|
19
|
-
* warning and continues on the current branch — `keep`
|
|
20
|
-
* will revert only run-modified paths
|
|
28
|
+
* When the worktree is dirty and we are not already on an autoresearch branch,
|
|
29
|
+
* this returns `{ ok: true, branchName: null, warning }` rather than failing:
|
|
30
|
+
* the caller surfaces the warning and continues on the current branch — `keep`
|
|
31
|
+
* will skip auto-commits and `discard` will revert only run-modified paths
|
|
32
|
+
* instead of resetting to baseline.
|
|
21
33
|
*/
|
|
22
|
-
export declare function ensureAutoresearchBranch(
|
|
34
|
+
export declare function ensureAutoresearchBranch(workDir: string, goal: string | null): Promise<EnsureAutoresearchBranchResult>;
|
|
23
35
|
export declare function parseWorkDirDirtyPaths(statusOutput: string, workDirPrefix: string): string[];
|
|
24
36
|
export declare function relativizeGitPathToWorkDir(repoRelativePath: string, workDirPrefix: string): string | null;
|
|
25
37
|
export declare function parseDirtyPaths(statusOutput: string): string[];
|
|
26
38
|
export declare function normalizeStatusPath(rawPath: string): string;
|
|
39
|
+
export declare function slugifyGoal(goal: string | null): string;
|
|
27
40
|
export interface DirtyPathEntry {
|
|
28
41
|
path: string;
|
|
29
42
|
untracked: boolean;
|
|
@@ -34,3 +47,48 @@ export declare function computeRunModifiedPaths(preRunDirtyPaths: string[], curr
|
|
|
34
47
|
tracked: string[];
|
|
35
48
|
untracked: string[];
|
|
36
49
|
};
|
|
50
|
+
export interface KeepAutoresearchRunInput {
|
|
51
|
+
cwd: string;
|
|
52
|
+
description: string;
|
|
53
|
+
status: string;
|
|
54
|
+
metric: number;
|
|
55
|
+
metrics: {
|
|
56
|
+
[key: string]: number;
|
|
57
|
+
};
|
|
58
|
+
/** Paths modified by this run (workdir-relative). */
|
|
59
|
+
files: string[];
|
|
60
|
+
/** True when on a dedicated `autoresearch/*` branch. */
|
|
61
|
+
onAutoresearchBranch: boolean;
|
|
62
|
+
primaryMetric: string;
|
|
63
|
+
}
|
|
64
|
+
export interface KeepAutoresearchRunResult {
|
|
65
|
+
/** Error text when the commit failed; `ok` is false. */
|
|
66
|
+
error?: string;
|
|
67
|
+
/** Human note for the caller (e.g. "nothing to commit", "committed", "skipped"). */
|
|
68
|
+
note?: string;
|
|
69
|
+
/** Commit SHA after the keep (null when nothing was committed). */
|
|
70
|
+
commitHash?: string | null;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Keep an iteration: commit the modified files on a dedicated autoresearch
|
|
74
|
+
* branch. Off-branch (degraded mode) auto-commit is skipped and the files stay
|
|
75
|
+
* in the worktree, matching the extension contract.
|
|
76
|
+
*/
|
|
77
|
+
export declare function keepAutoresearchRun(input: KeepAutoresearchRunInput): Promise<KeepAutoresearchRunResult>;
|
|
78
|
+
export interface DiscardAutoresearchRunInput {
|
|
79
|
+
cwd: string;
|
|
80
|
+
/** Paths that were already dirty before the run started (workdir-relative). */
|
|
81
|
+
preRunDirtyPaths: string[];
|
|
82
|
+
/** True when on a dedicated `autoresearch/*` branch. */
|
|
83
|
+
onAutoresearchBranch: boolean;
|
|
84
|
+
}
|
|
85
|
+
export interface DiscardAutoresearchRunResult {
|
|
86
|
+
error?: string;
|
|
87
|
+
note?: string;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Discard a failed/crashed iteration. On a dedicated autoresearch branch the
|
|
91
|
+
* worktree resets to HEAD (never rewinding prior `keep` commits); off-branch
|
|
92
|
+
* only run-modified paths are reverted so pre-existing user dirt survives.
|
|
93
|
+
*/
|
|
94
|
+
export declare function discardAutoresearchRun(input: DiscardAutoresearchRunInput): Promise<DiscardAutoresearchRunResult>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Autoresearch harness contract: the `autoresearch.sh` entrypoint contract plus
|
|
3
|
+
* parsing of `METRIC <name>=<value>` and `ASI <key>=<value>` lines out of
|
|
4
|
+
* captured output.
|
|
5
|
+
*
|
|
6
|
+
* The contract (ported from the deleted extension's `helpers.ts`):
|
|
7
|
+
* - the harness is `./autoresearch.sh` at the working directory;
|
|
8
|
+
* - it exits 0 on success and non-zero on failure;
|
|
9
|
+
* - it prints the primary metric as `METRIC <name>=<value>` and any secondary
|
|
10
|
+
* metrics as additional `METRIC` lines;
|
|
11
|
+
* - it may print `ASI <key>=<value>` lines for opaque structured learnings.
|
|
12
|
+
*
|
|
13
|
+
* Parsing tolerates interleaved noise: only well-formed lines are recognized,
|
|
14
|
+
* malformed lines are ignored (never thrown), and prototype-polluting key
|
|
15
|
+
* names are dropped.
|
|
16
|
+
*/
|
|
17
|
+
import type { ASIData, MetricDirection, NumericMetricMap } from "./types";
|
|
18
|
+
export declare const HARNESS_FILENAME = "autoresearch.sh";
|
|
19
|
+
export declare const DEFAULT_HARNESS_COMMAND = "bash autoresearch.sh";
|
|
20
|
+
export declare const METRIC_LINE_PREFIX = "METRIC";
|
|
21
|
+
export declare const ASI_LINE_PREFIX = "ASI";
|
|
22
|
+
export declare const EXPERIMENT_MAX_LINES = 10;
|
|
23
|
+
export declare const EXPERIMENT_MAX_BYTES: number;
|
|
24
|
+
/** All `METRIC name=value` lines found anywhere in the captured output. */
|
|
25
|
+
export declare function parseMetricLines(output: string): Map<string, number>;
|
|
26
|
+
/** All `ASI key=value` lines found anywhere in the captured output, or null when none parse. */
|
|
27
|
+
export declare function parseAsiLines(output: string): ASIData | null;
|
|
28
|
+
export interface ParsedHarnessOutput {
|
|
29
|
+
/** Every parsed metric, keyed by name. */
|
|
30
|
+
metrics: NumericMetricMap;
|
|
31
|
+
/**
|
|
32
|
+
* The surfaced primary metric: `metrics[primaryMetricName]` when a name is
|
|
33
|
+
* supplied, otherwise the value of the first `METRIC` line in the output.
|
|
34
|
+
*/
|
|
35
|
+
primary: number | null;
|
|
36
|
+
/** Parsed ASI learnings, or null when no ASI line parsed. */
|
|
37
|
+
asi: ASIData | null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Parse captured harness output into the primary metric, secondary values, and
|
|
41
|
+
* ASI learnings. Malformed or noisy lines are ignored, never thrown.
|
|
42
|
+
*/
|
|
43
|
+
export declare function parseHarnessOutput(output: string, primaryMetricName?: string): ParsedHarnessOutput;
|
|
44
|
+
export declare function mergeAsi(base: ASIData | null, override: ASIData | undefined): ASIData | undefined;
|
|
45
|
+
export declare function commas(value: number): string;
|
|
46
|
+
export declare function fmtNum(value: number, decimals?: number): string;
|
|
47
|
+
export declare function formatNum(value: number | null, unit: string): string;
|
|
48
|
+
export declare function formatElapsed(milliseconds: number): string;
|
|
49
|
+
/** True when `current` beats `best` under the given direction. */
|
|
50
|
+
export declare function isBetter(current: number, best: number, direction: MetricDirection): boolean;
|
|
51
|
+
export declare function inferMetricUnitFromName(name: string): string;
|
|
52
|
+
export declare function normalizePathSpec(value: string): string;
|
|
53
|
+
export declare function pathMatchesSpec(pathValue: string, specValue: string): boolean;
|
|
54
|
+
export declare function dedupeStrings(values: readonly string[]): string[];
|
|
55
|
+
export declare function ensureNumericMetricMap(value: NumericMetricMap | undefined): NumericMetricMap;
|
|
56
|
+
export declare function sanitizeAsi(value: {
|
|
57
|
+
[key: string]: unknown;
|
|
58
|
+
} | undefined): ASIData | undefined;
|