botmux 2.93.1 → 2.95.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/dist/adapters/backend/tmux-backend.d.ts +18 -2
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +45 -3
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.js +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
- package/dist/adapters/backend/types.d.ts +8 -0
- package/dist/adapters/backend/types.d.ts.map +1 -1
- package/dist/adapters/backend/types.js.map +1 -1
- package/dist/adapters/backend/zellij-backend.js +1 -1
- package/dist/adapters/backend/zellij-backend.js.map +1 -1
- package/dist/adapters/cli/claude-code.d.ts.map +1 -1
- package/dist/adapters/cli/claude-code.js +6 -1
- package/dist/adapters/cli/claude-code.js.map +1 -1
- package/dist/bot-registry.d.ts +50 -1
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +30 -0
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli/workflow.d.ts.map +1 -1
- package/dist/cli/workflow.js +11 -4
- package/dist/cli/workflow.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +146 -1
- package/dist/cli.js.map +1 -1
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +28 -3
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +92 -0
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/inherit-peer.d.ts +5 -0
- package/dist/core/inherit-peer.d.ts.map +1 -1
- package/dist/core/inherit-peer.js +3 -0
- package/dist/core/inherit-peer.js.map +1 -1
- package/dist/core/session-discovery.d.ts +9 -0
- package/dist/core/session-discovery.d.ts.map +1 -1
- package/dist/core/session-discovery.js +24 -0
- package/dist/core/session-discovery.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +7 -6
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/terminal-url.d.ts.map +1 -1
- package/dist/core/terminal-url.js +15 -0
- package/dist/core/terminal-url.js.map +1 -1
- package/dist/core/trigger-session.js +2 -2
- package/dist/core/trigger-session.js.map +1 -1
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +30 -0
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +296 -23
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/aggregator.d.ts +5 -0
- package/dist/dashboard/aggregator.d.ts.map +1 -1
- package/dist/dashboard/aggregator.js +7 -0
- package/dist/dashboard/aggregator.js.map +1 -1
- package/dist/dashboard/auth.d.ts.map +1 -1
- package/dist/dashboard/auth.js +7 -1
- package/dist/dashboard/auth.js.map +1 -1
- package/dist/dashboard/bot-payload.d.ts +2 -0
- package/dist/dashboard/bot-payload.d.ts.map +1 -1
- package/dist/dashboard/bot-payload.js +2 -0
- package/dist/dashboard/bot-payload.js.map +1 -1
- package/dist/dashboard/connector-api.d.ts.map +1 -1
- package/dist/dashboard/connector-api.js +14 -3
- package/dist/dashboard/connector-api.js.map +1 -1
- package/dist/dashboard/settings-write-applier.d.ts +2 -1
- package/dist/dashboard/settings-write-applier.d.ts.map +1 -1
- package/dist/dashboard/settings-write-applier.js +7 -0
- package/dist/dashboard/settings-write-applier.js.map +1 -1
- package/dist/dashboard/v3-runs-api.d.ts +26 -0
- package/dist/dashboard/v3-runs-api.d.ts.map +1 -0
- package/dist/dashboard/v3-runs-api.js +67 -0
- package/dist/dashboard/v3-runs-api.js.map +1 -0
- package/dist/dashboard/web/app.js +21 -9
- package/dist/dashboard/web/app.js.map +1 -1
- package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults.js +128 -33
- package/dist/dashboard/web/bot-defaults.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +42 -6
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/legacy-workflow-link.d.ts +2 -0
- package/dist/dashboard/web/legacy-workflow-link.d.ts.map +1 -0
- package/dist/dashboard/web/legacy-workflow-link.js +12 -0
- package/dist/dashboard/web/legacy-workflow-link.js.map +1 -0
- package/dist/dashboard/web/settings.d.ts.map +1 -1
- package/dist/dashboard/web/settings.js +11 -0
- package/dist/dashboard/web/settings.js.map +1 -1
- package/dist/dashboard/web/v3-terminal.d.ts +11 -0
- package/dist/dashboard/web/v3-terminal.d.ts.map +1 -0
- package/dist/dashboard/web/v3-terminal.js +143 -0
- package/dist/dashboard/web/v3-terminal.js.map +1 -0
- package/dist/dashboard/web/v3.d.ts +2 -0
- package/dist/dashboard/web/v3.d.ts.map +1 -0
- package/dist/dashboard/web/v3.js +564 -0
- package/dist/dashboard/web/v3.js.map +1 -0
- package/dist/dashboard/web/whiteboards.d.ts.map +1 -1
- package/dist/dashboard/web/whiteboards.js +14 -5
- package/dist/dashboard/web/whiteboards.js.map +1 -1
- package/dist/dashboard/web/workflows.d.ts.map +1 -1
- package/dist/dashboard/web/workflows.js +12 -8
- package/dist/dashboard/web/workflows.js.map +1 -1
- package/dist/dashboard-web/app.js +834 -724
- package/dist/dashboard-web/apple-touch-icon.png +0 -0
- package/dist/dashboard-web/favicon.png +0 -0
- package/dist/dashboard-web/index.html +3 -1
- package/dist/dashboard-web/style.css +223 -0
- package/dist/dashboard.js +170 -10
- package/dist/dashboard.js.map +1 -1
- package/dist/global-config.d.ts +10 -0
- package/dist/global-config.d.ts.map +1 -1
- package/dist/global-config.js +8 -0
- package/dist/global-config.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +23 -3
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +23 -3
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts +12 -0
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +28 -0
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +3 -12
- package/dist/im/lark/client.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +2 -1
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +20 -6
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/grant-command.js +4 -4
- package/dist/im/lark/grant-command.js.map +1 -1
- package/dist/im/lark/message-parser.d.ts +29 -1
- package/dist/im/lark/message-parser.d.ts.map +1 -1
- package/dist/im/lark/message-parser.js +35 -1
- package/dist/im/lark/message-parser.js.map +1 -1
- package/dist/im/lark/v3-blocked-card-handler.d.ts +28 -0
- package/dist/im/lark/v3-blocked-card-handler.d.ts.map +1 -0
- package/dist/im/lark/v3-blocked-card-handler.js +134 -0
- package/dist/im/lark/v3-blocked-card-handler.js.map +1 -0
- package/dist/im/lark/v3-blocked-card.d.ts +75 -0
- package/dist/im/lark/v3-blocked-card.d.ts.map +1 -0
- package/dist/im/lark/v3-blocked-card.js +206 -0
- package/dist/im/lark/v3-blocked-card.js.map +1 -0
- package/dist/im/lark/v3-gate-card-handler.d.ts +31 -0
- package/dist/im/lark/v3-gate-card-handler.d.ts.map +1 -0
- package/dist/im/lark/v3-gate-card-handler.js +110 -0
- package/dist/im/lark/v3-gate-card-handler.js.map +1 -0
- package/dist/im/lark/v3-gate-card.d.ts +47 -0
- package/dist/im/lark/v3-gate-card.d.ts.map +1 -0
- package/dist/im/lark/v3-gate-card.js +119 -0
- package/dist/im/lark/v3-gate-card.js.map +1 -0
- package/dist/im/lark/v3-loop-grant-card-handler.d.ts +28 -0
- package/dist/im/lark/v3-loop-grant-card-handler.d.ts.map +1 -0
- package/dist/im/lark/v3-loop-grant-card-handler.js +90 -0
- package/dist/im/lark/v3-loop-grant-card-handler.js.map +1 -0
- package/dist/im/lark/v3-loop-grant-card.d.ts +46 -0
- package/dist/im/lark/v3-loop-grant-card.d.ts.map +1 -0
- package/dist/im/lark/v3-loop-grant-card.js +119 -0
- package/dist/im/lark/v3-loop-grant-card.js.map +1 -0
- package/dist/im/lark/v3-revisit-grant-card-handler.d.ts +29 -0
- package/dist/im/lark/v3-revisit-grant-card-handler.d.ts.map +1 -0
- package/dist/im/lark/v3-revisit-grant-card-handler.js +96 -0
- package/dist/im/lark/v3-revisit-grant-card-handler.js.map +1 -0
- package/dist/im/lark/v3-revisit-grant-card.d.ts +52 -0
- package/dist/im/lark/v3-revisit-grant-card.d.ts.map +1 -0
- package/dist/im/lark/v3-revisit-grant-card.js +123 -0
- package/dist/im/lark/v3-revisit-grant-card.js.map +1 -0
- package/dist/im/lark/workflow-cards.d.ts.map +1 -1
- package/dist/im/lark/workflow-cards.js +5 -1
- package/dist/im/lark/workflow-cards.js.map +1 -1
- package/dist/im/lark/workflow-progress-card.d.ts +1 -1
- package/dist/im/lark/workflow-progress-card.js +1 -1
- package/dist/im/lark/workflow-slash-command.d.ts +28 -0
- package/dist/im/lark/workflow-slash-command.d.ts.map +1 -1
- package/dist/im/lark/workflow-slash-command.js +61 -5
- package/dist/im/lark/workflow-slash-command.js.map +1 -1
- package/dist/platform/binding.d.ts +6 -0
- package/dist/platform/binding.d.ts.map +1 -1
- package/dist/platform/binding.js +17 -0
- package/dist/platform/binding.js.map +1 -1
- package/dist/platform/tunnel-client.d.ts +2 -0
- package/dist/platform/tunnel-client.d.ts.map +1 -1
- package/dist/platform/tunnel-client.js.map +1 -1
- package/dist/services/bot-config-store.d.ts.map +1 -1
- package/dist/services/bot-config-store.js +1 -0
- package/dist/services/bot-config-store.js.map +1 -1
- package/dist/services/card-prefs-store.d.ts +4 -0
- package/dist/services/card-prefs-store.d.ts.map +1 -1
- package/dist/services/card-prefs-store.js +19 -0
- package/dist/services/card-prefs-store.js.map +1 -1
- package/dist/services/oncall-store.d.ts +25 -0
- package/dist/services/oncall-store.d.ts.map +1 -1
- package/dist/services/oncall-store.js +56 -0
- package/dist/services/oncall-store.js.map +1 -1
- package/dist/services/transcript-resolver.d.ts.map +1 -1
- package/dist/services/transcript-resolver.js +21 -4
- package/dist/services/transcript-resolver.js.map +1 -1
- package/dist/setup/bot-config-editor.d.ts +7 -0
- package/dist/setup/bot-config-editor.d.ts.map +1 -1
- package/dist/setup/bot-config-editor.js +33 -0
- package/dist/setup/bot-config-editor.js.map +1 -1
- package/dist/skills/definitions.d.ts.map +1 -1
- package/dist/skills/definitions.js +208 -4
- package/dist/skills/definitions.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/terminal-favicon.d.ts +2 -0
- package/dist/utils/terminal-favicon.d.ts.map +1 -0
- package/dist/utils/terminal-favicon.js +5 -0
- package/dist/utils/terminal-favicon.js.map +1 -0
- package/dist/worker.js +152 -8
- package/dist/worker.js.map +1 -1
- package/dist/workflows/definition.d.ts +80 -80
- package/dist/workflows/events/payloads.d.ts +4 -4
- package/dist/workflows/events/schema.d.ts +16 -16
- package/dist/workflows/v3/architect.d.ts +37 -0
- package/dist/workflows/v3/architect.d.ts.map +1 -0
- package/dist/workflows/v3/architect.js +226 -0
- package/dist/workflows/v3/architect.js.map +1 -0
- package/dist/workflows/v3/bot-resolve.d.ts +21 -0
- package/dist/workflows/v3/bot-resolve.d.ts.map +1 -0
- package/dist/workflows/v3/bot-resolve.js +48 -0
- package/dist/workflows/v3/bot-resolve.js.map +1 -0
- package/dist/workflows/v3/cli-run.d.ts +24 -0
- package/dist/workflows/v3/cli-run.d.ts.map +1 -0
- package/dist/workflows/v3/cli-run.js +265 -0
- package/dist/workflows/v3/cli-run.js.map +1 -0
- package/dist/workflows/v3/contract.d.ts +302 -0
- package/dist/workflows/v3/contract.d.ts.map +1 -0
- package/dist/workflows/v3/contract.js +91 -0
- package/dist/workflows/v3/contract.js.map +1 -0
- package/dist/workflows/v3/daemon-run.d.ts +311 -0
- package/dist/workflows/v3/daemon-run.d.ts.map +1 -0
- package/dist/workflows/v3/daemon-run.js +775 -0
- package/dist/workflows/v3/daemon-run.js.map +1 -0
- package/dist/workflows/v3/dag.d.ts +332 -0
- package/dist/workflows/v3/dag.d.ts.map +1 -0
- package/dist/workflows/v3/dag.js +1040 -0
- package/dist/workflows/v3/dag.js.map +1 -0
- package/dist/workflows/v3/ephemeral-pool.d.ts +29 -0
- package/dist/workflows/v3/ephemeral-pool.d.ts.map +1 -0
- package/dist/workflows/v3/ephemeral-pool.js +331 -0
- package/dist/workflows/v3/ephemeral-pool.js.map +1 -0
- package/dist/workflows/v3/grill-state.d.ts +92 -0
- package/dist/workflows/v3/grill-state.d.ts.map +1 -0
- package/dist/workflows/v3/grill-state.js +151 -0
- package/dist/workflows/v3/grill-state.js.map +1 -0
- package/dist/workflows/v3/host.d.ts +88 -0
- package/dist/workflows/v3/host.d.ts.map +1 -0
- package/dist/workflows/v3/host.js +349 -0
- package/dist/workflows/v3/host.js.map +1 -0
- package/dist/workflows/v3/human-gate.d.ts +85 -0
- package/dist/workflows/v3/human-gate.d.ts.map +1 -0
- package/dist/workflows/v3/human-gate.js +153 -0
- package/dist/workflows/v3/human-gate.js.map +1 -0
- package/dist/workflows/v3/journal.d.ts +207 -0
- package/dist/workflows/v3/journal.d.ts.map +1 -0
- package/dist/workflows/v3/journal.js +74 -0
- package/dist/workflows/v3/journal.js.map +1 -0
- package/dist/workflows/v3/manifest.d.ts +17 -0
- package/dist/workflows/v3/manifest.d.ts.map +1 -0
- package/dist/workflows/v3/manifest.js +266 -0
- package/dist/workflows/v3/manifest.js.map +1 -0
- package/dist/workflows/v3/ops-projection.d.ts +110 -0
- package/dist/workflows/v3/ops-projection.d.ts.map +1 -0
- package/dist/workflows/v3/ops-projection.js +0 -0
- package/dist/workflows/v3/ops-projection.js.map +1 -0
- package/dist/workflows/v3/orchestrator.d.ts +168 -0
- package/dist/workflows/v3/orchestrator.d.ts.map +1 -0
- package/dist/workflows/v3/orchestrator.js +417 -0
- package/dist/workflows/v3/orchestrator.js.map +1 -0
- package/dist/workflows/v3/runtime.d.ts +193 -0
- package/dist/workflows/v3/runtime.d.ts.map +1 -0
- package/dist/workflows/v3/runtime.js +1284 -0
- package/dist/workflows/v3/runtime.js.map +1 -0
- package/dist/workflows/v3/spec.d.ts +32 -0
- package/dist/workflows/v3/spec.d.ts.map +1 -0
- package/dist/workflows/v3/spec.js +173 -0
- package/dist/workflows/v3/spec.js.map +1 -0
- package/dist/workflows/v3/state.d.ts +74 -0
- package/dist/workflows/v3/state.d.ts.map +1 -0
- package/dist/workflows/v3/state.js +353 -0
- package/dist/workflows/v3/state.js.map +1 -0
- package/package.json +2 -2
- package/dist/dashboard/web/workflow-catalog.d.ts +0 -2
- package/dist/dashboard/web/workflow-catalog.d.ts.map +0 -1
- package/dist/dashboard/web/workflow-catalog.js +0 -323
- package/dist/dashboard/web/workflow-catalog.js.map +0 -1
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v3 humanGate — file-backed approval waits.
|
|
3
|
+
*
|
|
4
|
+
* Codex's v1-review blocker #4: a workflow gate is RUNTIME state, not an
|
|
5
|
+
* in-memory chat ask. Its pending/resolved status MUST persist to the runDir
|
|
6
|
+
* so a daemon restart doesn't lose a pending approval. The journal already
|
|
7
|
+
* records `gateDispatched` / `gateResolved` (audit truth); this module owns the
|
|
8
|
+
* materialized, mutable wait files under `runDir/waits/<waitId>.json` — the
|
|
9
|
+
* active state the Lark card layer keys off and the restart-recovery scan reads.
|
|
10
|
+
*
|
|
11
|
+
* Split of concerns:
|
|
12
|
+
* - THIS file: the file-wait store + a gate resolver that persists
|
|
13
|
+
* pending → resolved around an injected decision source. Pure file IO,
|
|
14
|
+
* bot-agnostic, testable without the daemon.
|
|
15
|
+
* - daemon (later): supplies `awaitDecision` — posts the Lark approval card
|
|
16
|
+
* (reusing v0.2's card-builder / card-handler UX) and resolves when the
|
|
17
|
+
* button is clicked; on restart it re-arms pending waits via
|
|
18
|
+
* `listPendingWaits`.
|
|
19
|
+
*
|
|
20
|
+
* The wait shape mirrors v0.2's `waitKind: 'human-gate'` lineage but is
|
|
21
|
+
* deliberately scoped to v3's gate needs: no deadline, but options /
|
|
22
|
+
* approveOptions / approvers are persisted for crash-safe card recovery.
|
|
23
|
+
*/
|
|
24
|
+
import { type V3HumanGate } from './dag.js';
|
|
25
|
+
import type { V3RuntimeDeps } from './runtime.js';
|
|
26
|
+
export type GateWaitStatus = 'pending' | 'approved' | 'rejected';
|
|
27
|
+
export interface GateWait {
|
|
28
|
+
waitId: string;
|
|
29
|
+
nodeId: string;
|
|
30
|
+
/** The runtime instance this gate belongs to (`A#001`). A revisit makes a
|
|
31
|
+
* fresh instance + fresh gate; resolve-time validation rejects a stale card
|
|
32
|
+
* whose instance is no longer the node's effective one (code review). */
|
|
33
|
+
instanceId?: string;
|
|
34
|
+
prompt: string;
|
|
35
|
+
options: string[];
|
|
36
|
+
approveOptions: string[];
|
|
37
|
+
approvers: string[];
|
|
38
|
+
status: GateWaitStatus;
|
|
39
|
+
createdAt: number;
|
|
40
|
+
resolvedAt?: number;
|
|
41
|
+
/** open_id (or 'system') of the resolver, once resolved. */
|
|
42
|
+
by?: string;
|
|
43
|
+
/** The concrete option selected by the reviewer. */
|
|
44
|
+
selected?: string;
|
|
45
|
+
}
|
|
46
|
+
/** The concrete (non-optional) shape the runtime injects as `resolveGate`. */
|
|
47
|
+
export type GateResolver = NonNullable<V3RuntimeDeps['resolveGate']>;
|
|
48
|
+
export declare function waitsDir(runDir: string): string;
|
|
49
|
+
export declare function waitPath(runDir: string, waitId: string): string;
|
|
50
|
+
/** Write the initial `pending` wait file for a gate. Overwrites any stale
|
|
51
|
+
* file at the same waitId (a re-dispatched gate). */
|
|
52
|
+
export declare function writePendingWait(runDir: string, input: {
|
|
53
|
+
waitId: string;
|
|
54
|
+
nodeId: string;
|
|
55
|
+
prompt: string;
|
|
56
|
+
} & Partial<Pick<GateWait, 'options' | 'approveOptions' | 'approvers' | 'instanceId'>>): GateWait;
|
|
57
|
+
/** Read a single wait file, or `undefined` if it doesn't exist. */
|
|
58
|
+
export declare function readWait(runDir: string, waitId: string): GateWait | undefined;
|
|
59
|
+
/** Transition a wait to approved / rejected. Throws if the wait is missing
|
|
60
|
+
* (a resolution for an unknown gate is a programming error, not a no-op). */
|
|
61
|
+
export declare function resolveWait(runDir: string, waitId: string, resolution: 'approved' | 'rejected', by: string, selected?: string): GateWait;
|
|
62
|
+
export declare function normalizeGateWaitInput(gate: V3HumanGate): Pick<GateWait, 'prompt' | 'options' | 'approveOptions' | 'approvers'>;
|
|
63
|
+
export declare function selectedResolution(wait: Pick<GateWait, 'options' | 'approveOptions'>, selected: string): 'approved' | 'rejected' | undefined;
|
|
64
|
+
export declare function canResolveGateWait(wait: Pick<GateWait, 'approvers'>, by: string | undefined): boolean;
|
|
65
|
+
/** All still-pending waits in the runDir — the daemon's restart-recovery scan
|
|
66
|
+
* uses this to re-post / re-arm approval cards after a crash. */
|
|
67
|
+
export declare function listPendingWaits(runDir: string): GateWait[];
|
|
68
|
+
/**
|
|
69
|
+
* Build the `resolveGate` the runtime injects. Persists the wait as `pending`,
|
|
70
|
+
* delegates to the daemon-supplied `awaitDecision` (post card + await the
|
|
71
|
+
* click), then persists the resolution — so the file store is authoritative
|
|
72
|
+
* for pending/resolved regardless of whether the in-memory decision promise
|
|
73
|
+
* survives a restart (the daemon re-arms via `listPendingWaits`).
|
|
74
|
+
*
|
|
75
|
+
* `awaitDecision` is the only daemon-coupled seam; everything else here is file
|
|
76
|
+
* IO, which is why this factory is unit-testable with a fake decision source.
|
|
77
|
+
*/
|
|
78
|
+
export declare function createFileGate(deps: {
|
|
79
|
+
awaitDecision: (wait: GateWait) => Promise<{
|
|
80
|
+
resolution: 'approved' | 'rejected';
|
|
81
|
+
by: string;
|
|
82
|
+
selected?: string;
|
|
83
|
+
}>;
|
|
84
|
+
}): GateResolver;
|
|
85
|
+
//# sourceMappingURL=human-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"human-gate.d.ts","sourceRoot":"","sources":["../../../src/workflows/v3/human-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,EAA8B,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AACxE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;AAEjE,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf;;8EAE0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,8EAA8E;AAC9E,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC;AAIrE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE/D;AAWD;sDACsD;AACtD,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACvD,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,GAAG,gBAAgB,GAAG,WAAW,GAAG,YAAY,CAAC,CAAC,GACnF,QAAQ,CAgBV;AAED,mEAAmE;AACnE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAI7E;AAED;8EAC8E;AAC9E,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,GAAG,UAAU,EACnC,EAAE,EAAE,MAAM,EACV,QAAQ,CAAC,EAAE,MAAM,GAChB,QAAQ,CAMV;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAAG,gBAAgB,GAAG,WAAW,CAAC,CAQ/H;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,SAAS,GAAG,gBAAgB,CAAC,EAClD,QAAQ,EAAE,MAAM,GACf,UAAU,GAAG,UAAU,GAAG,SAAS,CAGrC;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,WAAW,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAErG;AAED;kEACkE;AAClE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,EAAE,CAc3D;AAsBD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE;IACnC,aAAa,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC;QAAE,UAAU,EAAE,UAAU,GAAG,UAAU,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpH,GAAG,YAAY,CAOf"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v3 humanGate — file-backed approval waits.
|
|
3
|
+
*
|
|
4
|
+
* Codex's v1-review blocker #4: a workflow gate is RUNTIME state, not an
|
|
5
|
+
* in-memory chat ask. Its pending/resolved status MUST persist to the runDir
|
|
6
|
+
* so a daemon restart doesn't lose a pending approval. The journal already
|
|
7
|
+
* records `gateDispatched` / `gateResolved` (audit truth); this module owns the
|
|
8
|
+
* materialized, mutable wait files under `runDir/waits/<waitId>.json` — the
|
|
9
|
+
* active state the Lark card layer keys off and the restart-recovery scan reads.
|
|
10
|
+
*
|
|
11
|
+
* Split of concerns:
|
|
12
|
+
* - THIS file: the file-wait store + a gate resolver that persists
|
|
13
|
+
* pending → resolved around an injected decision source. Pure file IO,
|
|
14
|
+
* bot-agnostic, testable without the daemon.
|
|
15
|
+
* - daemon (later): supplies `awaitDecision` — posts the Lark approval card
|
|
16
|
+
* (reusing v0.2's card-builder / card-handler UX) and resolves when the
|
|
17
|
+
* button is clicked; on restart it re-arms pending waits via
|
|
18
|
+
* `listPendingWaits`.
|
|
19
|
+
*
|
|
20
|
+
* The wait shape mirrors v0.2's `waitKind: 'human-gate'` lineage but is
|
|
21
|
+
* deliberately scoped to v3's gate needs: no deadline, but options /
|
|
22
|
+
* approveOptions / approvers are persisted for crash-safe card recovery.
|
|
23
|
+
*/
|
|
24
|
+
import { writeFileSync, readFileSync, existsSync, mkdirSync, renameSync, readdirSync } from 'node:fs';
|
|
25
|
+
import { join } from 'node:path';
|
|
26
|
+
import { DEFAULT_HUMAN_GATE_OPTIONS } from './dag.js';
|
|
27
|
+
// ─── File-wait store ────────────────────────────────────────────────────────
|
|
28
|
+
export function waitsDir(runDir) {
|
|
29
|
+
return join(runDir, 'waits');
|
|
30
|
+
}
|
|
31
|
+
export function waitPath(runDir, waitId) {
|
|
32
|
+
return join(waitsDir(runDir), `${waitId}.json`);
|
|
33
|
+
}
|
|
34
|
+
/** Atomic JSON write (tmp + rename) so a crash never leaves a torn wait file. */
|
|
35
|
+
function atomicWriteJson(path, value) {
|
|
36
|
+
const dir = join(path, '..');
|
|
37
|
+
if (!existsSync(dir))
|
|
38
|
+
mkdirSync(dir, { recursive: true });
|
|
39
|
+
const tmp = `${path}.tmp`;
|
|
40
|
+
writeFileSync(tmp, JSON.stringify(value, null, 2));
|
|
41
|
+
renameSync(tmp, path);
|
|
42
|
+
}
|
|
43
|
+
/** Write the initial `pending` wait file for a gate. Overwrites any stale
|
|
44
|
+
* file at the same waitId (a re-dispatched gate). */
|
|
45
|
+
export function writePendingWait(runDir, input) {
|
|
46
|
+
const options = input.options ?? [...DEFAULT_HUMAN_GATE_OPTIONS];
|
|
47
|
+
const approveOptions = input.approveOptions ?? (options.includes('approve') ? ['approve'] : [options[0]]);
|
|
48
|
+
const wait = {
|
|
49
|
+
waitId: input.waitId,
|
|
50
|
+
nodeId: input.nodeId,
|
|
51
|
+
...(input.instanceId ? { instanceId: input.instanceId } : {}),
|
|
52
|
+
prompt: input.prompt,
|
|
53
|
+
options,
|
|
54
|
+
approveOptions,
|
|
55
|
+
approvers: input.approvers ?? [],
|
|
56
|
+
status: 'pending',
|
|
57
|
+
createdAt: Date.now(),
|
|
58
|
+
};
|
|
59
|
+
atomicWriteJson(waitPath(runDir, input.waitId), wait);
|
|
60
|
+
return wait;
|
|
61
|
+
}
|
|
62
|
+
/** Read a single wait file, or `undefined` if it doesn't exist. */
|
|
63
|
+
export function readWait(runDir, waitId) {
|
|
64
|
+
const path = waitPath(runDir, waitId);
|
|
65
|
+
if (!existsSync(path))
|
|
66
|
+
return undefined;
|
|
67
|
+
return normalizeWaitFile(JSON.parse(readFileSync(path, 'utf-8')));
|
|
68
|
+
}
|
|
69
|
+
/** Transition a wait to approved / rejected. Throws if the wait is missing
|
|
70
|
+
* (a resolution for an unknown gate is a programming error, not a no-op). */
|
|
71
|
+
export function resolveWait(runDir, waitId, resolution, by, selected) {
|
|
72
|
+
const existing = readWait(runDir, waitId);
|
|
73
|
+
if (!existing)
|
|
74
|
+
throw new Error(`v3 human-gate: no pending wait "${waitId}" in ${runDir}`);
|
|
75
|
+
const resolved = { ...existing, status: resolution, resolvedAt: Date.now(), by, selected };
|
|
76
|
+
atomicWriteJson(waitPath(runDir, waitId), resolved);
|
|
77
|
+
return resolved;
|
|
78
|
+
}
|
|
79
|
+
export function normalizeGateWaitInput(gate) {
|
|
80
|
+
const options = gate.options ?? [...DEFAULT_HUMAN_GATE_OPTIONS];
|
|
81
|
+
return {
|
|
82
|
+
prompt: gate.prompt,
|
|
83
|
+
options,
|
|
84
|
+
approveOptions: gate.approveOptions ?? (options.includes('approve') ? ['approve'] : [options[0]]),
|
|
85
|
+
approvers: gate.approvers ?? [],
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
export function selectedResolution(wait, selected) {
|
|
89
|
+
if (!wait.options.includes(selected))
|
|
90
|
+
return undefined;
|
|
91
|
+
return wait.approveOptions.includes(selected) ? 'approved' : 'rejected';
|
|
92
|
+
}
|
|
93
|
+
export function canResolveGateWait(wait, by) {
|
|
94
|
+
return wait.approvers.length === 0 || (!!by && wait.approvers.includes(by));
|
|
95
|
+
}
|
|
96
|
+
/** All still-pending waits in the runDir — the daemon's restart-recovery scan
|
|
97
|
+
* uses this to re-post / re-arm approval cards after a crash. */
|
|
98
|
+
export function listPendingWaits(runDir) {
|
|
99
|
+
const dir = waitsDir(runDir);
|
|
100
|
+
if (!existsSync(dir))
|
|
101
|
+
return [];
|
|
102
|
+
const out = [];
|
|
103
|
+
for (const name of readdirSync(dir)) {
|
|
104
|
+
if (!name.endsWith('.json') || name.endsWith('.tmp'))
|
|
105
|
+
continue;
|
|
106
|
+
try {
|
|
107
|
+
const wait = normalizeWaitFile(JSON.parse(readFileSync(join(dir, name), 'utf-8')));
|
|
108
|
+
if (wait.status === 'pending')
|
|
109
|
+
out.push(wait);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// skip a torn / unparseable wait file (mid-write crash)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
function normalizeWaitFile(raw) {
|
|
118
|
+
const options = raw.options ?? [...DEFAULT_HUMAN_GATE_OPTIONS];
|
|
119
|
+
return {
|
|
120
|
+
waitId: raw.waitId ?? '',
|
|
121
|
+
nodeId: raw.nodeId ?? '',
|
|
122
|
+
...(raw.instanceId ? { instanceId: raw.instanceId } : {}),
|
|
123
|
+
prompt: raw.prompt ?? '',
|
|
124
|
+
options,
|
|
125
|
+
approveOptions: raw.approveOptions ?? (options.includes('approve') ? ['approve'] : [options[0]]),
|
|
126
|
+
approvers: raw.approvers ?? [],
|
|
127
|
+
status: raw.status ?? 'pending',
|
|
128
|
+
createdAt: raw.createdAt ?? 0,
|
|
129
|
+
resolvedAt: raw.resolvedAt,
|
|
130
|
+
by: raw.by,
|
|
131
|
+
selected: raw.selected,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
// ─── Gate resolver (injected into the runtime) ──────────────────────────────
|
|
135
|
+
/**
|
|
136
|
+
* Build the `resolveGate` the runtime injects. Persists the wait as `pending`,
|
|
137
|
+
* delegates to the daemon-supplied `awaitDecision` (post card + await the
|
|
138
|
+
* click), then persists the resolution — so the file store is authoritative
|
|
139
|
+
* for pending/resolved regardless of whether the in-memory decision promise
|
|
140
|
+
* survives a restart (the daemon re-arms via `listPendingWaits`).
|
|
141
|
+
*
|
|
142
|
+
* `awaitDecision` is the only daemon-coupled seam; everything else here is file
|
|
143
|
+
* IO, which is why this factory is unit-testable with a fake decision source.
|
|
144
|
+
*/
|
|
145
|
+
export function createFileGate(deps) {
|
|
146
|
+
return async ({ nodeId, prompt, waitId, runDir }) => {
|
|
147
|
+
const wait = writePendingWait(runDir, { waitId, nodeId, prompt });
|
|
148
|
+
const { resolution, by, selected } = await deps.awaitDecision(wait);
|
|
149
|
+
resolveWait(runDir, waitId, resolution, by, selected);
|
|
150
|
+
return { resolution, by, selected };
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=human-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"human-gate.js","sourceRoot":"","sources":["../../../src/workflows/v3/human-gate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v3 journal — append-only event stream (the run's audit truth).
|
|
3
|
+
*
|
|
4
|
+
* Codex's v1-review blocker #1: v3 is NOT a "only-mutable-STATE" recoverable
|
|
5
|
+
* system. `journal.ndjson` is the append-only source of audit truth (one JSON
|
|
6
|
+
* object per line, `ts`-stamped) from which `state.ts` materializes the STATE
|
|
7
|
+
* checkpoint. Concurrency / retry / gate / cancel / failure-root-cause all
|
|
8
|
+
* leave an ordered trail here.
|
|
9
|
+
*
|
|
10
|
+
* Append-only + line-oriented = crash-tolerant by construction: a torn final
|
|
11
|
+
* line (process died mid-write) is skipped on read; everything before it is
|
|
12
|
+
* intact. No locking needed for the journal itself — appends are serialized
|
|
13
|
+
* by the single runtime loop (the per-node LOCK guards worker dispatch, not
|
|
14
|
+
* the journal).
|
|
15
|
+
*/
|
|
16
|
+
import type { GoalAsk } from './contract.js';
|
|
17
|
+
/** Why a node failed — drives fail-fast root-cause reporting + retry policy
|
|
18
|
+
* (`classifyTerminal` maps each class to `blocked` or `failed`).
|
|
19
|
+
* `gateRejected` / `cancelled` are user-driven; the rest are faults. */
|
|
20
|
+
export type V3ErrorClass = 'workerError' | 'manifestInvalid' | 'resultInvalid' | 'timeout' | 'gateRejected' | 'cancelled';
|
|
21
|
+
export type V3RunFailureReason = 'allSinksSkipped';
|
|
22
|
+
/**
|
|
23
|
+
* The MVP event union. Static DAG + fail-fast, so the lifecycle is small:
|
|
24
|
+
* run boundaries, per-node dispatch/settle, and gate dispatch/resolve. Retry
|
|
25
|
+
* (`attempts/NNN`) is modeled by `attemptId` on dispatch/settle events — a new
|
|
26
|
+
* attempt is just another `nodeDispatched` with a fresh `attemptId`.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Structured loop reference carried on body-instance dispatches. The expanded
|
|
30
|
+
* nodeId (`repairLoop.i001.code`) is an OPAQUE key — projections/dashboards
|
|
31
|
+
* group by THIS field and never parse the id string (body ids may themselves
|
|
32
|
+
* contain dots).
|
|
33
|
+
*/
|
|
34
|
+
export interface V3LoopRef {
|
|
35
|
+
loopId: string;
|
|
36
|
+
iteration: number;
|
|
37
|
+
bodyNodeId: string;
|
|
38
|
+
}
|
|
39
|
+
export type V3Event = {
|
|
40
|
+
type: 'runStarted';
|
|
41
|
+
runId: string;
|
|
42
|
+
} | {
|
|
43
|
+
type: 'nodeDispatched';
|
|
44
|
+
nodeId: string;
|
|
45
|
+
instanceId?: string;
|
|
46
|
+
attemptId: string;
|
|
47
|
+
loop?: V3LoopRef;
|
|
48
|
+
} | {
|
|
49
|
+
type: 'nodeSessionReady';
|
|
50
|
+
nodeId: string;
|
|
51
|
+
instanceId?: string;
|
|
52
|
+
attemptId: string;
|
|
53
|
+
sessionInfo: {
|
|
54
|
+
sessionId: string;
|
|
55
|
+
webPort?: number;
|
|
56
|
+
};
|
|
57
|
+
ptyLogPath?: string;
|
|
58
|
+
} | {
|
|
59
|
+
type: 'nodeSucceeded';
|
|
60
|
+
nodeId: string;
|
|
61
|
+
instanceId?: string;
|
|
62
|
+
attemptId: string;
|
|
63
|
+
manifestPath: string;
|
|
64
|
+
} | {
|
|
65
|
+
type: 'nodeFailed';
|
|
66
|
+
nodeId: string;
|
|
67
|
+
instanceId?: string;
|
|
68
|
+
attemptId: string;
|
|
69
|
+
errorClass: V3ErrorClass;
|
|
70
|
+
errorCode?: string;
|
|
71
|
+
message?: string;
|
|
72
|
+
} | {
|
|
73
|
+
type: 'nodeBlocked';
|
|
74
|
+
nodeId: string;
|
|
75
|
+
instanceId?: string;
|
|
76
|
+
attemptId: string;
|
|
77
|
+
errorClass: V3ErrorClass;
|
|
78
|
+
errorCode?: string;
|
|
79
|
+
message?: string;
|
|
80
|
+
ask?: GoalAsk;
|
|
81
|
+
revisitTo?: string;
|
|
82
|
+
} | {
|
|
83
|
+
type: 'nodeRetryRequested';
|
|
84
|
+
nodeId: string;
|
|
85
|
+
instanceId?: string;
|
|
86
|
+
previousAttemptId: string;
|
|
87
|
+
nextAttemptId: string;
|
|
88
|
+
reason: 'blockedRetry';
|
|
89
|
+
previousErrorClass?: V3ErrorClass;
|
|
90
|
+
previousErrorCode?: string;
|
|
91
|
+
answer?: {
|
|
92
|
+
path: string;
|
|
93
|
+
preview: string;
|
|
94
|
+
by: string;
|
|
95
|
+
};
|
|
96
|
+
} | {
|
|
97
|
+
type: 'gateDispatched';
|
|
98
|
+
nodeId: string;
|
|
99
|
+
instanceId?: string;
|
|
100
|
+
waitId: string;
|
|
101
|
+
} | {
|
|
102
|
+
type: 'gateResolved';
|
|
103
|
+
nodeId: string;
|
|
104
|
+
instanceId?: string;
|
|
105
|
+
waitId: string;
|
|
106
|
+
resolution: 'approved' | 'rejected';
|
|
107
|
+
by: string;
|
|
108
|
+
selected?: string;
|
|
109
|
+
} | {
|
|
110
|
+
type: 'edgeResolved';
|
|
111
|
+
from: string;
|
|
112
|
+
to: string;
|
|
113
|
+
fromInstanceId?: string;
|
|
114
|
+
toInstanceId?: string;
|
|
115
|
+
sourceAttemptId: string;
|
|
116
|
+
active: boolean;
|
|
117
|
+
detail?: string;
|
|
118
|
+
} | {
|
|
119
|
+
type: 'nodeSkipped';
|
|
120
|
+
nodeId: string;
|
|
121
|
+
reason: 'triggerRuleUnsatisfied';
|
|
122
|
+
detail?: string;
|
|
123
|
+
} | {
|
|
124
|
+
type: 'nodeCancelled';
|
|
125
|
+
nodeId: string;
|
|
126
|
+
instanceId?: string;
|
|
127
|
+
attemptId?: string;
|
|
128
|
+
reason: 'earlyReleaseLoser';
|
|
129
|
+
byNodeId: string;
|
|
130
|
+
detail?: string;
|
|
131
|
+
} | {
|
|
132
|
+
type: 'nodeRevisitRequested';
|
|
133
|
+
nodeId: string;
|
|
134
|
+
instanceId: string;
|
|
135
|
+
attemptId: string;
|
|
136
|
+
toNodeId: string;
|
|
137
|
+
reason?: string;
|
|
138
|
+
reasonPath?: string;
|
|
139
|
+
sourceManifestPath?: string;
|
|
140
|
+
targetPreviousManifestPath?: string;
|
|
141
|
+
} | {
|
|
142
|
+
type: 'nodeInstanceSuperseded';
|
|
143
|
+
nodeId: string;
|
|
144
|
+
instanceId: string;
|
|
145
|
+
byNodeId: string;
|
|
146
|
+
reason: 'refresh';
|
|
147
|
+
} | {
|
|
148
|
+
type: 'revisitBudgetGranted';
|
|
149
|
+
sourceNodeId?: string;
|
|
150
|
+
toNodeId?: string;
|
|
151
|
+
by: string;
|
|
152
|
+
reason?: string;
|
|
153
|
+
} | {
|
|
154
|
+
type: 'loopStarted';
|
|
155
|
+
loopId: string;
|
|
156
|
+
} | {
|
|
157
|
+
type: 'loopIterationStarted';
|
|
158
|
+
loopId: string;
|
|
159
|
+
iteration: number;
|
|
160
|
+
} | {
|
|
161
|
+
type: 'loopIterationDecision';
|
|
162
|
+
loopId: string;
|
|
163
|
+
iteration: number;
|
|
164
|
+
decision: 'exit' | 'continue' | 'exhausted';
|
|
165
|
+
/** Human-readable why (e.g. `result.passed=false (2/3 iterations used)`). */
|
|
166
|
+
detail?: string;
|
|
167
|
+
} | {
|
|
168
|
+
type: 'loopIterationGranted';
|
|
169
|
+
loopId: string;
|
|
170
|
+
fromIteration: number;
|
|
171
|
+
by?: string;
|
|
172
|
+
} | {
|
|
173
|
+
type: 'runSucceeded';
|
|
174
|
+
} | {
|
|
175
|
+
type: 'runFailed';
|
|
176
|
+
failedNodeId?: string;
|
|
177
|
+
reason?: V3RunFailureReason;
|
|
178
|
+
detail?: string;
|
|
179
|
+
} | {
|
|
180
|
+
type: 'runBlocked';
|
|
181
|
+
blockedNodeId: string;
|
|
182
|
+
};
|
|
183
|
+
/** A journal line: the event flattened with its append timestamp (flattened —
|
|
184
|
+
* not `{ts, event}` — so `grep nodeFailed journal.ndjson` just works). */
|
|
185
|
+
export type StoredEvent = V3Event & {
|
|
186
|
+
ts: number;
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* Append one event as a single NDJSON line. Stamps `ts` (epoch ms) at write
|
|
190
|
+
* time. Creates the parent directory if missing so the very first
|
|
191
|
+
* `runStarted` doesn't require the caller to pre-create the runDir.
|
|
192
|
+
*
|
|
193
|
+
* Synchronous on purpose: the runtime loop must observe its own writes in
|
|
194
|
+
* order, and the journal is the linearization point — an async append would
|
|
195
|
+
* open a window where `decideNext` runs against stale state.
|
|
196
|
+
*/
|
|
197
|
+
export declare function appendEvent(journalPath: string, event: V3Event): StoredEvent;
|
|
198
|
+
/**
|
|
199
|
+
* Read every event in append order. Tolerates a torn FINAL line (crash
|
|
200
|
+
* mid-append) but throws on any earlier unparseable line — a mid-file parse
|
|
201
|
+
* failure is real corruption, and silently dropping a middle event would
|
|
202
|
+
* desync the replayed state (e.g. a lost `nodeSucceeded` leaves the node
|
|
203
|
+
* looking pending forever), so fail loud instead (codex hardening #11).
|
|
204
|
+
* Returns `[]` if the file does not exist yet (a run that never started).
|
|
205
|
+
*/
|
|
206
|
+
export declare function readJournal(journalPath: string): StoredEvent[];
|
|
207
|
+
//# sourceMappingURL=journal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../../../src/workflows/v3/journal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v3 journal — append-only event stream (the run's audit truth).
|
|
3
|
+
*
|
|
4
|
+
* Codex's v1-review blocker #1: v3 is NOT a "only-mutable-STATE" recoverable
|
|
5
|
+
* system. `journal.ndjson` is the append-only source of audit truth (one JSON
|
|
6
|
+
* object per line, `ts`-stamped) from which `state.ts` materializes the STATE
|
|
7
|
+
* checkpoint. Concurrency / retry / gate / cancel / failure-root-cause all
|
|
8
|
+
* leave an ordered trail here.
|
|
9
|
+
*
|
|
10
|
+
* Append-only + line-oriented = crash-tolerant by construction: a torn final
|
|
11
|
+
* line (process died mid-write) is skipped on read; everything before it is
|
|
12
|
+
* intact. No locking needed for the journal itself — appends are serialized
|
|
13
|
+
* by the single runtime loop (the per-node LOCK guards worker dispatch, not
|
|
14
|
+
* the journal).
|
|
15
|
+
*/
|
|
16
|
+
import { appendFileSync, readFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
17
|
+
import { dirname } from 'node:path';
|
|
18
|
+
// ─── Append ─────────────────────────────────────────────────────────────────
|
|
19
|
+
/**
|
|
20
|
+
* Append one event as a single NDJSON line. Stamps `ts` (epoch ms) at write
|
|
21
|
+
* time. Creates the parent directory if missing so the very first
|
|
22
|
+
* `runStarted` doesn't require the caller to pre-create the runDir.
|
|
23
|
+
*
|
|
24
|
+
* Synchronous on purpose: the runtime loop must observe its own writes in
|
|
25
|
+
* order, and the journal is the linearization point — an async append would
|
|
26
|
+
* open a window where `decideNext` runs against stale state.
|
|
27
|
+
*/
|
|
28
|
+
export function appendEvent(journalPath, event) {
|
|
29
|
+
const stored = { ts: Date.now(), ...event };
|
|
30
|
+
const dir = dirname(journalPath);
|
|
31
|
+
if (!existsSync(dir))
|
|
32
|
+
mkdirSync(dir, { recursive: true });
|
|
33
|
+
appendFileSync(journalPath, JSON.stringify(stored) + '\n');
|
|
34
|
+
return stored;
|
|
35
|
+
}
|
|
36
|
+
// ─── Read / replay ────────────────────────────────────────────────────────
|
|
37
|
+
/**
|
|
38
|
+
* Read every event in append order. Tolerates a torn FINAL line (crash
|
|
39
|
+
* mid-append) but throws on any earlier unparseable line — a mid-file parse
|
|
40
|
+
* failure is real corruption, and silently dropping a middle event would
|
|
41
|
+
* desync the replayed state (e.g. a lost `nodeSucceeded` leaves the node
|
|
42
|
+
* looking pending forever), so fail loud instead (codex hardening #11).
|
|
43
|
+
* Returns `[]` if the file does not exist yet (a run that never started).
|
|
44
|
+
*/
|
|
45
|
+
export function readJournal(journalPath) {
|
|
46
|
+
if (!existsSync(journalPath))
|
|
47
|
+
return [];
|
|
48
|
+
const raw = readFileSync(journalPath, 'utf-8');
|
|
49
|
+
const lines = raw.split('\n');
|
|
50
|
+
// Only the last non-empty line can legitimately be a half-written append.
|
|
51
|
+
let lastNonEmpty = -1;
|
|
52
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
53
|
+
if (lines[i].trim()) {
|
|
54
|
+
lastNonEmpty = i;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const out = [];
|
|
59
|
+
for (let i = 0; i < lines.length; i++) {
|
|
60
|
+
const trimmed = lines[i].trim();
|
|
61
|
+
if (!trimmed)
|
|
62
|
+
continue;
|
|
63
|
+
try {
|
|
64
|
+
out.push(JSON.parse(trimmed));
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
if (i === lastNonEmpty)
|
|
68
|
+
break; // torn final line from an interrupted append — tolerate
|
|
69
|
+
throw new Error(`v3 journal corrupted at line ${i + 1} of ${journalPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=journal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal.js","sourceRoot":"","sources":["../../../src/workflows/v3/journal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0NpC,+EAA+E;AAE/E;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,WAAmB,EAAE,KAAc;IAC7D,MAAM,MAAM,GAAgB,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,GAAG,KAAK,EAAE,CAAC;IACzD,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACjC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,cAAc,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;IAC3D,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,6EAA6E;AAE7E;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,WAAmB;IAC7C,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,EAAE,CAAC;IACxC,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC/C,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9B,0EAA0E;IAC1E,IAAI,YAAY,GAAG,CAAC,CAAC,CAAC;IACtB,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3C,IAAI,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,EAAE,CAAC;YAAC,YAAY,GAAG,CAAC,CAAC;YAAC,MAAM;QAAC,CAAC;IACpD,CAAC;IACD,MAAM,GAAG,GAAkB,EAAE,CAAC;IAC9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC;QACjC,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAgB,CAAC,CAAC;QAC/C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,KAAK,YAAY;gBAAE,MAAM,CAAC,wDAAwD;YACvF,MAAM,IAAI,KAAK,CACb,gCAAgC,CAAC,GAAG,CAAC,OAAO,WAAW,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAC/G,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v3 node manifest validator.
|
|
3
|
+
*
|
|
4
|
+
* Goal-mode workers write `manifest.json` at BOTMUX_GOAL_MANIFEST_PATH.
|
|
5
|
+
* The runtime treats that file as untrusted agent output: this module checks
|
|
6
|
+
* the shape, status invariants, path containment, file metadata, and hashes
|
|
7
|
+
* before downstream nodes receive any file paths.
|
|
8
|
+
*/
|
|
9
|
+
import { type Manifest } from './contract.js';
|
|
10
|
+
export declare class ManifestValidationError extends Error {
|
|
11
|
+
readonly problems: string[];
|
|
12
|
+
constructor(problems: string[]);
|
|
13
|
+
}
|
|
14
|
+
export declare function readAndValidateManifest(manifestPath: string, outputDir: string): Promise<Manifest>;
|
|
15
|
+
export declare function validateManifest(value: unknown, outputDir: string): Promise<Manifest>;
|
|
16
|
+
export declare function resolveManifestFilePath(outputDir: string, relativePath: string): string;
|
|
17
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/workflows/v3/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,EAML,KAAK,QAAQ,EAId,MAAM,eAAe,CAAC;AAEvB,qBAAa,uBAAwB,SAAQ,KAAK;aACpB,QAAQ,EAAE,MAAM,EAAE;gBAAlB,QAAQ,EAAE,MAAM,EAAE;CAI/C;AAED,wBAAsB,uBAAuB,CAC3C,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,QAAQ,CAAC,CAoBnB;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAkD3F;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEvF"}
|