@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
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-scoped durable store for kind-aware invocation reconciliation (#3032/#3035).
|
|
3
|
+
*
|
|
4
|
+
* Path is always a private sibling of the transcript, never under artifactsDir:
|
|
5
|
+
* <dirname(sessionFile)>/.sdk-reconciliation/<safeSessionId>.json
|
|
6
|
+
*
|
|
7
|
+
* Safe session ids only: /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/
|
|
8
|
+
* Atomic write: temp + fsync + rename + 0600. Corrupt → quarantine + empty.
|
|
9
|
+
* Non-terminal skill records settle to failed/process_restart on bootstrap; prompt
|
|
10
|
+
* records finalize their pending outcome or a prompt_failed fallback.
|
|
11
|
+
*/
|
|
12
|
+
import * as fs from "node:fs/promises";
|
|
13
|
+
import * as path from "node:path";
|
|
14
|
+
import type { PromptReconciliationStatus, SdkPromptTerminalOutcome } from "../prompt-status";
|
|
15
|
+
import type { PromptCorrelation } from "./prompt-reconciliation";
|
|
16
|
+
|
|
17
|
+
export const RECONCILIATION_STORE_VERSION = 1;
|
|
18
|
+
export const RECONCILIATION_SESSION_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
19
|
+
export const RECONCILIATION_DIR_NAME = ".sdk-reconciliation";
|
|
20
|
+
|
|
21
|
+
export type ReconciliationKind = "prompt" | "skill";
|
|
22
|
+
|
|
23
|
+
export interface DurableReconciliationRecord extends PromptCorrelation {
|
|
24
|
+
kind: ReconciliationKind;
|
|
25
|
+
clientRef?: string;
|
|
26
|
+
status: PromptReconciliationStatus;
|
|
27
|
+
error?: { code: string; message: string };
|
|
28
|
+
acceptedAt: number;
|
|
29
|
+
startedAt?: number;
|
|
30
|
+
terminalAt?: number;
|
|
31
|
+
outcome?: SdkPromptTerminalOutcome;
|
|
32
|
+
pendingOutcome?: SdkPromptTerminalOutcome;
|
|
33
|
+
/** Skill-only safe token; never skill args bodies. */
|
|
34
|
+
skillName?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ReconciliationStoreDocument {
|
|
38
|
+
version: typeof RECONCILIATION_STORE_VERSION;
|
|
39
|
+
sessionId: string;
|
|
40
|
+
records: DurableReconciliationRecord[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ReconciliationStoreFs {
|
|
44
|
+
mkdir(directory: string, options: { recursive: true; mode: number }): Promise<unknown>;
|
|
45
|
+
readFile(file: string, encoding: "utf8"): Promise<string>;
|
|
46
|
+
writeFile(file: string, data: string, options: { mode: number }): Promise<void>;
|
|
47
|
+
rename(from: string, to: string): Promise<void>;
|
|
48
|
+
unlink(file: string): Promise<void>;
|
|
49
|
+
open(
|
|
50
|
+
file: string,
|
|
51
|
+
flags: string,
|
|
52
|
+
): Promise<{
|
|
53
|
+
sync(): Promise<void>;
|
|
54
|
+
close(): Promise<void>;
|
|
55
|
+
writeFile(data: string, encoding: "utf8"): Promise<void>;
|
|
56
|
+
}>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const nodeFs: ReconciliationStoreFs = {
|
|
60
|
+
mkdir: fs.mkdir,
|
|
61
|
+
readFile: fs.readFile,
|
|
62
|
+
writeFile: fs.writeFile,
|
|
63
|
+
rename: fs.rename,
|
|
64
|
+
unlink: fs.unlink,
|
|
65
|
+
open: fs.open as ReconciliationStoreFs["open"],
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export function isSafeReconciliationSessionId(sessionId: string): boolean {
|
|
69
|
+
return RECONCILIATION_SESSION_ID_PATTERN.test(sessionId);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Derive private store path; throws if sessionId is unsafe (path escape). */
|
|
73
|
+
export function reconciliationStorePath(sessionFile: string, sessionId: string): string {
|
|
74
|
+
if (!isSafeReconciliationSessionId(sessionId))
|
|
75
|
+
throw Object.assign(new Error("Unsafe session id for reconciliation store path."), {
|
|
76
|
+
code: "invalid_input",
|
|
77
|
+
});
|
|
78
|
+
return path.join(path.dirname(sessionFile), RECONCILIATION_DIR_NAME, `${sessionId}.json`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
82
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Record-level validation: JSON-valid but malformed entries must be quarantined too. */
|
|
86
|
+
function isValidRecord(value: unknown): boolean {
|
|
87
|
+
if (!isRecord(value)) return false;
|
|
88
|
+
const { kind, commandId, turnId, status, acceptedAt, terminalAt, outcome, pendingOutcome } = value;
|
|
89
|
+
if (kind !== "prompt" && kind !== "skill") return false;
|
|
90
|
+
if (typeof commandId !== "string" || !commandId || typeof turnId !== "string" || !turnId) return false;
|
|
91
|
+
if (status !== "accepted" && status !== "in_flight" && status !== "terminal_ok" && status !== "failed") return false;
|
|
92
|
+
if (typeof acceptedAt !== "number" || !Number.isFinite(acceptedAt)) return false;
|
|
93
|
+
if (terminalAt !== undefined && (typeof terminalAt !== "number" || !Number.isFinite(terminalAt))) return false;
|
|
94
|
+
// Durable invariants: only prompts carry a pending claim, a finalized record has no
|
|
95
|
+
// pending claim left, and terminal/active status must agree with `terminalAt`.
|
|
96
|
+
if (pendingOutcome !== undefined && kind !== "prompt") return false;
|
|
97
|
+
if (pendingOutcome !== undefined && terminalAt !== undefined) return false;
|
|
98
|
+
const isTerminalStatus = status === "terminal_ok" || status === "failed";
|
|
99
|
+
if (isTerminalStatus !== (terminalAt !== undefined)) return false;
|
|
100
|
+
if (outcome !== undefined && !isTerminalStatus) return false;
|
|
101
|
+
if (
|
|
102
|
+
outcome !== undefined &&
|
|
103
|
+
((status === "terminal_ok" && (!isRecord(outcome) || outcome.kind !== "stopped")) ||
|
|
104
|
+
(status === "failed" && (!isRecord(outcome) || outcome.kind !== "failed")))
|
|
105
|
+
)
|
|
106
|
+
return false;
|
|
107
|
+
if (value.startedAt !== undefined && (typeof value.startedAt !== "number" || !Number.isFinite(value.startedAt)))
|
|
108
|
+
return false;
|
|
109
|
+
if (value.clientRef !== undefined && typeof value.clientRef !== "string") return false;
|
|
110
|
+
if (value.skillName !== undefined && typeof value.skillName !== "string") return false;
|
|
111
|
+
if (value.error !== undefined) {
|
|
112
|
+
if (!isRecord(value.error)) return false;
|
|
113
|
+
if (typeof value.error.code !== "string" || typeof value.error.message !== "string") return false;
|
|
114
|
+
}
|
|
115
|
+
return [outcome, pendingOutcome].every(candidate => {
|
|
116
|
+
if (candidate === undefined) return true;
|
|
117
|
+
if (!isRecord(candidate)) return false;
|
|
118
|
+
if (candidate.kind === "stopped")
|
|
119
|
+
return (
|
|
120
|
+
["end_turn", "max_tokens", "max_turn_requests", "refusal", "cancelled"].includes(
|
|
121
|
+
candidate.reason as string,
|
|
122
|
+
) && ["agent", "client_cancel"].includes(candidate.provenance as string)
|
|
123
|
+
);
|
|
124
|
+
return (
|
|
125
|
+
candidate.kind === "failed" &&
|
|
126
|
+
["prompt_failed", "prompt_deadline_exceeded"].includes(candidate.code as string) &&
|
|
127
|
+
typeof candidate.message === "string" &&
|
|
128
|
+
["agent_failed", "deadline"].includes(candidate.provenance as string)
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function parseDocument(raw: string, expectedSessionId: string): ReconciliationStoreDocument {
|
|
134
|
+
const value = JSON.parse(raw) as unknown;
|
|
135
|
+
if (!isRecord(value) || value.version !== RECONCILIATION_STORE_VERSION)
|
|
136
|
+
throw new Error("invalid reconciliation store version");
|
|
137
|
+
if (value.sessionId !== expectedSessionId) throw new Error("session id mismatch");
|
|
138
|
+
if (!Array.isArray(value.records)) throw new Error("invalid records");
|
|
139
|
+
if (!value.records.every(isValidRecord)) throw new Error("invalid reconciliation record");
|
|
140
|
+
return value as unknown as ReconciliationStoreDocument;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Settle non-terminal durable records after process death.
|
|
145
|
+
* Prompt records preserve a durable pending outcome; skills retain the existing
|
|
146
|
+
* reconciliation-incomplete result.
|
|
147
|
+
*/
|
|
148
|
+
export function settleProcessRestart(
|
|
149
|
+
records: DurableReconciliationRecord[],
|
|
150
|
+
now: number,
|
|
151
|
+
): DurableReconciliationRecord[] {
|
|
152
|
+
return records.map(record => {
|
|
153
|
+
if (record.terminalAt !== undefined) return record;
|
|
154
|
+
if (record.kind === "prompt") {
|
|
155
|
+
const outcome: SdkPromptTerminalOutcome = record.pendingOutcome ?? {
|
|
156
|
+
kind: "failed",
|
|
157
|
+
code: "prompt_failed",
|
|
158
|
+
message: "Prompt did not complete before process restart.",
|
|
159
|
+
provenance: "agent_failed",
|
|
160
|
+
};
|
|
161
|
+
return {
|
|
162
|
+
...record,
|
|
163
|
+
status: outcome.kind === "stopped" ? "terminal_ok" : "failed",
|
|
164
|
+
terminalAt: now,
|
|
165
|
+
outcome,
|
|
166
|
+
pendingOutcome: undefined,
|
|
167
|
+
...(outcome.kind === "failed" ? { error: { code: outcome.code, message: outcome.message } } : {}),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
...record,
|
|
172
|
+
status: "failed",
|
|
173
|
+
terminalAt: now,
|
|
174
|
+
error: { code: "process_restart", message: "Reconciliation incomplete after process restart." },
|
|
175
|
+
};
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface ReconciliationStore {
|
|
180
|
+
readonly path: string | null;
|
|
181
|
+
readonly sessionId: string;
|
|
182
|
+
/** Serialize mutations; reload not required for single-process host (in-memory + write). */
|
|
183
|
+
transact(mutator: (records: DurableReconciliationRecord[]) => DurableReconciliationRecord[]): Promise<void>;
|
|
184
|
+
load(): Promise<DurableReconciliationRecord[]>;
|
|
185
|
+
/** Snapshot currently held in memory after last load/transact. */
|
|
186
|
+
snapshot(): DurableReconciliationRecord[];
|
|
187
|
+
delete(): Promise<void>;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function createReconciliationStore(options: {
|
|
191
|
+
sessionFile: string | null | undefined;
|
|
192
|
+
sessionId: string;
|
|
193
|
+
fs?: ReconciliationStoreFs;
|
|
194
|
+
now?: () => number;
|
|
195
|
+
}): ReconciliationStore {
|
|
196
|
+
const fileFs = options.fs ?? nodeFs;
|
|
197
|
+
const now = options.now ?? Date.now;
|
|
198
|
+
const sessionId = options.sessionId;
|
|
199
|
+
const filePath =
|
|
200
|
+
options.sessionFile && isSafeReconciliationSessionId(sessionId)
|
|
201
|
+
? reconciliationStorePath(options.sessionFile, sessionId)
|
|
202
|
+
: null;
|
|
203
|
+
|
|
204
|
+
let memory: DurableReconciliationRecord[] = [];
|
|
205
|
+
let chain: Promise<void> = Promise.resolve();
|
|
206
|
+
|
|
207
|
+
const writeAtomic = async (document: ReconciliationStoreDocument): Promise<void> => {
|
|
208
|
+
if (!filePath) return;
|
|
209
|
+
const directory = path.dirname(filePath);
|
|
210
|
+
await fileFs.mkdir(directory, { recursive: true, mode: 0o700 });
|
|
211
|
+
const temporary = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
212
|
+
try {
|
|
213
|
+
await fileFs.writeFile(temporary, `${JSON.stringify(document)}\n`, { mode: 0o600 });
|
|
214
|
+
try {
|
|
215
|
+
const handle = await fileFs.open(temporary, "r+");
|
|
216
|
+
try {
|
|
217
|
+
await handle.sync();
|
|
218
|
+
} finally {
|
|
219
|
+
await handle.close();
|
|
220
|
+
}
|
|
221
|
+
} catch {
|
|
222
|
+
// fsync optional on some fs seams
|
|
223
|
+
}
|
|
224
|
+
await fileFs.rename(temporary, filePath);
|
|
225
|
+
} catch (error) {
|
|
226
|
+
await fileFs.unlink(temporary).catch(() => {});
|
|
227
|
+
throw Object.assign(error instanceof Error ? error : new Error("reconciliation persist failed"), {
|
|
228
|
+
code: "reconciliation_persist_failed",
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const load = async (): Promise<DurableReconciliationRecord[]> => {
|
|
234
|
+
if (!filePath) {
|
|
235
|
+
memory = [];
|
|
236
|
+
return memory;
|
|
237
|
+
}
|
|
238
|
+
let raw: string;
|
|
239
|
+
try {
|
|
240
|
+
raw = await fileFs.readFile(filePath, "utf8");
|
|
241
|
+
} catch (error) {
|
|
242
|
+
// Only a missing file is an empty store. Permission/IO failures must propagate
|
|
243
|
+
// so the endpoint never becomes ready as if no prompt had been accepted.
|
|
244
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
245
|
+
memory = [];
|
|
246
|
+
return memory;
|
|
247
|
+
}
|
|
248
|
+
let document: ReconciliationStoreDocument;
|
|
249
|
+
try {
|
|
250
|
+
document = parseDocument(raw, sessionId);
|
|
251
|
+
} catch {
|
|
252
|
+
// Corrupt → quarantine
|
|
253
|
+
try {
|
|
254
|
+
await fileFs.rename(filePath, `${filePath}.corrupt.${now()}`);
|
|
255
|
+
} catch {
|
|
256
|
+
// ignore
|
|
257
|
+
}
|
|
258
|
+
memory = [];
|
|
259
|
+
return memory;
|
|
260
|
+
}
|
|
261
|
+
const settled = settleProcessRestart(document.records, now());
|
|
262
|
+
// Restart settlement must be durable before it is observable: a failed rewrite
|
|
263
|
+
// propagates so the endpoint stays unready instead of serving empty state as if
|
|
264
|
+
// no prompt had ever been accepted.
|
|
265
|
+
if (settled.some((record, index) => record !== document.records[index]))
|
|
266
|
+
await writeAtomic({ version: RECONCILIATION_STORE_VERSION, sessionId, records: settled });
|
|
267
|
+
memory = settled;
|
|
268
|
+
return memory;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const transact = async (
|
|
272
|
+
mutator: (records: DurableReconciliationRecord[]) => DurableReconciliationRecord[],
|
|
273
|
+
): Promise<void> => {
|
|
274
|
+
const run = async () => {
|
|
275
|
+
const next = mutator(memory.map(r => ({ ...r })));
|
|
276
|
+
await writeAtomic({ version: RECONCILIATION_STORE_VERSION, sessionId, records: next });
|
|
277
|
+
memory = next;
|
|
278
|
+
};
|
|
279
|
+
const pending = chain.then(run, run);
|
|
280
|
+
chain = pending.then(
|
|
281
|
+
() => undefined,
|
|
282
|
+
() => undefined,
|
|
283
|
+
);
|
|
284
|
+
await pending;
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const deleteStore = async (): Promise<void> => {
|
|
288
|
+
memory = [];
|
|
289
|
+
if (!filePath) return;
|
|
290
|
+
await fileFs.unlink(filePath).catch(error => {
|
|
291
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
292
|
+
});
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
return {
|
|
296
|
+
path: filePath,
|
|
297
|
+
sessionId,
|
|
298
|
+
transact,
|
|
299
|
+
load,
|
|
300
|
+
snapshot: () => memory.map(r => ({ ...r })),
|
|
301
|
+
delete: deleteStore,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
@@ -88,7 +88,7 @@ export class DraftStreamState {
|
|
|
88
88
|
*/
|
|
89
89
|
export async function deliverDraft(
|
|
90
90
|
botApi: BotApi,
|
|
91
|
-
base: { chat_id: string | number; message_thread_id?: number },
|
|
91
|
+
base: { chat_id: string | number; message_thread_id?: number; disable_notification?: true },
|
|
92
92
|
draftId: number,
|
|
93
93
|
raw: string,
|
|
94
94
|
log?: { warn(msg: string): void },
|
|
@@ -62,7 +62,7 @@ export function shouldPromoteRich(input: { enabled?: boolean; send: ThreadedSend
|
|
|
62
62
|
*/
|
|
63
63
|
export async function deliverRichWithFallback(
|
|
64
64
|
botApi: BotApi,
|
|
65
|
-
base: { chat_id: string | number; message_thread_id?: number },
|
|
65
|
+
base: { chat_id: string | number; message_thread_id?: number; disable_notification?: true },
|
|
66
66
|
send: ThreadedSend,
|
|
67
67
|
signal: AbortSignal,
|
|
68
68
|
fallbackDeliver: () => Promise<void>,
|
|
@@ -99,7 +99,7 @@ export async function deliverRichWithFallback(
|
|
|
99
99
|
*/
|
|
100
100
|
export async function deliverRichActionWithFallback(
|
|
101
101
|
botApi: BotApi,
|
|
102
|
-
base: { chat_id: string | number; message_thread_id?: number },
|
|
102
|
+
base: { chat_id: string | number; message_thread_id?: number; disable_notification?: true },
|
|
103
103
|
opts: { markdown: string; replyMarkup?: unknown },
|
|
104
104
|
signal: AbortSignal,
|
|
105
105
|
htmlFallback: () => Promise<number | undefined>,
|