@gajae-code/coding-agent 0.11.11 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +131 -4
- package/dist/types/async/job-manager.d.ts +17 -3
- package/dist/types/cli/read-cli.d.ts +1 -0
- package/dist/types/commands/deep-interview.d.ts +9 -0
- package/dist/types/commands/harness.d.ts +22 -0
- package/dist/types/commands/read.d.ts +6 -0
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/provider-auth-health.d.ts +14 -0
- package/dist/types/config/provider-ranking.d.ts +59 -0
- package/dist/types/config/settings-schema.d.ts +83 -1
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/exa/mcp-client.d.ts +10 -1
- package/dist/types/exec/bash-executor.d.ts +45 -12
- package/dist/types/extensibility/extensions/types.d.ts +38 -4
- package/dist/types/extensibility/gjc-plugins/index.d.ts +12 -2
- package/dist/types/extensibility/gjc-plugins/installer.d.ts +63 -7
- package/dist/types/extensibility/gjc-plugins/lifecycle-reconciliation.d.ts +55 -0
- package/dist/types/extensibility/gjc-plugins/lifecycle.d.ts +64 -0
- package/dist/types/extensibility/gjc-plugins/runtime-quarantine.d.ts +67 -0
- package/dist/types/extensibility/gjc-plugins/session-validation.d.ts +3 -1
- package/dist/types/extensibility/gjc-plugins/types.d.ts +124 -0
- package/dist/types/extensibility/shared-events.d.ts +6 -0
- package/dist/types/gjc-runtime/deep-interview-ambiguity.d.ts +5 -12
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +17 -32
- package/dist/types/gjc-runtime/deep-interview-stage.d.ts +37 -0
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +4 -135
- package/dist/types/gjc-runtime/gc-runtime.d.ts +1 -1
- package/dist/types/gjc-runtime/launch-tmux.d.ts +1 -0
- package/dist/types/gjc-runtime/memory-guard-owner-claims.d.ts +47 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +70 -1
- package/dist/types/gjc-runtime/state-writer.d.ts +5 -46
- package/dist/types/gjc-runtime/team-launch.d.ts +1 -1
- package/dist/types/gjc-runtime/team-runtime.d.ts +27 -1
- package/dist/types/gjc-runtime/team-store.d.ts +12 -0
- package/dist/types/gjc-runtime/team-worker-heartbeat.d.ts +43 -0
- package/dist/types/gjc-runtime/team-worker-memory-guard.d.ts +92 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +17 -0
- package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +8 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +31 -80
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +3 -5
- package/dist/types/internal-urls/local-root-gc.d.ts +2 -0
- package/dist/types/main.d.ts +1 -1
- package/dist/types/memories/index.d.ts +2 -0
- package/dist/types/modes/acp/acp-agent.d.ts +10 -19
- package/dist/types/modes/components/assistant-message.d.ts +3 -1
- package/dist/types/modes/components/gjc-bundle-settings.d.ts +34 -0
- package/dist/types/modes/components/irc-sidebar.d.ts +41 -0
- package/dist/types/modes/components/notifications-settings-editor.d.ts +1 -0
- package/dist/types/modes/components/read-tool-group.d.ts +4 -4
- package/dist/types/modes/components/session-observer-overlay.d.ts +22 -0
- package/dist/types/modes/components/settings-selector.d.ts +11 -0
- package/dist/types/modes/components/status-line/gh.d.ts +5 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -0
- package/dist/types/modes/components/transcript-viewer-overlay.d.ts +18 -1
- package/dist/types/modes/controllers/event-controller.d.ts +3 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/irc-observation-ledger.d.ts +17 -1
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +16 -0
- package/dist/types/reminders/star-reminder.d.ts +3 -13
- package/dist/types/runtime-mcp/manager.d.ts +1 -0
- package/dist/types/runtime-mcp/smithery-auth.d.ts +5 -0
- package/dist/types/sdk/acp/adapter.d.ts +3 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +5 -0
- package/dist/types/sdk/broker/transport.d.ts +2 -0
- package/dist/types/sdk/bus/attachment-registry.d.ts +1 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +4 -1
- package/dist/types/sdk/bus/config.d.ts +6 -2
- package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
- package/dist/types/sdk/bus/index.d.ts +44 -1
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +44 -0
- package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +4 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +2 -1
- package/dist/types/sdk/bus/reconciliation-store.d.ts +69 -0
- package/dist/types/sdk/bus/rich-draft.d.ts +1 -0
- package/dist/types/sdk/bus/rich-render.d.ts +2 -0
- package/dist/types/sdk/bus/telegram-adoption-intent.d.ts +165 -0
- package/dist/types/sdk/bus/telegram-cli.d.ts +12 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +11 -4
- package/dist/types/sdk/bus/telegram-daemon.d.ts +25 -1
- package/dist/types/sdk/bus/telegram-reference.d.ts +11 -1
- package/dist/types/sdk/bus/threaded-render.d.ts +3 -2
- package/dist/types/sdk/bus/topic-registry.d.ts +11 -1
- package/dist/types/sdk/host/control/operations.d.ts +1 -1
- package/dist/types/sdk/host/host.d.ts +2 -0
- package/dist/types/sdk/host/query/handlers.d.ts +5 -0
- package/dist/types/sdk/host/query/revision-store.d.ts +2 -0
- package/dist/types/sdk/host/reverse-leases.d.ts +1 -1
- package/dist/types/sdk/prompt-status.d.ts +23 -3
- package/dist/types/sdk/session.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +82 -12
- package/dist/types/session/artifacts.d.ts +8 -0
- package/dist/types/session/blob-store.d.ts +57 -4
- package/dist/types/session/internal/managed-session-storage.d.ts +8 -1
- package/dist/types/session/memory-guard-checkpoint-participant.d.ts +69 -0
- package/dist/types/session/session-manager.d.ts +48 -19
- package/dist/types/session/session-storage.d.ts +4 -0
- package/dist/types/session/streaming-output.d.ts +96 -12
- package/dist/types/setup/credential-import.d.ts +12 -0
- package/dist/types/skill-state/active-state.d.ts +12 -4
- package/dist/types/skill-state/workflow-hud.d.ts +9 -0
- package/dist/types/task/executor.d.ts +3 -2
- package/dist/types/task/index.d.ts +3 -1
- package/dist/types/task/provider-retry-status.d.ts +8 -0
- package/dist/types/task/receipt.d.ts +7 -1
- package/dist/types/task/types.d.ts +23 -0
- package/dist/types/tools/ask-answer-registry.d.ts +2 -2
- package/dist/types/tools/bash-interactive.d.ts +4 -1
- package/dist/types/tools/bash.d.ts +3 -1
- package/dist/types/tools/browser/launch.d.ts +7 -0
- package/dist/types/tools/fetch.d.ts +17 -1
- package/dist/types/tools/image-gen.d.ts +4 -0
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/tools/output-meta.d.ts +44 -3
- package/dist/types/tools/path-utils.d.ts +1 -0
- package/dist/types/tools/read-internals.d.ts +12 -0
- package/dist/types/tools/read.d.ts +45 -7
- package/dist/types/tools/resource-gc.d.ts +4 -0
- package/dist/types/tools/review.d.ts +5 -7
- package/dist/types/tools/subagent.d.ts +2 -0
- package/dist/types/tools/tool-result.d.ts +7 -1
- package/dist/types/utils/gh.d.ts +13 -0
- package/dist/types/utils/image-resize.d.ts +1 -0
- package/dist/types/web/search/providers/kimi.d.ts +2 -0
- package/dist/types/web/search/providers/searxng.d.ts +7 -0
- package/dist/types/web/search/providers/xai.d.ts +2 -0
- package/package.json +11 -13
- package/scripts/acp-conformance-agent.ts +218 -0
- package/scripts/benchmark-sticky-viewport-pr1.ts +239 -0
- package/scripts/capture-gjc-bundle-settings.ts +306 -0
- package/scripts/capture-sticky-viewport-showcase.ts +292 -0
- package/scripts/generate-sdk-operation-inventory.ts +5 -0
- package/scripts/resident-memory-bench.ts +1341 -0
- package/scripts/run-acp-conformance.ts +285 -0
- package/scripts/verify-gjc-sdk-canonicalization.ts +122 -18
- package/scripts/verify-sticky-viewport-showcase.ts +611 -0
- package/src/async/job-manager.ts +78 -20
- package/src/capability/index.ts +8 -0
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/plugin-cli.ts +176 -18
- package/src/cli/read-cli.ts +2 -1
- package/src/cli.ts +1 -0
- package/src/commands/deep-interview.ts +29 -9
- package/src/commands/harness.ts +35 -12
- package/src/commands/read.ts +11 -2
- package/src/commands/team.ts +20 -2
- package/src/config/file-lock.ts +11 -2
- package/src/config/model-registry.ts +21 -2
- package/src/config/provider-auth-health.ts +42 -0
- package/src/config/provider-ranking.ts +119 -0
- package/src/config/settings-schema.ts +74 -1
- package/src/config/settings.ts +246 -108
- package/src/coordinator-mcp/question-gate-codec.ts +1 -1
- package/src/coordinator-mcp/server.ts +5 -12
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +230 -82
- package/src/defaults/gjc/skills/ralplan/SKILL.md +77 -41
- package/src/defaults/gjc/skills/team/SKILL.md +3 -1
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +62 -39
- package/src/defaults/gjc/skills/ultragoal/validation-batch-contracts.md +12 -0
- package/src/defaults/gjc-defaults.ts +4 -4
- package/src/eval/py/executor.ts +7 -2
- package/src/exa/mcp-client.ts +12 -3
- package/src/exec/bash-executor.ts +144 -27
- package/src/extensibility/extensions/runner.ts +15 -0
- package/src/extensibility/extensions/types.ts +46 -5
- package/src/extensibility/gjc-plugins/constrained-hooks.ts +8 -2
- package/src/extensibility/gjc-plugins/index.ts +19 -2
- package/src/extensibility/gjc-plugins/installer.ts +189 -50
- package/src/extensibility/gjc-plugins/lifecycle-reconciliation.ts +199 -0
- package/src/extensibility/gjc-plugins/lifecycle.ts +626 -0
- package/src/extensibility/gjc-plugins/observability.ts +3 -3
- package/src/extensibility/gjc-plugins/prompt-appendix.ts +5 -1
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +12 -2
- package/src/extensibility/gjc-plugins/runtime-quarantine.ts +121 -0
- package/src/extensibility/gjc-plugins/schema.ts +84 -12
- package/src/extensibility/gjc-plugins/session-validation.ts +9 -3
- package/src/extensibility/gjc-plugins/types.ts +141 -0
- package/src/extensibility/gjc-plugins/validation.ts +9 -0
- package/src/extensibility/shared-events.ts +5 -0
- package/src/gjc-runtime/deep-interview-ambiguity.ts +47 -82
- package/src/gjc-runtime/deep-interview-recorder.ts +227 -310
- package/src/gjc-runtime/deep-interview-runtime.ts +109 -270
- package/src/gjc-runtime/deep-interview-stage.ts +1016 -0
- package/src/gjc-runtime/deep-interview-state.ts +12 -943
- package/src/gjc-runtime/gc-render.ts +1 -0
- package/src/gjc-runtime/gc-runtime.ts +11 -1
- package/src/gjc-runtime/launch-tmux.ts +25 -6
- package/src/gjc-runtime/managed-owner-admission.ts +1 -1
- package/src/gjc-runtime/memory-guard-owner-claims.ts +389 -0
- package/src/gjc-runtime/ralplan-runtime.ts +1149 -98
- package/src/gjc-runtime/state-runtime.ts +2 -1
- package/src/gjc-runtime/state-writer.ts +70 -222
- package/src/gjc-runtime/team-launch.ts +42 -5
- package/src/gjc-runtime/team-runtime.ts +1085 -40
- package/src/gjc-runtime/team-store.ts +32 -0
- package/src/gjc-runtime/team-worker-heartbeat.ts +149 -0
- package/src/gjc-runtime/team-worker-memory-guard.ts +363 -0
- package/src/gjc-runtime/team-workers.ts +1 -0
- package/src/gjc-runtime/tmux-owner-isolation.ts +28 -0
- package/src/gjc-runtime/tmux-sessions.ts +58 -13
- package/src/gjc-runtime/ultragoal-receipt-freshness.ts +20 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +414 -618
- package/src/gjc-runtime/workflow-manifest.generated.json +105 -449
- package/src/gjc-runtime/workflow-manifest.ts +38 -247
- package/src/hooks/native-skill-hook.ts +17 -4
- package/src/hooks/skill-state.ts +1 -23
- package/src/internal-urls/docs-index.generated.ts +21 -22
- package/src/internal-urls/local-root-gc.ts +127 -0
- package/src/main.ts +9 -5
- package/src/memories/index.ts +10 -0
- package/src/modes/DESIGN.md +98 -0
- package/src/modes/acp/acp-agent.ts +339 -191
- package/src/modes/acp/acp-event-mapper.ts +14 -7
- package/src/modes/acp/acp-mode.ts +39 -3
- package/src/modes/components/assistant-message.ts +123 -22
- package/src/modes/components/gjc-bundle-settings.ts +508 -0
- package/src/modes/components/irc-sidebar.ts +176 -62
- package/src/modes/components/model-selector.ts +61 -4
- package/src/modes/components/notifications-settings-editor.ts +12 -1
- package/src/modes/components/oauth-selector.ts +96 -28
- package/src/modes/components/read-tool-group.ts +82 -14
- package/src/modes/components/session-observer-overlay.ts +457 -83
- package/src/modes/components/settings-selector.ts +116 -23
- package/src/modes/components/status-line/gh.ts +16 -0
- package/src/modes/components/tool-execution.ts +138 -27
- package/src/modes/components/tool-status-header.ts +3 -14
- package/src/modes/components/transcript-viewer-overlay.ts +99 -24
- package/src/modes/controllers/command-controller.ts +4 -0
- package/src/modes/controllers/event-controller.ts +109 -27
- package/src/modes/controllers/extension-ui-controller.ts +26 -2
- package/src/modes/controllers/input-controller.ts +18 -3
- package/src/modes/controllers/selector-controller.ts +127 -36
- package/src/modes/interactive-mode.ts +62 -3
- package/src/modes/irc-observation-ledger.ts +71 -15
- package/src/modes/runtime-init.ts +22 -2
- package/src/modes/types.ts +2 -0
- package/src/modes/utils/ui-helpers.ts +94 -1
- package/src/prompts/agent-fragments/ralplan-persistence.md +4 -3
- package/src/prompts/agents/architect.md +9 -1
- package/src/prompts/agents/critic.md +9 -0
- package/src/prompts/system/eager-todo.md +8 -4
- package/src/prompts/system/system-prompt.md +11 -5
- package/src/prompts/tools/bash.md +5 -5
- package/src/prompts/tools/read.md +4 -2
- package/src/prompts/tools/search-tool-bm25.md +5 -0
- package/src/prompts/tools/task.md +1 -1
- package/src/reminders/star-reminder.ts +4 -42
- package/src/runtime-mcp/manager.ts +19 -4
- package/src/runtime-mcp/smithery-auth.ts +17 -2
- package/src/runtime-mcp/smithery-connect.ts +11 -1
- package/src/sdk/acp/adapter.ts +22 -6
- package/src/sdk/broker/lifecycle.ts +25 -6
- package/src/sdk/broker/transport.ts +35 -3
- package/src/sdk/bus/attachment-registry.ts +5 -1
- package/src/sdk/bus/chat-daemon-cli.ts +4 -39
- package/src/sdk/bus/chat-daemon-control.ts +6 -3
- package/src/sdk/bus/config.ts +18 -2
- package/src/sdk/bus/control-drain-lease.ts +104 -0
- package/src/sdk/bus/index.ts +984 -146
- package/src/sdk/bus/kind-aware-reconciliation.ts +317 -0
- package/src/sdk/bus/lifecycle-commands.ts +4 -1
- package/src/sdk/bus/lifecycle-control-runtime.ts +403 -88
- package/src/sdk/bus/prompt-reconciliation.ts +2 -1
- package/src/sdk/bus/reconciliation-store.ts +303 -0
- package/src/sdk/bus/rich-draft.ts +1 -1
- package/src/sdk/bus/rich-render.ts +2 -2
- package/src/sdk/bus/telegram-adoption-intent.ts +574 -0
- package/src/sdk/bus/telegram-cli.ts +33 -8
- package/src/sdk/bus/telegram-daemon-cli.ts +1 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +11 -4
- package/src/sdk/bus/telegram-daemon.ts +916 -23
- package/src/sdk/bus/telegram-reference.ts +40 -3
- package/src/sdk/bus/threaded-render.ts +48 -3
- package/src/sdk/bus/topic-registry.ts +20 -0
- package/src/sdk/host/control/dispatch.ts +5 -1
- package/src/sdk/host/control/operations.ts +1 -1
- package/src/sdk/host/host.ts +23 -1
- package/src/sdk/host/query/handlers.ts +55 -0
- package/src/sdk/host/query/revision-store.ts +36 -2
- package/src/sdk/host/reverse-leases.ts +44 -7
- package/src/sdk/mcp/server.ts +46 -18
- package/src/sdk/prompt-status.ts +16 -3
- package/src/sdk/protocol/operation-inventory.generated.json +51 -0
- package/src/sdk/protocol/operation-registry.ts +7 -2
- package/src/sdk/session.ts +101 -10
- package/src/session/agent-session.ts +1286 -220
- package/src/session/artifacts.ts +22 -0
- package/src/session/auth-broker-config.ts +10 -3
- package/src/session/blob-store.ts +915 -24
- package/src/session/internal/managed-session-scope.ts +44 -6
- package/src/session/internal/managed-session-storage.ts +48 -6
- package/src/session/memory-guard-checkpoint-participant.ts +131 -0
- package/src/session/session-manager.ts +2364 -438
- package/src/session/session-storage.ts +9 -1
- package/src/session/streaming-output.ts +500 -111
- package/src/setup/credential-import.ts +75 -13
- package/src/setup/provider-onboarding.ts +19 -4
- package/src/skill-state/active-state.ts +32 -10
- package/src/skill-state/workflow-hud.ts +42 -2
- package/src/skill-state/workflow-mutation-guard.ts +560 -8
- package/src/task/executor.ts +122 -39
- package/src/task/index.ts +23 -14
- package/src/task/provider-retry-status.ts +16 -0
- package/src/task/receipt.ts +23 -7
- package/src/task/render.ts +6 -2
- package/src/task/types.ts +108 -0
- package/src/tools/ask-answer-registry.ts +12 -6
- package/src/tools/ask.ts +169 -118
- package/src/tools/ast-edit.ts +1 -0
- package/src/tools/ast-grep.ts +1 -0
- package/src/tools/bash-interactive.ts +8 -3
- package/src/tools/bash.ts +503 -60
- package/src/tools/browser/launch.ts +41 -6
- package/src/tools/fetch.ts +114 -23
- package/src/tools/find.ts +1 -0
- package/src/tools/image-gen.ts +29 -5
- package/src/tools/index.ts +4 -0
- package/src/tools/output-meta.ts +281 -20
- package/src/tools/path-utils.ts +8 -3
- package/src/tools/read-internals.ts +44 -0
- package/src/tools/read.ts +1560 -310
- package/src/tools/resource-gc.ts +182 -18
- package/src/tools/review.ts +5 -7
- package/src/tools/search.ts +1 -0
- package/src/tools/subagent-render.ts +6 -2
- package/src/tools/subagent.ts +6 -0
- package/src/tools/telegram-send.ts +6 -3
- package/src/tools/tool-result.ts +14 -1
- package/src/utils/gh.ts +39 -0
- package/src/utils/image-resize.ts +3 -0
- package/src/web/search/providers/anthropic.ts +4 -4
- package/src/web/search/providers/codex.ts +1 -1
- package/src/web/search/providers/kimi.ts +15 -5
- package/src/web/search/providers/searxng.ts +47 -4
- package/src/web/search/providers/xai.ts +7 -2
- package/dist/types/gjc-runtime/deep-interview-draft.d.ts +0 -8
- package/dist/types/gjc-runtime/deep-interview-payload.d.ts +0 -41
- package/dist/types/gjc-runtime/deep-interview-repair.d.ts +0 -17
- package/dist/types/gjc-runtime/ultragoal-pipeline.d.ts +0 -23
- package/src/defaults/gjc/skills/ultragoal/pipeline-validation-contracts.md +0 -30
- package/src/gjc-runtime/deep-interview-draft.ts +0 -843
- package/src/gjc-runtime/deep-interview-payload.ts +0 -262
- package/src/gjc-runtime/deep-interview-repair.ts +0 -1674
- package/src/gjc-runtime/ultragoal-pipeline.ts +0 -306
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { syncSkillActiveState } from "../skill-state/active-state";
|
|
2
2
|
import { deriveDeepInterviewHud } from "../skill-state/workflow-hud";
|
|
3
|
+
import { WORKFLOW_STATE_VERSION } from "../skill-state/workflow-state-contract";
|
|
4
|
+
import {
|
|
5
|
+
clampReportedAmbiguity,
|
|
6
|
+
computeAmbiguityFloor,
|
|
7
|
+
disputeFactsFromRetractedRound,
|
|
8
|
+
} from "./deep-interview-ambiguity";
|
|
3
9
|
import {
|
|
4
10
|
answerHash,
|
|
5
|
-
applyDeepInterviewRoundResultV1,
|
|
6
11
|
assertDeepInterviewInputWithinLimit,
|
|
7
12
|
assertDeepInterviewIntentManifest,
|
|
8
13
|
assertDeepInterviewStructuredResponseWithinLimit,
|
|
@@ -11,10 +16,8 @@ import {
|
|
|
11
16
|
type DeepInterviewIntentItem,
|
|
12
17
|
type DeepInterviewIntentSubstitution,
|
|
13
18
|
type DeepInterviewRoundRecord,
|
|
14
|
-
type DeepInterviewRoundResultV1,
|
|
15
19
|
type DeepInterviewStateEnvelope,
|
|
16
20
|
type DeepInterviewTriggerMetadata,
|
|
17
|
-
deepInterviewAnswerIdentityEqual,
|
|
18
21
|
deriveRoundKey,
|
|
19
22
|
MAX_USER_RESPONSE_LENGTH,
|
|
20
23
|
normalizeDeepInterviewEnvelope,
|
|
@@ -22,7 +25,7 @@ import {
|
|
|
22
25
|
reviewDeepInterviewIntent,
|
|
23
26
|
} from "./deep-interview-state";
|
|
24
27
|
import { writeSessionActivityMarker } from "./session-resolution";
|
|
25
|
-
import { readExistingStateForMutation,
|
|
28
|
+
import { readExistingStateForMutation, writeGuardedWorkflowEnvelopeAtomic } from "./state-writer";
|
|
26
29
|
|
|
27
30
|
export * from "./deep-interview-ambiguity";
|
|
28
31
|
export * from "./deep-interview-state";
|
|
@@ -69,8 +72,6 @@ export interface DeepInterviewScoringInput {
|
|
|
69
72
|
scores: Record<string, number>;
|
|
70
73
|
ambiguity: number;
|
|
71
74
|
triggers?: DeepInterviewTriggerMetadata[];
|
|
72
|
-
/** Complete native round result for typed callers. */
|
|
73
|
-
roundResult?: DeepInterviewRoundResultV1;
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
export type AppendOrMergeAction = "created" | "noop" | "replaced";
|
|
@@ -101,21 +102,6 @@ export interface TransitionValidationResult {
|
|
|
101
102
|
// Pure helpers: records
|
|
102
103
|
// =============================================================================
|
|
103
104
|
|
|
104
|
-
/**
|
|
105
|
-
* `deepInterview.dimension` is agent-supplied free text on post-topology asks, but the persisted
|
|
106
|
-
* envelope only accepts canonical dimension ids. Fold obvious case/label variants down, and drop
|
|
107
|
-
* anything unrecognized rather than persisting a value the validator rejects.
|
|
108
|
-
*/
|
|
109
|
-
function canonicalDimension(value: string | undefined): string | undefined {
|
|
110
|
-
if (value === undefined) return undefined;
|
|
111
|
-
const normalized = value.trim().toLowerCase();
|
|
112
|
-
if (normalized === "topology") return "topology";
|
|
113
|
-
for (const dimension of ["goal", "constraints", "criteria", "context"]) {
|
|
114
|
-
if (normalized === dimension || normalized.startsWith(`${dimension} `)) return dimension;
|
|
115
|
-
}
|
|
116
|
-
return undefined;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
105
|
export function buildAnswerShell(
|
|
120
106
|
input: DeepInterviewAnswerInput,
|
|
121
107
|
now: string = new Date().toISOString(),
|
|
@@ -131,7 +117,7 @@ export function buildAnswerShell(
|
|
|
131
117
|
selected_options: input.selectedOptions,
|
|
132
118
|
custom_input: input.customInput,
|
|
133
119
|
component: input.component,
|
|
134
|
-
dimension:
|
|
120
|
+
dimension: input.dimension,
|
|
135
121
|
ambiguity_at_ask: input.ambiguity,
|
|
136
122
|
lifecycle: "answered",
|
|
137
123
|
answered_at: now,
|
|
@@ -139,9 +125,11 @@ export function buildAnswerShell(
|
|
|
139
125
|
}
|
|
140
126
|
|
|
141
127
|
/**
|
|
142
|
-
* Append-or-merge by `round_key`.
|
|
143
|
-
*
|
|
144
|
-
*
|
|
128
|
+
* Append-or-merge by `round_key`. Exactly one record per key:
|
|
129
|
+
* - no existing record -> append (`created`);
|
|
130
|
+
* - identical question_hash + answer_hash -> deterministic no-op (`noop`);
|
|
131
|
+
* - same key, different hashes -> deterministic replacement of the prior shell
|
|
132
|
+
* (`replaced`); the prior answer for that key is superseded and lifecycle resets.
|
|
145
133
|
*/
|
|
146
134
|
export function appendOrMergeRound(
|
|
147
135
|
rounds: readonly DeepInterviewRoundRecord[],
|
|
@@ -154,10 +142,9 @@ export function appendOrMergeRound(
|
|
|
154
142
|
return { rounds: next, action: "created", record: shell };
|
|
155
143
|
}
|
|
156
144
|
const existing = next[index];
|
|
157
|
-
if (
|
|
145
|
+
if (existing.question_hash === shell.question_hash && existing.answer_hash === shell.answer_hash) {
|
|
158
146
|
return { rounds: next, action: "noop", record: existing };
|
|
159
147
|
}
|
|
160
|
-
if (existing.lifecycle === "scored") throw new Error("DI_ANSWER_LIFECYCLE_CONFLICT");
|
|
161
148
|
next[index] = shell;
|
|
162
149
|
return { rounds: next, action: "replaced", record: shell };
|
|
163
150
|
}
|
|
@@ -237,10 +224,10 @@ export function validateDeepInterviewScoredTransition(
|
|
|
237
224
|
`active trigger ${trigger.kind} did not raise ambiguity (${prior.ambiguity} -> ${next.ambiguity})`,
|
|
238
225
|
);
|
|
239
226
|
}
|
|
240
|
-
//
|
|
241
|
-
//
|
|
242
|
-
const priorDim = prior.scores?.[trigger.dimension];
|
|
243
|
-
const nextDim = next.scores?.[trigger.dimension];
|
|
227
|
+
// Affected dimension must not improve. Prefer record scores, fall back to the trigger's
|
|
228
|
+
// own prior/new dimension scores; absent metrics cannot prove non-improvement.
|
|
229
|
+
const priorDim = prior.scores?.[trigger.dimension] ?? trigger.priorDimensionScore;
|
|
230
|
+
const nextDim = next.scores?.[trigger.dimension] ?? trigger.newDimensionScore;
|
|
244
231
|
if (typeof priorDim !== "number" || typeof nextDim !== "number") {
|
|
245
232
|
violations.push(
|
|
246
233
|
`active trigger ${trigger.kind} is missing dimension "${trigger.dimension}" scores to prove non-improvement`,
|
|
@@ -317,16 +304,19 @@ export function projectCompactState(value: unknown, options: { lastN?: number }
|
|
|
317
304
|
// Persistence wrappers (state-writer backed; runtime-owned)
|
|
318
305
|
// =============================================================================
|
|
319
306
|
|
|
320
|
-
interface RecorderMutationOptions {
|
|
321
|
-
sessionId?: string;
|
|
322
|
-
expectedRevision?: number;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
307
|
async function readEnvelope(statePath: string): Promise<DeepInterviewStateEnvelope> {
|
|
326
308
|
const read = await readExistingStateForMutation(statePath);
|
|
327
309
|
if (read.kind === "valid") return ensureDeepInterviewStateShape(read.value);
|
|
328
|
-
if (read.kind === "
|
|
329
|
-
|
|
310
|
+
if (read.kind === "corrupt") {
|
|
311
|
+
// Fail closed: never silently overwrite a corrupt/tampered state file. Callers
|
|
312
|
+
// (e.g. the ask tool) catch this and warn without mutating, preserving the file
|
|
313
|
+
// for recovery. Only a genuinely absent file is defaulted below.
|
|
314
|
+
throw new Error(
|
|
315
|
+
`deep-interview state at ${statePath} is corrupt or tampered (${read.error}); refusing to overwrite`,
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
// Absent: start from a defaulted shape.
|
|
319
|
+
return ensureDeepInterviewStateShape(undefined);
|
|
330
320
|
}
|
|
331
321
|
|
|
332
322
|
function existingStateRevision(value: unknown): number | undefined {
|
|
@@ -339,16 +329,49 @@ function interviewIdOf(envelope: DeepInterviewStateEnvelope): string | undefined
|
|
|
339
329
|
const inner = (envelope.state ?? {}) as Record<string, unknown>;
|
|
340
330
|
return typeof inner.interview_id === "string" ? inner.interview_id : undefined;
|
|
341
331
|
}
|
|
342
|
-
interface RecorderHudSyncSlot {
|
|
343
|
-
latestCommittedRevision: number;
|
|
344
|
-
tail: Promise<void>;
|
|
345
|
-
}
|
|
346
332
|
|
|
347
|
-
|
|
333
|
+
async function persistEnvelope(
|
|
334
|
+
cwd: string,
|
|
335
|
+
statePath: string,
|
|
336
|
+
envelope: DeepInterviewStateEnvelope,
|
|
337
|
+
sessionId: string | undefined,
|
|
338
|
+
command: string,
|
|
339
|
+
): Promise<void> {
|
|
340
|
+
if (!sessionId) throw new Error("deep-interview recorder requires a session id");
|
|
341
|
+
const now = new Date().toISOString();
|
|
342
|
+
const payload: Record<string, unknown> = { ...normalizeDeepInterviewEnvelope(envelope), updated_at: now };
|
|
343
|
+
// Guarantee RequiredOnWriteEnvelopeSchema fields for the fresh/absent fallback;
|
|
344
|
+
// existing real state already carries these and is preserved by the spread above.
|
|
345
|
+
payload.skill ??= "deep-interview";
|
|
346
|
+
payload.version ??= WORKFLOW_STATE_VERSION;
|
|
347
|
+
payload.active ??= true;
|
|
348
|
+
payload.current_phase ??= "interviewing";
|
|
349
|
+
const expectedRevision = existingStateRevision(envelope);
|
|
350
|
+
const writeResult = await writeGuardedWorkflowEnvelopeAtomic(statePath, payload, {
|
|
351
|
+
cwd,
|
|
352
|
+
policy: "source",
|
|
353
|
+
expectedRevision,
|
|
354
|
+
receipt: { cwd, skill: "deep-interview", owner: "gjc-runtime", command, sessionId, nowIso: now },
|
|
355
|
+
audit: { category: "state", verb: "write", owner: "gjc-runtime", skill: "deep-interview", sessionId },
|
|
356
|
+
});
|
|
357
|
+
// Reflect the freshly written revision back onto the in-memory envelope so a
|
|
358
|
+
// follow-up HUD sync derives its `sourceRevision` from the persisted revision
|
|
359
|
+
// (not the stale pre-write value), otherwise the active-state writer treats the
|
|
360
|
+
// newer HUD as stale and skips it (e.g. dropping the ambiguity chip after scoring).
|
|
361
|
+
if (writeResult.written && typeof expectedRevision === "number") {
|
|
362
|
+
(envelope as Record<string, unknown>).state_revision = expectedRevision + 1;
|
|
363
|
+
}
|
|
364
|
+
await writeSessionActivityMarker(cwd, sessionId, { writer: "deep-interview-recorder", path: statePath });
|
|
365
|
+
}
|
|
348
366
|
|
|
367
|
+
/**
|
|
368
|
+
* Best-effort active-state/HUD cache refresh for the deep-interview rail, derived
|
|
369
|
+
* from the complete normalized mode-state envelope. HUD is a cache; a failure here
|
|
370
|
+
* must never change durable record semantics.
|
|
371
|
+
*/
|
|
349
372
|
async function syncRecorderHud(
|
|
350
373
|
cwd: string,
|
|
351
|
-
envelope:
|
|
374
|
+
envelope: DeepInterviewStateEnvelope,
|
|
352
375
|
sessionId: string | undefined,
|
|
353
376
|
): Promise<void> {
|
|
354
377
|
const phase = typeof envelope.current_phase === "string" ? envelope.current_phase : "interviewing";
|
|
@@ -359,65 +382,26 @@ async function syncRecorderHud(
|
|
|
359
382
|
phase,
|
|
360
383
|
sessionId,
|
|
361
384
|
source: "gjc-runtime-deep-interview-recorder",
|
|
362
|
-
hud: deriveDeepInterviewHud(envelope, { phase }),
|
|
363
|
-
|
|
385
|
+
hud: deriveDeepInterviewHud(normalizeDeepInterviewEnvelope(envelope) as Record<string, unknown>, { phase }),
|
|
386
|
+
sourceRevision: (existingStateRevision(envelope) ?? 0) + 1,
|
|
364
387
|
});
|
|
365
388
|
}
|
|
366
389
|
|
|
367
|
-
|
|
390
|
+
/**
|
|
391
|
+
* Repair the cached HUD after a no-op append. A no-op writes no mode-state, so the
|
|
392
|
+
* HUD is derived from a fresh read of the current persisted state (never from the
|
|
393
|
+
* pre-noop in-memory envelope) to avoid overwriting newer active-state with stale values.
|
|
394
|
+
*/
|
|
395
|
+
async function repairRecorderHudFromPersisted(
|
|
368
396
|
cwd: string,
|
|
369
|
-
|
|
370
|
-
sessionId: string,
|
|
371
|
-
revision: number,
|
|
397
|
+
statePath: string,
|
|
398
|
+
sessionId: string | undefined,
|
|
372
399
|
): Promise<void> {
|
|
373
|
-
const key = `${cwd}\0${sessionId}`;
|
|
374
|
-
const slot = recorderHudSyncSlots.get(key) ?? { latestCommittedRevision: -1, tail: Promise.resolve() };
|
|
375
|
-
recorderHudSyncSlots.set(key, slot);
|
|
376
|
-
const sync = slot.tail
|
|
377
|
-
.catch(() => undefined)
|
|
378
|
-
.then(async () => {
|
|
379
|
-
if (revision <= slot.latestCommittedRevision) return;
|
|
380
|
-
await syncRecorderHud(cwd, { ...envelope, state_revision: revision }, sessionId);
|
|
381
|
-
slot.latestCommittedRevision = revision;
|
|
382
|
-
});
|
|
383
|
-
slot.tail = sync;
|
|
384
|
-
await sync;
|
|
385
|
-
}
|
|
386
|
-
export interface DeepInterviewPostCommitWarning {
|
|
387
|
-
code: "DI_POST_COMMIT_AUDIT_FAILED" | "DI_POST_COMMIT_ACTIVITY_FAILED" | "DI_POST_COMMIT_HUD_FAILED";
|
|
388
|
-
message: string;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
export async function runDeepInterviewPostCommitEffects(options: {
|
|
392
|
-
cwd: string;
|
|
393
|
-
statePath: string;
|
|
394
|
-
sessionId: string;
|
|
395
|
-
envelope: Record<string, unknown>;
|
|
396
|
-
revision: number;
|
|
397
|
-
writer: string;
|
|
398
|
-
auditWarnings?: readonly { code: "DI_POST_COMMIT_AUDIT_FAILED"; message: string }[];
|
|
399
|
-
}): Promise<DeepInterviewPostCommitWarning[]> {
|
|
400
|
-
const warnings: DeepInterviewPostCommitWarning[] = [...(options.auditWarnings ?? [])];
|
|
401
|
-
try {
|
|
402
|
-
await writeSessionActivityMarker(options.cwd, options.sessionId, {
|
|
403
|
-
writer: options.writer,
|
|
404
|
-
path: options.statePath,
|
|
405
|
-
});
|
|
406
|
-
} catch (error) {
|
|
407
|
-
warnings.push({
|
|
408
|
-
code: "DI_POST_COMMIT_ACTIVITY_FAILED",
|
|
409
|
-
message: error instanceof Error ? error.message : String(error),
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
400
|
try {
|
|
413
|
-
await
|
|
414
|
-
} catch
|
|
415
|
-
|
|
416
|
-
code: "DI_POST_COMMIT_HUD_FAILED",
|
|
417
|
-
message: error instanceof Error ? error.message : String(error),
|
|
418
|
-
});
|
|
401
|
+
await syncDeepInterviewRecorderHud(cwd, statePath, sessionId);
|
|
402
|
+
} catch {
|
|
403
|
+
// HUD sync is best-effort cache maintenance and must not change record semantics.
|
|
419
404
|
}
|
|
420
|
-
return warnings;
|
|
421
405
|
}
|
|
422
406
|
|
|
423
407
|
/** Refresh the best-effort HUD cache from persisted deep-interview state. */
|
|
@@ -428,129 +412,110 @@ export async function syncDeepInterviewRecorderHud(
|
|
|
428
412
|
): Promise<void> {
|
|
429
413
|
const read = await readExistingStateForMutation(statePath);
|
|
430
414
|
if (read.kind !== "valid") return;
|
|
431
|
-
|
|
432
|
-
const revision = existingStateRevision(read.value);
|
|
433
|
-
if (revision === undefined || !sessionId) return;
|
|
434
|
-
await syncRecorderHudAtCommittedRevision(cwd, envelope, sessionId, revision);
|
|
415
|
+
await syncRecorderHud(cwd, normalizeDeepInterviewEnvelope(read.value), sessionId);
|
|
435
416
|
}
|
|
436
417
|
|
|
437
418
|
/**
|
|
438
419
|
* Record an `answered` shell for one round (append-or-merge by durable key).
|
|
439
420
|
*
|
|
440
|
-
*
|
|
441
|
-
*
|
|
421
|
+
* Retraction dynamics: replacing an already-`scored` answer is a mechanical
|
|
422
|
+
* contradiction signal (the user pivoted). The facts that round established are
|
|
423
|
+
* marked disputed, which raises the deterministic ambiguity floor immediately —
|
|
424
|
+
* without waiting for the LLM scorer to self-report a trigger.
|
|
442
425
|
*/
|
|
443
426
|
export async function appendOrMergeDeepInterviewRound(
|
|
444
427
|
cwd: string,
|
|
445
428
|
statePath: string,
|
|
446
429
|
input: DeepInterviewAnswerInput,
|
|
447
|
-
options:
|
|
448
|
-
): Promise<{
|
|
449
|
-
action: AppendOrMergeAction;
|
|
450
|
-
record: DeepInterviewRoundRecord;
|
|
451
|
-
warnings: DeepInterviewPostCommitWarning[];
|
|
452
|
-
}> {
|
|
430
|
+
options: { sessionId?: string } = {},
|
|
431
|
+
): Promise<{ action: AppendOrMergeAction; record: DeepInterviewRoundRecord; disputedFactIds?: string[] }> {
|
|
453
432
|
assertDeepInterviewStructuredResponseWithinLimit(input);
|
|
454
433
|
if (input.customInput !== undefined)
|
|
455
434
|
assertDeepInterviewInputWithinLimit(input.customInput, MAX_USER_RESPONSE_LENGTH, "user_response");
|
|
456
|
-
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
const
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
435
|
+
const envelope = await readEnvelope(statePath);
|
|
436
|
+
const interviewId = input.interviewId ?? interviewIdOf(envelope);
|
|
437
|
+
const shell = buildAnswerShell({
|
|
438
|
+
...input,
|
|
439
|
+
interviewId,
|
|
440
|
+
customInput: input.intent_contract || input.intent_review ? undefined : input.customInput,
|
|
441
|
+
});
|
|
442
|
+
const rounds = readRounds(envelope);
|
|
443
|
+
const priorRecord = rounds.find(r => r.round_key === shell.round_key);
|
|
444
|
+
const result = appendOrMergeRound(rounds, shell);
|
|
445
|
+
const inner = envelope.state as Record<string, unknown>;
|
|
446
|
+
let intentStateChanged = false;
|
|
447
|
+
if (input.intent_contract) {
|
|
448
|
+
if (input.round !== 0 || input.component !== "review-topology" || input.dimension !== "topology")
|
|
449
|
+
throw new Error("intent contract requires Round 0 topology metadata");
|
|
450
|
+
const confirmed =
|
|
451
|
+
input.selectedOptions?.length === 1 &&
|
|
452
|
+
input.intent_contract.confirmation_options.includes(input.selectedOptions[0]);
|
|
453
|
+
if (confirmed) {
|
|
454
|
+
const contract = createDeepInterviewIntentManifest(input.intent_contract.items, {
|
|
455
|
+
round: 0,
|
|
456
|
+
answer_hash: shell.answer_hash,
|
|
457
|
+
});
|
|
458
|
+
const existingContract = inner.intent_contract;
|
|
459
|
+
if (existingContract !== undefined) {
|
|
460
|
+
assertDeepInterviewIntentManifest(existingContract);
|
|
461
|
+
if (
|
|
462
|
+
existingContract.digest !== contract.digest ||
|
|
463
|
+
existingContract.confirmation_answer_hash !== contract.confirmation_answer_hash
|
|
464
|
+
)
|
|
465
|
+
throw new Error("locked intent contract cannot be replaced");
|
|
466
|
+
} else {
|
|
467
|
+
inner.intent_contract = contract;
|
|
468
|
+
intentStateChanged = true;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
if (input.intent_review) {
|
|
473
|
+
if (input.round <= 0) throw new Error("intent review requires a post-Round-0 answer");
|
|
474
|
+
const locked = inner.intent_contract;
|
|
475
|
+
assertDeepInterviewIntentManifest(locked);
|
|
476
|
+
const approved =
|
|
477
|
+
input.selectedOptions?.length === 1 && input.intent_review.approval_options.includes(input.selectedOptions[0]);
|
|
478
|
+
inner.intent_review = reviewDeepInterviewIntent(locked, input.intent_review.observed_items, {
|
|
479
|
+
status: approved ? "approved" : "pending",
|
|
480
|
+
supporting_substitutions: input.intent_review.supporting_substitutions,
|
|
481
|
+
...(approved
|
|
482
|
+
? {
|
|
483
|
+
approval_round: input.round,
|
|
500
484
|
answer_hash: shell.answer_hash,
|
|
501
|
-
|
|
502
|
-
const existingContract = state.intent_contract;
|
|
503
|
-
if (existingContract !== undefined) {
|
|
504
|
-
assertDeepInterviewIntentManifest(existingContract);
|
|
505
|
-
if (
|
|
506
|
-
existingContract.digest !== contract.digest ||
|
|
507
|
-
existingContract.confirmation_answer_hash !== contract.confirmation_answer_hash
|
|
508
|
-
)
|
|
509
|
-
throw new Error("locked intent contract cannot be replaced");
|
|
510
|
-
} else {
|
|
511
|
-
state.intent_contract = contract;
|
|
512
|
-
intentStateChanged = true;
|
|
485
|
+
user_answer_evidence: `answer_hash:${shell.answer_hash}`,
|
|
513
486
|
}
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
487
|
+
: {}),
|
|
488
|
+
});
|
|
489
|
+
intentStateChanged = true;
|
|
490
|
+
}
|
|
491
|
+
if (result.action === "noop" && !intentStateChanged) {
|
|
492
|
+
await repairRecorderHudFromPersisted(cwd, statePath, options.sessionId);
|
|
493
|
+
return { action: result.action, record: result.record };
|
|
494
|
+
}
|
|
495
|
+
inner.rounds = result.rounds;
|
|
496
|
+
let disputedFactIds: string[] | undefined;
|
|
497
|
+
if (result.action === "replaced" && priorRecord?.lifecycle === "scored") {
|
|
498
|
+
const facts = Array.isArray(inner.established_facts) ? inner.established_facts : [];
|
|
499
|
+
const disputed = disputeFactsFromRetractedRound(facts, shell.round);
|
|
500
|
+
if (disputed.disputedIds.length > 0) {
|
|
501
|
+
inner.established_facts = disputed.facts;
|
|
502
|
+
disputedFactIds = disputed.disputedIds;
|
|
503
|
+
// Surface the rise right away: the retraction changed the evidence, so the
|
|
504
|
+
// gating score must reflect the new floor before the next scoring pass.
|
|
505
|
+
const breakdown = computeAmbiguityFloor(inner);
|
|
506
|
+
inner.ambiguity_floor = breakdown;
|
|
507
|
+
if (typeof inner.current_ambiguity === "number") {
|
|
508
|
+
inner.current_ambiguity = clampReportedAmbiguity(inner.current_ambiguity, breakdown.floor).effective;
|
|
535
509
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
statePath,
|
|
546
|
-
sessionId: options.sessionId,
|
|
547
|
-
envelope: committed.stamped,
|
|
548
|
-
revision: committed.revision,
|
|
549
|
-
writer: "deep-interview-recorder",
|
|
550
|
-
auditWarnings: committed.warnings,
|
|
551
|
-
})
|
|
552
|
-
: [];
|
|
553
|
-
return { action: result.action, record: result.record, warnings };
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
await persistEnvelope(cwd, statePath, envelope, options.sessionId, "gjc deep-interview record-answer");
|
|
513
|
+
try {
|
|
514
|
+
await syncRecorderHud(cwd, envelope, options.sessionId);
|
|
515
|
+
} catch {
|
|
516
|
+
// HUD sync is best-effort cache maintenance and must not change record semantics.
|
|
517
|
+
}
|
|
518
|
+
return { action: result.action, record: result.record, disputedFactIds };
|
|
554
519
|
}
|
|
555
520
|
|
|
556
521
|
/**
|
|
@@ -564,58 +529,21 @@ export async function appendOrMergeDeepInterviewRound(
|
|
|
564
529
|
* not finite, that comparison is treated as non-matching, so no prior is selected
|
|
565
530
|
* rather than risking a spurious comparison against an unrelated round.
|
|
566
531
|
*/
|
|
567
|
-
function
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
const
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
if (
|
|
581
|
-
!dimensions?.every(
|
|
582
|
-
dimension => typeof input.scores[dimension] === "number" && Number.isFinite(input.scores[dimension]),
|
|
583
|
-
)
|
|
584
|
-
) {
|
|
585
|
-
throw new Error(
|
|
586
|
-
"DI_LEGACY_SCORING_INPUT_INSUFFICIENT: provide roundResult with complete global and component scores",
|
|
587
|
-
);
|
|
588
|
-
}
|
|
589
|
-
const topology = state.topology;
|
|
590
|
-
const hasActiveComponents =
|
|
591
|
-
!!topology &&
|
|
592
|
-
typeof topology === "object" &&
|
|
593
|
-
!Array.isArray(topology) &&
|
|
594
|
-
(() => {
|
|
595
|
-
const topologyRecord = topology as Record<string, unknown>;
|
|
596
|
-
if (!Array.isArray(topologyRecord.components)) return false;
|
|
597
|
-
const components = topologyRecord.components as unknown[];
|
|
598
|
-
return components.some(
|
|
599
|
-
(component: unknown) =>
|
|
600
|
-
component &&
|
|
601
|
-
typeof component === "object" &&
|
|
602
|
-
!Array.isArray(component) &&
|
|
603
|
-
(component as Record<string, unknown>).active !== false &&
|
|
604
|
-
(component as Record<string, unknown>).status !== "deferred",
|
|
605
|
-
);
|
|
606
|
-
})();
|
|
607
|
-
if (hasActiveComponents) {
|
|
608
|
-
throw new Error(
|
|
609
|
-
"DI_LEGACY_SCORING_INPUT_INSUFFICIENT: provide roundResult with complete global and component scores",
|
|
610
|
-
);
|
|
532
|
+
function latestPriorScoredRound(
|
|
533
|
+
rounds: readonly DeepInterviewRoundRecord[],
|
|
534
|
+
currentKey: string,
|
|
535
|
+
currentRound: number,
|
|
536
|
+
): DeepInterviewRoundRecord | undefined {
|
|
537
|
+
if (!Number.isFinite(currentRound)) return undefined;
|
|
538
|
+
let prior: DeepInterviewRoundRecord | undefined;
|
|
539
|
+
for (const candidate of rounds) {
|
|
540
|
+
if (candidate.lifecycle !== "scored") continue;
|
|
541
|
+
if (candidate.round_key === currentKey) continue;
|
|
542
|
+
if (!Number.isFinite(candidate.round)) continue;
|
|
543
|
+
if (!(candidate.round < currentRound)) continue;
|
|
544
|
+
if (prior === undefined || candidate.round > prior.round) prior = candidate;
|
|
611
545
|
}
|
|
612
|
-
return
|
|
613
|
-
global_scores: Object.fromEntries(
|
|
614
|
-
dimensions.map(dimension => [dimension, input.scores[dimension]]),
|
|
615
|
-
) as DeepInterviewRoundResultV1["global_scores"],
|
|
616
|
-
component_scores: {},
|
|
617
|
-
...(input.triggers === undefined ? {} : { triggers: input.triggers }),
|
|
618
|
-
};
|
|
546
|
+
return prior;
|
|
619
547
|
}
|
|
620
548
|
|
|
621
549
|
/** Merge scoring output into the same round record, transitioning to `scored`. */
|
|
@@ -623,64 +551,53 @@ export async function enrichDeepInterviewRoundScoring(
|
|
|
623
551
|
cwd: string,
|
|
624
552
|
statePath: string,
|
|
625
553
|
input: DeepInterviewScoringInput,
|
|
626
|
-
options:
|
|
627
|
-
): Promise<{ record: DeepInterviewRoundRecord
|
|
554
|
+
options: { sessionId?: string } = {},
|
|
555
|
+
): Promise<{ record: DeepInterviewRoundRecord }> {
|
|
628
556
|
assertDeepInterviewStructuredResponseWithinLimit(input);
|
|
629
|
-
|
|
630
|
-
const
|
|
631
|
-
const
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
expectedRevision: options.expectedRevision ?? existingStateRevision(initial) ?? 0,
|
|
636
|
-
receipt: {
|
|
637
|
-
cwd,
|
|
638
|
-
skill: "deep-interview",
|
|
639
|
-
owner: "gjc-runtime",
|
|
640
|
-
command: "gjc deep-interview score-round",
|
|
641
|
-
sessionId: options.sessionId,
|
|
642
|
-
nowIso: now,
|
|
643
|
-
},
|
|
644
|
-
audit: {
|
|
645
|
-
category: "state",
|
|
646
|
-
verb: "write",
|
|
647
|
-
owner: "gjc-runtime",
|
|
648
|
-
skill: "deep-interview",
|
|
649
|
-
sessionId: options.sessionId,
|
|
650
|
-
},
|
|
651
|
-
transform: current => {
|
|
652
|
-
const envelope = ensureDeepInterviewStateShape(current);
|
|
653
|
-
const interviewId = input.interviewId ?? interviewIdOf(envelope);
|
|
654
|
-
const roundKey = deriveRoundKey(interviewId, input);
|
|
655
|
-
const outcome = applyDeepInterviewRoundResultV1(
|
|
656
|
-
envelope,
|
|
657
|
-
roundKey,
|
|
658
|
-
legacyScoringRoundResult(envelope, input),
|
|
659
|
-
now,
|
|
660
|
-
);
|
|
661
|
-
const next = ensureDeepInterviewStateShape(outcome.envelope);
|
|
662
|
-
record = readRounds(next).find(round => round.round_key === roundKey);
|
|
663
|
-
if (!record) throw new Error("DI_STATE_SCHEMA_INVALID");
|
|
664
|
-
if (outcome.kind === "noop") return { kind: "noop" as const };
|
|
665
|
-
return {
|
|
666
|
-
kind: "write" as const,
|
|
667
|
-
value: { ...next, schema_version: 1, updated_at: now } as Record<string, unknown>,
|
|
668
|
-
};
|
|
669
|
-
},
|
|
557
|
+
const envelope = await readEnvelope(statePath);
|
|
558
|
+
const interviewId = input.interviewId ?? interviewIdOf(envelope);
|
|
559
|
+
const rounds = readRounds(envelope);
|
|
560
|
+
const { rounds: enrichedRounds, record: reportedRecord } = enrichRoundWithScoring(rounds, {
|
|
561
|
+
...input,
|
|
562
|
+
interviewId,
|
|
670
563
|
});
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
564
|
+
// Deterministic floor (Ouroboros principle): the LLM-reported ambiguity is only a
|
|
565
|
+
// lower-bound input. Evidence persisted in state — unresolved disputed facts,
|
|
566
|
+
// unscored active components, auto-answer dilution — sets a code-computed floor
|
|
567
|
+
// the reported score can never fall under, so a pivot mechanically raises the
|
|
568
|
+
// gating score even when the scorer omits triggers.
|
|
569
|
+
const inner = envelope.state as Record<string, unknown>;
|
|
570
|
+
const breakdown = computeAmbiguityFloor({ ...inner, rounds: enrichedRounds });
|
|
571
|
+
const clampResult = clampReportedAmbiguity(input.ambiguity, breakdown.floor);
|
|
572
|
+
let record = reportedRecord;
|
|
573
|
+
let nextRounds = enrichedRounds;
|
|
574
|
+
if (clampResult.clamped) {
|
|
575
|
+
record = {
|
|
576
|
+
...reportedRecord,
|
|
577
|
+
ambiguity: clampResult.effective,
|
|
578
|
+
reported_ambiguity: input.ambiguity,
|
|
579
|
+
ambiguity_floor: breakdown.floor,
|
|
580
|
+
};
|
|
581
|
+
nextRounds = enrichedRounds.map(item => (item.round_key === record.round_key ? record : item));
|
|
582
|
+
}
|
|
583
|
+
// Fail closed: a scored transition that violates the bidirectional invariant
|
|
584
|
+
// (an active trigger that improves the affected dimension or fails to raise
|
|
585
|
+
// overall ambiguity, or a disputed/unresolved trigger lacking a rationale) must
|
|
586
|
+
// never be persisted — storing it lets the interview falsely converge. Validate
|
|
587
|
+
// the effective (floor-clamped) record against the most recent prior scored round.
|
|
588
|
+
const prior = latestPriorScoredRound(rounds, record.round_key, record.round);
|
|
589
|
+
const validation = validateDeepInterviewScoredTransition(prior, record);
|
|
590
|
+
if (!validation.ok) {
|
|
591
|
+
throw new Error(
|
|
592
|
+
`deep-interview scored transition for round ${record.round} is invalid and was refused: ${validation.violations.join("; ")}`,
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
inner.rounds = nextRounds;
|
|
596
|
+
inner.current_ambiguity = clampResult.effective;
|
|
597
|
+
inner.ambiguity_floor = breakdown;
|
|
598
|
+
await persistEnvelope(cwd, statePath, envelope, options.sessionId, "gjc deep-interview score-round");
|
|
599
|
+
await syncRecorderHud(cwd, envelope, options.sessionId);
|
|
600
|
+
return { record };
|
|
684
601
|
}
|
|
685
602
|
|
|
686
603
|
/** Compact projection so callers read a slice instead of the full transcript. */
|