@osolmaz/pi-workflows 0.16.4 → 0.16.6
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/README.md +5 -5
- package/dist/builtins/autoimplement.workflow.d.ts +5 -1
- package/dist/builtins/autoimplement.workflow.js +29 -10
- package/dist/builtins/autoimplement.workflow.js.map +1 -1
- package/dist/client/client.js +3 -1
- package/dist/client/client.js.map +1 -1
- package/dist/client/protocol.d.ts +1 -1
- package/dist/client/protocol.js +18 -1
- package/dist/client/protocol.js.map +1 -1
- package/dist/client/view.d.ts +7 -5
- package/dist/extension/index.d.ts +2 -1
- package/dist/extension/index.js +115 -165
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/recorder.d.ts +1 -1
- package/dist/extension/recorder.js +2 -2
- package/dist/extension/recorder.js.map +1 -1
- package/dist/extension/step-message.d.ts +1 -1
- package/dist/extension/step-message.js +4 -4
- package/dist/extension/step-message.js.map +1 -1
- package/dist/extension/workflow-message-coordinator.d.ts +12 -1
- package/dist/extension/workflow-message-coordinator.js +65 -54
- package/dist/extension/workflow-message-coordinator.js.map +1 -1
- package/dist/resource-managers/index.d.ts +1 -2
- package/dist/resource-managers/index.js +1 -2
- package/dist/resource-managers/index.js.map +1 -1
- package/dist/resource-managers/sqlite.d.ts +2 -295
- package/dist/resource-managers/sqlite.js +71 -1528
- package/dist/resource-managers/sqlite.js.map +1 -1
- package/dist/resource-managers/types.d.ts +2 -7
- package/dist/server/resource-runner-entry.js +2 -6
- package/dist/server/resource-runner-entry.js.map +1 -1
- package/dist/server/rpc-bridge.js +1 -1
- package/dist/server/rpc-bridge.js.map +1 -1
- package/dist/server/rpc-executor.js +2 -4
- package/dist/server/rpc-executor.js.map +1 -1
- package/dist/server/server-entry.js +3 -0
- package/dist/server/server-entry.js.map +1 -1
- package/dist/server/server.d.ts +22 -8
- package/dist/server/server.js +498 -543
- package/dist/server/server.js.map +1 -1
- package/dist/server/state.d.ts +16 -29
- package/dist/server/state.js +105 -319
- package/dist/server/state.js.map +1 -1
- package/dist/server/view.d.ts +4 -5
- package/dist/server/view.js +51 -51
- package/dist/server/view.js.map +1 -1
- package/dist/server/workflow-runner-entry.d.ts +1 -1
- package/dist/server/workflow-runner-entry.js +1 -49
- package/dist/server/workflow-runner-entry.js.map +1 -1
- package/dist/server/workflow-runner-protocol.d.ts +2 -7
- package/dist/server/workflow-runner-protocol.js +5 -10
- package/dist/server/workflow-runner-protocol.js.map +1 -1
- package/dist/server/workflow-runner-store.d.ts +5 -6
- package/dist/server/workflow-runner-store.js +7 -14
- package/dist/server/workflow-runner-store.js.map +1 -1
- package/dist/state/attempt-time.d.ts +21 -0
- package/dist/state/attempt-time.js +95 -0
- package/dist/state/attempt-time.js.map +1 -0
- package/dist/state/json.d.ts +2 -1
- package/dist/state/json.js.map +1 -1
- package/dist/state/project-store.d.ts +39 -0
- package/dist/state/project-store.js +154 -0
- package/dist/state/project-store.js.map +1 -0
- package/dist/state/prune.d.ts +28 -0
- package/dist/state/prune.js +278 -83
- package/dist/state/prune.js.map +1 -1
- package/dist/state/schema.js +23 -23
- package/dist/state/workflow-messages.d.ts +1 -1
- package/dist/state/workflow-messages.js +3 -1
- package/dist/state/workflow-messages.js.map +1 -1
- package/dist/viewer/backup.js +5 -0
- package/dist/viewer/backup.js.map +1 -1
- package/dist/workflows/diagnostics.d.ts +8 -0
- package/dist/workflows/diagnostics.js +61 -0
- package/dist/workflows/diagnostics.js.map +1 -0
- package/dist/workflows/engine.d.ts +2 -15
- package/dist/workflows/engine.js +125 -245
- package/dist/workflows/engine.js.map +1 -1
- package/dist/workflows/human-decision.d.ts +3 -7
- package/dist/workflows/human-decision.js +2 -56
- package/dist/workflows/human-decision.js.map +1 -1
- package/dist/workflows/index.d.ts +2 -1
- package/dist/workflows/index.js +1 -0
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/queue.d.ts +267 -0
- package/dist/workflows/queue.js +1248 -0
- package/dist/workflows/queue.js.map +1 -0
- package/dist/workflows/requests.d.ts +47 -0
- package/dist/workflows/requests.js +158 -0
- package/dist/workflows/requests.js.map +1 -0
- package/dist/workflows/schema.js +2 -4
- package/dist/workflows/schema.js.map +1 -1
- package/dist/workflows/store.d.ts +11 -16
- package/dist/workflows/store.js +174 -169
- package/dist/workflows/store.js.map +1 -1
- package/dist/workflows/tool-input.d.ts +6 -9
- package/dist/workflows/tool-input.js +15 -12
- package/dist/workflows/tool-input.js.map +1 -1
- package/dist/workflows/transitions.d.ts +38 -0
- package/dist/workflows/transitions.js +276 -0
- package/dist/workflows/transitions.js.map +1 -0
- package/dist/workflows/types.d.ts +9 -42
- package/dist/workflows/workflow-message-content.d.ts +7 -1
- package/dist/workflows/workflow-message-content.js +22 -4
- package/dist/workflows/workflow-message-content.js.map +1 -1
- package/docs/2026-08-25-workflow-follow-ups.md +5 -5
- package/docs/2026-09-02-unify-workflow-messages-plan.md +2 -0
- package/docs/2026-09-04-workflow-run-state-plan.md +2 -0
- package/docs/2026-09-06-durable-execution-plan.md +176 -0
- package/docs/DEFERRED_TURNS.md +84 -105
- package/docs/HUMAN_DECISIONS.md +17 -19
- package/docs/HUMAN_DECISION_PRESENTATIONS.md +1 -1
- package/docs/SQLITE_STATE.md +87 -30
- package/docs/WORKFLOW_SERVER.md +50 -11
- package/docs/WORKFLOW_STEP_MESSAGES.md +72 -54
- package/docs/plans/2026-08-04-controller-runtime-plan.md +2 -0
- package/docs/plans/2026-08-21-deferred-turn-intents-plan.md +2 -0
- package/docs/plans/2026-08-27-workflow-terminal-restart-plan.md +2 -0
- package/docs/plans/2026-09-04-automatic-state-retention-plan.md +283 -0
- package/docs/workflows.md +109 -44
- package/examples/workflows/autoresearch.workflow.ts +0 -2
- package/examples/workflows/branch.workflow.ts +0 -4
- package/examples/workflows/echo.workflow.ts +0 -2
- package/examples/workflows/live-settings.workflow.ts +0 -2
- package/examples/workflows/two-turn.workflow.ts +0 -2
- package/herdr-plugin.toml +1 -1
- package/package.json +1 -1
- package/protocol/fixtures/client-v1.json +1 -1
- package/skills/pi-workflows/SKILL.md +8 -7
- package/src/builtins/autoimplement.workflow.ts +31 -10
- package/src/client/client.ts +3 -1
- package/src/client/protocol.ts +21 -1
- package/src/client/view.ts +9 -5
- package/src/extension/index.ts +117 -189
- package/src/extension/recorder.ts +2 -2
- package/src/extension/step-message.ts +4 -4
- package/src/extension/workflow-message-coordinator.ts +78 -50
- package/src/resource-managers/index.ts +1 -9
- package/src/resource-managers/sqlite.ts +330 -2655
- package/src/resource-managers/types.ts +2 -3
- package/src/server/resource-runner-entry.ts +2 -7
- package/src/server/rpc-bridge.ts +1 -1
- package/src/server/rpc-executor.ts +4 -9
- package/src/server/server-entry.ts +3 -0
- package/src/server/server.ts +568 -644
- package/src/server/state.ts +122 -466
- package/src/server/view.ts +56 -60
- package/src/server/workflow-runner-entry.ts +2 -55
- package/src/server/workflow-runner-protocol.ts +5 -15
- package/src/server/workflow-runner-store.ts +12 -27
- package/src/state/attempt-time.ts +122 -0
- package/src/state/json.ts +2 -1
- package/src/state/project-store.ts +218 -0
- package/src/state/prune.ts +408 -95
- package/src/state/schema.ts +23 -23
- package/src/state/workflow-messages.ts +4 -2
- package/src/viewer/backup.ts +7 -0
- package/src/workflows/diagnostics.ts +78 -0
- package/src/workflows/engine.ts +133 -291
- package/src/workflows/human-decision.ts +3 -81
- package/src/workflows/index.ts +8 -2
- package/src/workflows/queue.ts +2091 -0
- package/src/workflows/requests.ts +283 -0
- package/src/workflows/schema.ts +2 -6
- package/src/workflows/store.ts +223 -231
- package/src/workflows/tool-input.ts +20 -12
- package/src/workflows/transitions.ts +330 -0
- package/src/workflows/types.ts +9 -46
- package/src/workflows/workflow-message-content.ts +28 -4
- package/dist/resource-managers/runtime.d.ts +0 -59
- package/dist/resource-managers/runtime.js +0 -423
- package/dist/resource-managers/runtime.js.map +0 -1
- package/src/resource-managers/runtime.ts +0 -576
package/src/server/state.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
type ClientRequest,
|
|
6
6
|
type ClientResponse,
|
|
7
7
|
} from "../client/protocol.js";
|
|
8
|
+
import { AttemptTime, type AttemptClock } from "../state/attempt-time.js";
|
|
8
9
|
import { StateDatabase } from "../state/database.js";
|
|
9
10
|
import { canonicalJson, type JsonValue } from "../state/json.js";
|
|
10
11
|
import { tokenHash } from "../state/mutation.js";
|
|
@@ -13,8 +14,15 @@ import {
|
|
|
13
14
|
workflowMessageIdFor,
|
|
14
15
|
type WorkflowStepReason,
|
|
15
16
|
} from "../state/workflow-messages.js";
|
|
17
|
+
import {
|
|
18
|
+
ensureWorkflowRequest,
|
|
19
|
+
readWorkflowRequest,
|
|
20
|
+
recordWorkflowSubmission,
|
|
21
|
+
type InteractiveRequestRecord,
|
|
22
|
+
} from "../workflows/requests.js";
|
|
16
23
|
import {
|
|
17
24
|
decisionWorkflowMessageContent,
|
|
25
|
+
checkpointWorkflowMessageContent,
|
|
18
26
|
stepWorkflowMessageContent,
|
|
19
27
|
} from "../workflows/workflow-message-content.js";
|
|
20
28
|
import type { WorkflowRunnerMessage, WorkflowRunnerResponse } from "./workflow-runner-protocol.js";
|
|
@@ -69,29 +77,16 @@ export type InteractiveSubmissionRecord = {
|
|
|
69
77
|
submittedAt: string;
|
|
70
78
|
};
|
|
71
79
|
|
|
72
|
-
export type InteractiveRequestRecord = {
|
|
73
|
-
requestId: string;
|
|
74
|
-
runId: string;
|
|
75
|
-
attemptId: string;
|
|
76
|
-
targetSessionId: string;
|
|
77
|
-
kind: "agent" | "assistant" | "decision";
|
|
78
|
-
contract: JsonValue;
|
|
79
|
-
revision: number;
|
|
80
|
-
status: "pending" | "settled" | "cancelled";
|
|
81
|
-
unproductiveTurnEnds: number;
|
|
82
|
-
acceptedSubmissionId: string | null;
|
|
83
|
-
createdAt: string;
|
|
84
|
-
updatedAt: string;
|
|
85
|
-
settledAt: string | null;
|
|
86
|
-
consumedAt: string | null;
|
|
87
|
-
};
|
|
88
|
-
|
|
89
80
|
export class ServerStateStore {
|
|
90
81
|
readonly state: StateDatabase;
|
|
91
82
|
private readonly ownsState: boolean;
|
|
92
83
|
readonly workflowMessages: WorkflowMessageStore;
|
|
84
|
+
private readonly attemptTime: AttemptTime;
|
|
93
85
|
|
|
94
|
-
constructor(
|
|
86
|
+
constructor(
|
|
87
|
+
databasePath: string,
|
|
88
|
+
options: { state?: StateDatabase; readOnly?: boolean; clock?: AttemptClock } = {},
|
|
89
|
+
) {
|
|
95
90
|
this.ownsState = options.state === undefined;
|
|
96
91
|
this.state =
|
|
97
92
|
options.state ??
|
|
@@ -100,6 +95,7 @@ export class ServerStateStore {
|
|
|
100
95
|
mode: options.readOnly === true ? "read-only" : "read-write",
|
|
101
96
|
});
|
|
102
97
|
this.workflowMessages = new WorkflowMessageStore(this.state);
|
|
98
|
+
this.attemptTime = new AttemptTime(this.state, options.clock);
|
|
103
99
|
}
|
|
104
100
|
|
|
105
101
|
close(): void {
|
|
@@ -443,54 +439,10 @@ export class ServerStateStore {
|
|
|
443
439
|
kind: InteractiveRequestRecord["kind"];
|
|
444
440
|
contract: JsonValue;
|
|
445
441
|
}): InteractiveRequestRecord {
|
|
446
|
-
const now = Date.now();
|
|
447
442
|
return this.state.transaction(() => {
|
|
448
|
-
const
|
|
449
|
-
if (
|
|
450
|
-
|
|
451
|
-
existing.runId !== options.runId ||
|
|
452
|
-
existing.attemptId !== options.attemptId ||
|
|
453
|
-
canonicalJson(existing.contract) !== canonicalJson(options.contract)
|
|
454
|
-
) {
|
|
455
|
-
throw new Error(`Interactive request conflicts: ${options.requestId}`);
|
|
456
|
-
}
|
|
457
|
-
if (existing.status === "settled" && existing.consumedAt === null) {
|
|
458
|
-
this.state.connection
|
|
459
|
-
.prepare(
|
|
460
|
-
`UPDATE interactive_requests
|
|
461
|
-
SET status = 'pending', accepted_submission_id = NULL,
|
|
462
|
-
settled_at = NULL, consumed_at = NULL, revision = revision + 1, updated_at = ?
|
|
463
|
-
WHERE request_id = ? AND status = 'settled' AND consumed_at IS NULL`,
|
|
464
|
-
)
|
|
465
|
-
.run(now, options.requestId);
|
|
466
|
-
const reopened = this.requireInteractiveRequest(options.requestId);
|
|
467
|
-
this.ensureInteractionMessage(reopened, "resumed", now);
|
|
468
|
-
return reopened;
|
|
469
|
-
}
|
|
470
|
-
if (existing.status === "pending") this.ensureInteractionMessage(existing, "initial", now);
|
|
471
|
-
return existing;
|
|
472
|
-
}
|
|
473
|
-
const contractHash = this.state.putJson(options.contract, now);
|
|
474
|
-
this.state.connection
|
|
475
|
-
.prepare(
|
|
476
|
-
`INSERT INTO interactive_requests(
|
|
477
|
-
request_id, run_id, attempt_id, target_session_id, kind, contract_hash,
|
|
478
|
-
revision, status, created_at, updated_at
|
|
479
|
-
) VALUES (?, ?, ?, ?, ?, ?, 1, 'pending', ?, ?)`,
|
|
480
|
-
)
|
|
481
|
-
.run(
|
|
482
|
-
options.requestId,
|
|
483
|
-
options.runId,
|
|
484
|
-
options.attemptId,
|
|
485
|
-
options.targetSessionId,
|
|
486
|
-
options.kind,
|
|
487
|
-
contractHash,
|
|
488
|
-
now,
|
|
489
|
-
now,
|
|
490
|
-
);
|
|
491
|
-
const created = this.requireInteractiveRequest(options.requestId);
|
|
492
|
-
this.ensureInteractionMessage(created, "initial", now);
|
|
493
|
-
return created;
|
|
443
|
+
const request = ensureWorkflowRequest(this.state, options);
|
|
444
|
+
if (request.status === "pending") this.ensureInteractionMessage(request, "initial");
|
|
445
|
+
return request;
|
|
494
446
|
});
|
|
495
447
|
}
|
|
496
448
|
|
|
@@ -499,12 +451,14 @@ export class ServerStateStore {
|
|
|
499
451
|
reason: WorkflowStepReason,
|
|
500
452
|
now: number = Date.now(),
|
|
501
453
|
) {
|
|
502
|
-
const kind = request.kind === "decision" ? "decision" : "step";
|
|
454
|
+
const kind = request.kind === "decision" || request.kind === "checkpoint" ? "decision" : "step";
|
|
503
455
|
const idempotencyKey = `${request.revision}:${reason}`;
|
|
504
456
|
const workflowMessageId = workflowMessageIdFor(kind, request.requestId, idempotencyKey);
|
|
505
457
|
const content =
|
|
506
458
|
kind === "decision"
|
|
507
|
-
?
|
|
459
|
+
? (request.kind === "checkpoint"
|
|
460
|
+
? checkpointWorkflowMessageContent
|
|
461
|
+
: decisionWorkflowMessageContent)({
|
|
508
462
|
workflowMessageId,
|
|
509
463
|
requestId: request.requestId,
|
|
510
464
|
runId: request.runId,
|
|
@@ -538,7 +492,7 @@ export class ServerStateStore {
|
|
|
538
492
|
.get(runId);
|
|
539
493
|
if (!isRequestIdRow(row)) throw new Error("Pending workflow interaction is missing");
|
|
540
494
|
const current = this.requireInteractiveRequest(row.requestId);
|
|
541
|
-
if (current.kind === "decision") return current;
|
|
495
|
+
if (current.kind === "decision" || current.kind === "checkpoint") return current;
|
|
542
496
|
this.workflowMessages.cancelPendingForSource(current.requestId, "step", now);
|
|
543
497
|
const changed = this.state.connection
|
|
544
498
|
.prepare(
|
|
@@ -553,144 +507,92 @@ export class ServerStateStore {
|
|
|
553
507
|
});
|
|
554
508
|
}
|
|
555
509
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
.prepare(
|
|
559
|
-
`SELECT i.attempt_id AS attemptId, i.created_at AS createdAt,
|
|
560
|
-
a.started_at AS startedAt, a.deadline_at AS deadlineAt,
|
|
561
|
-
(SELECT MAX(t.ended_at)
|
|
562
|
-
FROM workflow_messages m
|
|
563
|
-
JOIN workflow_turns t ON t.workflow_message_id = m.workflow_message_id
|
|
564
|
-
WHERE m.source_id = i.request_id AND m.kind = 'step'
|
|
565
|
-
AND t.state = 'ended') AS previousTurnEndedAt
|
|
566
|
-
FROM interactive_requests i
|
|
567
|
-
JOIN node_attempts a ON a.attempt_id = i.attempt_id
|
|
568
|
-
WHERE i.request_id = ? AND i.status = 'pending'`,
|
|
569
|
-
)
|
|
570
|
-
.get(requestId);
|
|
571
|
-
if (!isInteractionTimeoutRow(row)) {
|
|
572
|
-
throw new Error("Pending workflow interaction timeout is missing");
|
|
573
|
-
}
|
|
574
|
-
this.shiftInteractionTimeout(
|
|
575
|
-
row,
|
|
576
|
-
Math.max(0, now - (row.previousTurnEndedAt ?? row.startedAt ?? row.createdAt)),
|
|
577
|
-
now,
|
|
578
|
-
);
|
|
510
|
+
syncActiveTime(): void {
|
|
511
|
+
this.attemptTime.sample();
|
|
579
512
|
}
|
|
580
513
|
|
|
581
|
-
|
|
582
|
-
const
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
)
|
|
589
|
-
|
|
590
|
-
|
|
514
|
+
beginInteractionModelTurn(requestId: string): void {
|
|
515
|
+
const request = this.requireInteractiveRequest(requestId);
|
|
516
|
+
if (request.status !== "pending" || !["agent", "assistant"].includes(request.kind)) {
|
|
517
|
+
throw new Error("Only a pending agent request can start active model time");
|
|
518
|
+
}
|
|
519
|
+
const run = this.state.connection
|
|
520
|
+
.prepare("SELECT paused FROM runs WHERE run_id = ?")
|
|
521
|
+
.get(request.runId) as { paused: number };
|
|
522
|
+
if (run.paused !== 0) throw new Error("Paused workflow cannot start active model time");
|
|
523
|
+
this.attemptTime.start(request.attemptId);
|
|
591
524
|
}
|
|
592
525
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
pausedAt: number | undefined,
|
|
596
|
-
now: number = Date.now(),
|
|
597
|
-
): void {
|
|
598
|
-
if (pausedAt === undefined) return;
|
|
599
|
-
const row = this.state.connection
|
|
600
|
-
.prepare(
|
|
601
|
-
`SELECT i.attempt_id AS attemptId, i.created_at AS createdAt,
|
|
602
|
-
a.started_at AS startedAt, a.deadline_at AS deadlineAt,
|
|
603
|
-
t.ended_at AS previousTurnEndedAt
|
|
604
|
-
FROM interactive_requests i
|
|
605
|
-
JOIN node_attempts a ON a.attempt_id = i.attempt_id
|
|
606
|
-
JOIN workflow_messages m ON m.source_id = i.request_id AND m.kind = 'step'
|
|
607
|
-
JOIN workflow_turns t ON t.workflow_message_id = m.workflow_message_id
|
|
608
|
-
WHERE i.run_id = ? AND i.status = 'pending' AND t.started_at <= ?
|
|
609
|
-
AND (t.ended_at IS NULL OR t.ended_at > ?)
|
|
610
|
-
ORDER BY t.started_at DESC LIMIT 1`,
|
|
611
|
-
)
|
|
612
|
-
.get(runId, pausedAt, pausedAt);
|
|
613
|
-
if (!isInteractionTimeoutRow(row)) return;
|
|
614
|
-
const modelTurnStoppedAt = Math.min(now, row.previousTurnEndedAt ?? now);
|
|
615
|
-
this.shiftInteractionTimeout(row, Math.max(0, modelTurnStoppedAt - pausedAt), now);
|
|
526
|
+
endInteractionModelTurn(requestId: string): void {
|
|
527
|
+
this.attemptTime.stop(this.requireInteractiveRequest(requestId).attemptId);
|
|
616
528
|
}
|
|
617
529
|
|
|
618
|
-
|
|
619
|
-
this.state.connection
|
|
530
|
+
resumeInteractionModelTurn(runId: string): void {
|
|
531
|
+
const rows = this.state.connection
|
|
620
532
|
.prepare(
|
|
621
|
-
`
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
JOIN workflow_turns t ON t.workflow_message_id = m.workflow_message_id
|
|
627
|
-
AND t.state = 'started'
|
|
628
|
-
WHERE i.target_session_id = ? AND i.status = 'pending'
|
|
629
|
-
)`,
|
|
533
|
+
`SELECT DISTINCT i.attempt_id AS attemptId FROM interactive_requests i
|
|
534
|
+
JOIN runs r ON r.run_id = i.run_id
|
|
535
|
+
JOIN workflow_messages m ON m.source_id = i.request_id AND m.kind = 'step'
|
|
536
|
+
JOIN workflow_turns t ON t.workflow_message_id = m.workflow_message_id AND t.state = 'started'
|
|
537
|
+
WHERE i.run_id = ? AND i.status = 'pending' AND r.paused = 0`,
|
|
630
538
|
)
|
|
631
|
-
.
|
|
539
|
+
.all(runId) as { attemptId: string }[];
|
|
540
|
+
for (const row of rows) {
|
|
541
|
+
this.attemptTime.start(row.attemptId);
|
|
542
|
+
this.state.connection
|
|
543
|
+
.prepare("UPDATE node_attempts SET status = 'waiting' WHERE attempt_id = ?")
|
|
544
|
+
.run(row.attemptId);
|
|
545
|
+
}
|
|
632
546
|
}
|
|
633
547
|
|
|
634
|
-
|
|
635
|
-
previousHeartbeatAt: number | undefined,
|
|
636
|
-
now: number = Date.now(),
|
|
637
|
-
): void {
|
|
548
|
+
markSessionModelTurnsInactive(targetSessionId: string, now: number = Date.now()): void {
|
|
638
549
|
const rows = this.state.connection
|
|
639
550
|
.prepare(
|
|
640
|
-
`SELECT i.attempt_id AS attemptId
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
JOIN node_attempts a ON a.attempt_id = i.attempt_id
|
|
645
|
-
JOIN workflow_messages m ON m.source_id = i.request_id AND m.kind = 'step'
|
|
646
|
-
JOIN workflow_turns t ON t.workflow_message_id = m.workflow_message_id
|
|
647
|
-
AND t.state = 'started'
|
|
648
|
-
WHERE i.status = 'pending'
|
|
649
|
-
GROUP BY i.attempt_id`,
|
|
551
|
+
`SELECT DISTINCT i.attempt_id AS attemptId FROM interactive_requests i
|
|
552
|
+
JOIN workflow_messages m ON m.source_id = i.request_id AND m.kind = 'step'
|
|
553
|
+
JOIN workflow_turns t ON t.workflow_message_id = m.workflow_message_id AND t.state = 'started'
|
|
554
|
+
WHERE i.target_session_id = ? AND i.status = 'pending'`,
|
|
650
555
|
)
|
|
651
|
-
.all()
|
|
652
|
-
.filter(isInactiveInteractionTimeoutRow);
|
|
556
|
+
.all(targetSessionId) as { attemptId: string }[];
|
|
653
557
|
for (const row of rows) {
|
|
654
|
-
|
|
655
|
-
row.status === "interrupted"
|
|
656
|
-
? row.updatedAt
|
|
657
|
-
: previousHeartbeatAt === undefined
|
|
658
|
-
? row.updatedAt
|
|
659
|
-
: Math.max(previousHeartbeatAt, row.turnStartedAt);
|
|
660
|
-
this.shiftInteractionTimeout(row, Math.max(0, now - inactiveAt), now);
|
|
558
|
+
this.attemptTime.stop(row.attemptId);
|
|
661
559
|
this.state.connection
|
|
662
560
|
.prepare(
|
|
663
|
-
|
|
664
|
-
WHERE attempt_id = ? AND status IN ('pending', 'running', 'waiting', 'interrupted')`,
|
|
561
|
+
"UPDATE node_attempts SET status = 'interrupted', updated_at = ? WHERE attempt_id = ?",
|
|
665
562
|
)
|
|
666
563
|
.run(now, row.attemptId);
|
|
667
564
|
}
|
|
668
565
|
}
|
|
669
566
|
|
|
567
|
+
recoverInactiveModelTurns(now: number = Date.now()): void {
|
|
568
|
+
this.attemptTime.recover();
|
|
569
|
+
this.state.connection
|
|
570
|
+
.prepare(
|
|
571
|
+
`UPDATE node_attempts SET status = 'interrupted', updated_at = ? WHERE attempt_id IN (
|
|
572
|
+
SELECT i.attempt_id FROM interactive_requests i
|
|
573
|
+
JOIN workflow_messages m ON m.source_id = i.request_id AND m.kind = 'step'
|
|
574
|
+
JOIN workflow_turns t ON t.workflow_message_id = m.workflow_message_id AND t.state = 'started'
|
|
575
|
+
WHERE i.status = 'pending'
|
|
576
|
+
)`,
|
|
577
|
+
)
|
|
578
|
+
.run(now);
|
|
579
|
+
}
|
|
580
|
+
|
|
670
581
|
resumeSessionModelTurns(targetSessionId: string, now: number = Date.now()): void {
|
|
671
582
|
const rows = this.state.connection
|
|
672
583
|
.prepare(
|
|
673
|
-
`SELECT i.attempt_id AS attemptId
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
JOIN workflow_turns t ON t.workflow_message_id = m.workflow_message_id
|
|
680
|
-
AND t.state = 'started'
|
|
681
|
-
WHERE i.target_session_id = ? AND i.status = 'pending'
|
|
682
|
-
AND a.status = 'interrupted'
|
|
683
|
-
GROUP BY i.attempt_id`,
|
|
584
|
+
`SELECT DISTINCT i.attempt_id AS attemptId FROM interactive_requests i
|
|
585
|
+
JOIN runs r ON r.run_id = i.run_id
|
|
586
|
+
JOIN node_attempts a ON a.attempt_id = i.attempt_id
|
|
587
|
+
JOIN workflow_messages m ON m.source_id = i.request_id AND m.kind = 'step'
|
|
588
|
+
JOIN workflow_turns t ON t.workflow_message_id = m.workflow_message_id AND t.state = 'started'
|
|
589
|
+
WHERE i.target_session_id = ? AND i.status = 'pending' AND r.paused = 0 AND a.status = 'interrupted'`,
|
|
684
590
|
)
|
|
685
|
-
.all(targetSessionId)
|
|
686
|
-
.filter(isInactiveInteractionTimeoutRow);
|
|
591
|
+
.all(targetSessionId) as { attemptId: string }[];
|
|
687
592
|
for (const row of rows) {
|
|
688
|
-
this.
|
|
593
|
+
this.attemptTime.start(row.attemptId);
|
|
689
594
|
this.state.connection
|
|
690
|
-
.prepare(
|
|
691
|
-
`UPDATE node_attempts SET status = 'waiting', updated_at = ?
|
|
692
|
-
WHERE attempt_id = ? AND status = 'interrupted'`,
|
|
693
|
-
)
|
|
595
|
+
.prepare("UPDATE node_attempts SET status = 'waiting', updated_at = ? WHERE attempt_id = ?")
|
|
694
596
|
.run(now, row.attemptId);
|
|
695
597
|
}
|
|
696
598
|
}
|
|
@@ -760,14 +662,18 @@ export class ServerStateStore {
|
|
|
760
662
|
};
|
|
761
663
|
}
|
|
762
664
|
|
|
763
|
-
|
|
665
|
+
readyInteractionRunIds(): string[] {
|
|
764
666
|
return this.state.connection
|
|
765
667
|
.prepare(
|
|
766
668
|
`SELECT i.run_id AS runId
|
|
767
669
|
FROM interactive_requests i
|
|
768
670
|
JOIN interactive_submissions s ON s.request_id = i.request_id
|
|
769
671
|
JOIN run_queue q ON q.run_id = i.run_id
|
|
770
|
-
|
|
672
|
+
JOIN node_attempts a ON a.attempt_id = i.attempt_id
|
|
673
|
+
WHERE ((i.status = 'pending' AND s.outcome = 'validating')
|
|
674
|
+
OR (i.status = 'settled' AND i.accepted_submission_id = s.submission_id
|
|
675
|
+
AND s.outcome = 'accepted' AND i.consumed_at IS NULL))
|
|
676
|
+
AND a.status IN ('pending', 'running', 'waiting', 'interrupted')
|
|
771
677
|
AND q.status NOT IN ('done', 'failed', 'cancelled')
|
|
772
678
|
GROUP BY i.run_id
|
|
773
679
|
ORDER BY MIN(s.submitted_at), i.run_id`,
|
|
@@ -776,26 +682,21 @@ export class ServerStateStore {
|
|
|
776
682
|
.flatMap((row) => (isRunIdRow(row) ? [row.runId] : []));
|
|
777
683
|
}
|
|
778
684
|
|
|
779
|
-
expiredInteractionRuns(
|
|
685
|
+
expiredInteractionRuns(): ExpiredInteractionRunRow[] {
|
|
780
686
|
return this.state.connection
|
|
781
687
|
.prepare(
|
|
782
688
|
`SELECT i.run_id AS runId, i.target_session_id AS targetSessionId
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
JOIN workflow_turns t ON t.workflow_message_id = m.workflow_message_id
|
|
793
|
-
WHERE m.source_id = i.request_id AND m.kind = 'step' AND t.state = 'started'
|
|
794
|
-
)
|
|
795
|
-
GROUP BY i.run_id, i.target_session_id
|
|
796
|
-
ORDER BY MIN(a.deadline_at), i.run_id`,
|
|
689
|
+
FROM interactive_requests i
|
|
690
|
+
JOIN node_attempts a ON a.attempt_id = i.attempt_id
|
|
691
|
+
JOIN runs r ON r.run_id = i.run_id
|
|
692
|
+
JOIN run_queue q ON q.run_id = i.run_id
|
|
693
|
+
WHERE i.status = 'pending' AND r.paused = 0 AND a.timeout_ms > 0
|
|
694
|
+
AND (SELECT COALESCE(SUM(elapsed_ms), 0) FROM attempt_active_intervals t
|
|
695
|
+
WHERE t.attempt_id = a.attempt_id) >= a.timeout_ms
|
|
696
|
+
AND q.status NOT IN ('done', 'failed', 'cancelled')
|
|
697
|
+
GROUP BY i.run_id, i.target_session_id ORDER BY MIN(a.started_at), i.run_id`,
|
|
797
698
|
)
|
|
798
|
-
.all(
|
|
699
|
+
.all()
|
|
799
700
|
.filter(isExpiredInteractionRunRow);
|
|
800
701
|
}
|
|
801
702
|
|
|
@@ -803,20 +704,17 @@ export class ServerStateStore {
|
|
|
803
704
|
const row = this.state.connection
|
|
804
705
|
.prepare(
|
|
805
706
|
`SELECT i.request_id AS requestId, i.attempt_id AS attemptId, a.node_id AS nodeId
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
WHERE m.source_id = i.request_id AND m.kind = 'step' AND t.state = 'started'
|
|
816
|
-
)
|
|
817
|
-
ORDER BY a.deadline_at, i.request_id LIMIT 1`,
|
|
707
|
+
FROM interactive_requests i
|
|
708
|
+
JOIN node_attempts a ON a.attempt_id = i.attempt_id
|
|
709
|
+
JOIN runs r ON r.run_id = i.run_id
|
|
710
|
+
WHERE i.run_id = ? AND i.status = 'cancelled' AND r.status = 'running'
|
|
711
|
+
AND r.paused = 0 AND a.timeout_ms > 0
|
|
712
|
+
AND (SELECT COALESCE(SUM(elapsed_ms), 0) FROM attempt_active_intervals t
|
|
713
|
+
WHERE t.attempt_id = a.attempt_id) >= a.timeout_ms
|
|
714
|
+
AND a.status IN ('pending', 'running', 'waiting', 'interrupted', 'timed_out')
|
|
715
|
+
ORDER BY a.started_at, i.request_id LIMIT 1`,
|
|
818
716
|
)
|
|
819
|
-
.get(runId
|
|
717
|
+
.get(runId);
|
|
820
718
|
return isTimedOutInteractionRow(row) ? row : undefined;
|
|
821
719
|
}
|
|
822
720
|
|
|
@@ -890,17 +788,6 @@ export class ServerStateStore {
|
|
|
890
788
|
});
|
|
891
789
|
}
|
|
892
790
|
|
|
893
|
-
consumeAcceptedInteraction(runId: string, attemptId: string, now: number = Date.now()): boolean {
|
|
894
|
-
const changed = this.state.connection
|
|
895
|
-
.prepare(
|
|
896
|
-
`UPDATE interactive_requests SET consumed_at = ?, updated_at = ?
|
|
897
|
-
WHERE run_id = ? AND attempt_id = ? AND status = 'settled'
|
|
898
|
-
AND accepted_submission_id IS NOT NULL AND consumed_at IS NULL`,
|
|
899
|
-
)
|
|
900
|
-
.run(now, now, runId, attemptId);
|
|
901
|
-
return changed.changes === 1;
|
|
902
|
-
}
|
|
903
|
-
|
|
904
791
|
listPendingInteractions(sessionId: string): InteractiveRequestRecord[] {
|
|
905
792
|
return this.state.connection
|
|
906
793
|
.prepare(
|
|
@@ -941,13 +828,23 @@ export class ServerStateStore {
|
|
|
941
828
|
outcome: "accepted" | "adopted";
|
|
942
829
|
receipt: JsonValue;
|
|
943
830
|
} {
|
|
944
|
-
return this.
|
|
831
|
+
return recordWorkflowSubmission(this.state, {
|
|
945
832
|
...options,
|
|
946
833
|
outcome: options.accepted ? "accepted" : "rejected",
|
|
947
834
|
settle: options.accepted,
|
|
948
835
|
});
|
|
949
836
|
}
|
|
950
837
|
|
|
838
|
+
hasInteractionSubmission(requestId: string, idempotencyKey: string): boolean {
|
|
839
|
+
return (
|
|
840
|
+
this.state.connection
|
|
841
|
+
.prepare(
|
|
842
|
+
"SELECT 1 FROM interactive_submissions WHERE request_id = ? AND idempotency_key = ?",
|
|
843
|
+
)
|
|
844
|
+
.get(requestId, idempotencyKey) !== undefined
|
|
845
|
+
);
|
|
846
|
+
}
|
|
847
|
+
|
|
951
848
|
beginInteractionValidation(options: {
|
|
952
849
|
requestId: string;
|
|
953
850
|
submissionId: string;
|
|
@@ -961,111 +858,13 @@ export class ServerStateStore {
|
|
|
961
858
|
outcome: "accepted" | "adopted";
|
|
962
859
|
receipt: JsonValue;
|
|
963
860
|
} {
|
|
964
|
-
return this.
|
|
861
|
+
return recordWorkflowSubmission(this.state, {
|
|
965
862
|
...options,
|
|
966
863
|
outcome: "validating",
|
|
967
864
|
settle: false,
|
|
968
865
|
});
|
|
969
866
|
}
|
|
970
867
|
|
|
971
|
-
private recordInteractionSubmission(options: {
|
|
972
|
-
requestId: string;
|
|
973
|
-
submissionId: string;
|
|
974
|
-
idempotencyKey: string;
|
|
975
|
-
expectedRevision: number;
|
|
976
|
-
payload: JsonValue;
|
|
977
|
-
outcome: InteractiveSubmissionRecord["outcome"];
|
|
978
|
-
settle: boolean;
|
|
979
|
-
receipt?: JsonValue;
|
|
980
|
-
}): {
|
|
981
|
-
interaction: InteractiveRequestRecord;
|
|
982
|
-
submissionId: string;
|
|
983
|
-
outcome: "accepted" | "adopted";
|
|
984
|
-
receipt: JsonValue;
|
|
985
|
-
} {
|
|
986
|
-
const now = Date.now();
|
|
987
|
-
return this.state.transaction(() => {
|
|
988
|
-
const existing = this.state.connection
|
|
989
|
-
.prepare(
|
|
990
|
-
`SELECT submission_id AS submissionId, payload_hash AS payloadHash,
|
|
991
|
-
outcome, receipt_hash AS receiptHash
|
|
992
|
-
FROM interactive_submissions WHERE request_id = ? AND idempotency_key = ?`,
|
|
993
|
-
)
|
|
994
|
-
.get(options.requestId, options.idempotencyKey);
|
|
995
|
-
const payloadHash = createHash("sha256").update(canonicalJson(options.payload)).digest();
|
|
996
|
-
if (isSubmissionRow(existing)) {
|
|
997
|
-
if (!existing.payloadHash.equals(payloadHash)) {
|
|
998
|
-
throw new Error("Interactive submission idempotency key conflicts");
|
|
999
|
-
}
|
|
1000
|
-
return {
|
|
1001
|
-
interaction: this.requireInteractiveRequest(options.requestId),
|
|
1002
|
-
submissionId: existing.submissionId,
|
|
1003
|
-
outcome: "adopted",
|
|
1004
|
-
receipt:
|
|
1005
|
-
existing.receiptHash === null
|
|
1006
|
-
? { requestId: options.requestId, submissionId: existing.submissionId }
|
|
1007
|
-
: this.state.readJson(existing.receiptHash),
|
|
1008
|
-
};
|
|
1009
|
-
}
|
|
1010
|
-
if (options.outcome === "validating") {
|
|
1011
|
-
const active = this.state.connection
|
|
1012
|
-
.prepare(
|
|
1013
|
-
`SELECT submission_id AS submissionId FROM interactive_submissions
|
|
1014
|
-
WHERE request_id = ? AND outcome = 'validating' LIMIT 1`,
|
|
1015
|
-
)
|
|
1016
|
-
.get(options.requestId);
|
|
1017
|
-
if (isSubmissionIdRow(active)) {
|
|
1018
|
-
throw new Error("Interactive submission validation is already active");
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
const request = this.requireInteractiveRequest(options.requestId);
|
|
1022
|
-
if (request.revision !== options.expectedRevision || request.status !== "pending") {
|
|
1023
|
-
throw new Error("Interactive request revision conflict");
|
|
1024
|
-
}
|
|
1025
|
-
const savedPayloadHash = this.state.putJson(options.payload, now);
|
|
1026
|
-
const receiptHash =
|
|
1027
|
-
options.receipt === undefined ? null : this.state.putJson(options.receipt, now);
|
|
1028
|
-
this.state.connection
|
|
1029
|
-
.prepare(
|
|
1030
|
-
`INSERT INTO interactive_submissions(
|
|
1031
|
-
submission_id, request_id, idempotency_key, request_revision,
|
|
1032
|
-
payload_hash, outcome, receipt_hash, submitted_at
|
|
1033
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
1034
|
-
)
|
|
1035
|
-
.run(
|
|
1036
|
-
options.submissionId,
|
|
1037
|
-
options.requestId,
|
|
1038
|
-
options.idempotencyKey,
|
|
1039
|
-
options.expectedRevision,
|
|
1040
|
-
savedPayloadHash,
|
|
1041
|
-
options.outcome,
|
|
1042
|
-
receiptHash,
|
|
1043
|
-
now,
|
|
1044
|
-
);
|
|
1045
|
-
if (options.settle) {
|
|
1046
|
-
this.state.connection
|
|
1047
|
-
.prepare(
|
|
1048
|
-
`UPDATE interactive_requests
|
|
1049
|
-
SET status = 'settled', accepted_submission_id = ?, revision = revision + 1,
|
|
1050
|
-
updated_at = ?, settled_at = ?
|
|
1051
|
-
WHERE request_id = ? AND revision = ? AND status = 'pending'`,
|
|
1052
|
-
)
|
|
1053
|
-
.run(options.submissionId, now, now, options.requestId, options.expectedRevision);
|
|
1054
|
-
this.workflowMessages.cancelPendingForSource(options.requestId, "step", now);
|
|
1055
|
-
this.workflowMessages.cancelPendingForSource(options.requestId, "decision", now);
|
|
1056
|
-
}
|
|
1057
|
-
return {
|
|
1058
|
-
interaction: this.requireInteractiveRequest(options.requestId),
|
|
1059
|
-
submissionId: options.submissionId,
|
|
1060
|
-
outcome: "accepted",
|
|
1061
|
-
receipt: options.receipt ?? {
|
|
1062
|
-
requestId: options.requestId,
|
|
1063
|
-
submissionId: options.submissionId,
|
|
1064
|
-
},
|
|
1065
|
-
};
|
|
1066
|
-
});
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
868
|
private serverRow(): ServerRow {
|
|
1070
869
|
const row = this.state.connection
|
|
1071
870
|
.prepare(
|
|
@@ -1097,50 +896,7 @@ export class ServerStateStore {
|
|
|
1097
896
|
}
|
|
1098
897
|
|
|
1099
898
|
private interactiveRequest(requestId: string): InteractiveRequestRecord | undefined {
|
|
1100
|
-
|
|
1101
|
-
.prepare(
|
|
1102
|
-
`SELECT request_id AS requestId, run_id AS runId, attempt_id AS attemptId,
|
|
1103
|
-
target_session_id AS targetSessionId, kind, contract_hash AS contractHash,
|
|
1104
|
-
revision, status, unproductive_turn_ends AS unproductiveTurnEnds,
|
|
1105
|
-
accepted_submission_id AS acceptedSubmissionId, created_at AS createdAt,
|
|
1106
|
-
updated_at AS updatedAt, settled_at AS settledAt, consumed_at AS consumedAt
|
|
1107
|
-
FROM interactive_requests WHERE request_id = ?`,
|
|
1108
|
-
)
|
|
1109
|
-
.get(requestId);
|
|
1110
|
-
if (!isInteractiveRequestRow(row)) return undefined;
|
|
1111
|
-
return {
|
|
1112
|
-
requestId: row.requestId,
|
|
1113
|
-
runId: row.runId,
|
|
1114
|
-
attemptId: row.attemptId,
|
|
1115
|
-
targetSessionId: row.targetSessionId,
|
|
1116
|
-
kind: row.kind,
|
|
1117
|
-
contract: this.state.readJson(row.contractHash),
|
|
1118
|
-
revision: row.revision,
|
|
1119
|
-
status: row.status,
|
|
1120
|
-
unproductiveTurnEnds: row.unproductiveTurnEnds,
|
|
1121
|
-
acceptedSubmissionId: row.acceptedSubmissionId,
|
|
1122
|
-
createdAt: new Date(row.createdAt).toISOString(),
|
|
1123
|
-
updatedAt: new Date(row.updatedAt).toISOString(),
|
|
1124
|
-
settledAt: iso(row.settledAt),
|
|
1125
|
-
consumedAt: iso(row.consumedAt),
|
|
1126
|
-
};
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
private shiftInteractionTimeout(
|
|
1130
|
-
row: Pick<InteractionTimeoutRow, "attemptId" | "startedAt" | "deadlineAt">,
|
|
1131
|
-
idleMs: number,
|
|
1132
|
-
now: number,
|
|
1133
|
-
): void {
|
|
1134
|
-
if (idleMs === 0 || row.deadlineAt === null) return;
|
|
1135
|
-
const changed = this.state.connection
|
|
1136
|
-
.prepare(
|
|
1137
|
-
`UPDATE node_attempts
|
|
1138
|
-
SET started_at = CASE WHEN started_at IS NULL THEN NULL ELSE started_at + ? END,
|
|
1139
|
-
deadline_at = deadline_at + ?, updated_at = ?
|
|
1140
|
-
WHERE attempt_id = ? AND deadline_at = ?`,
|
|
1141
|
-
)
|
|
1142
|
-
.run(idleMs, idleMs, now, row.attemptId, row.deadlineAt);
|
|
1143
|
-
if (changed.changes !== 1) throw new Error("Workflow interaction timeout changed concurrently");
|
|
899
|
+
return readWorkflowRequest(this.state, requestId);
|
|
1144
900
|
}
|
|
1145
901
|
|
|
1146
902
|
private requireInteractiveRequest(requestId: string): InteractiveRequestRecord {
|
|
@@ -1191,13 +947,6 @@ type CommandRow = {
|
|
|
1191
947
|
type IdempotentCommandRow = CommandRow & { requestId: string };
|
|
1192
948
|
type RequestIdRow = { requestId: string };
|
|
1193
949
|
type RunIdRow = { runId: string };
|
|
1194
|
-
type SubmissionRow = {
|
|
1195
|
-
submissionId: string;
|
|
1196
|
-
payloadHash: Buffer;
|
|
1197
|
-
outcome: string;
|
|
1198
|
-
receiptHash: Buffer | null;
|
|
1199
|
-
};
|
|
1200
|
-
type SubmissionIdRow = { submissionId: string };
|
|
1201
950
|
type SubmissionDetailRow = {
|
|
1202
951
|
requestId: string;
|
|
1203
952
|
submissionId: string;
|
|
@@ -1221,44 +970,12 @@ type AcceptedInteractionRow = {
|
|
|
1221
970
|
submissionId: string;
|
|
1222
971
|
payloadHash: Buffer;
|
|
1223
972
|
};
|
|
973
|
+
type ExpiredInteractionRunRow = { runId: string; targetSessionId: string };
|
|
1224
974
|
type TimedOutInteractionRow = {
|
|
1225
975
|
requestId: string;
|
|
1226
976
|
attemptId: string;
|
|
1227
977
|
nodeId: string;
|
|
1228
978
|
};
|
|
1229
|
-
type InteractionTimeoutRow = {
|
|
1230
|
-
attemptId: string;
|
|
1231
|
-
createdAt: number;
|
|
1232
|
-
startedAt: number | null;
|
|
1233
|
-
deadlineAt: number | null;
|
|
1234
|
-
previousTurnEndedAt: number | null;
|
|
1235
|
-
};
|
|
1236
|
-
type InactiveInteractionTimeoutRow = {
|
|
1237
|
-
attemptId: string;
|
|
1238
|
-
status: "pending" | "running" | "waiting" | "interrupted";
|
|
1239
|
-
startedAt: number | null;
|
|
1240
|
-
deadlineAt: number | null;
|
|
1241
|
-
updatedAt: number;
|
|
1242
|
-
turnStartedAt: number;
|
|
1243
|
-
};
|
|
1244
|
-
type ExpiredInteractionRunRow = { runId: string; targetSessionId: string };
|
|
1245
|
-
type PausedAtRow = { pausedAt: number };
|
|
1246
|
-
type InteractiveRequestRow = {
|
|
1247
|
-
requestId: string;
|
|
1248
|
-
runId: string;
|
|
1249
|
-
attemptId: string;
|
|
1250
|
-
targetSessionId: string;
|
|
1251
|
-
kind: InteractiveRequestRecord["kind"];
|
|
1252
|
-
contractHash: Buffer;
|
|
1253
|
-
revision: number;
|
|
1254
|
-
status: InteractiveRequestRecord["status"];
|
|
1255
|
-
unproductiveTurnEnds: number;
|
|
1256
|
-
acceptedSubmissionId: string | null;
|
|
1257
|
-
createdAt: number;
|
|
1258
|
-
updatedAt: number;
|
|
1259
|
-
settledAt: number | null;
|
|
1260
|
-
consumedAt: number | null;
|
|
1261
|
-
};
|
|
1262
979
|
|
|
1263
980
|
function isServerRow(value: unknown): value is ServerRow {
|
|
1264
981
|
return (
|
|
@@ -1332,53 +1049,12 @@ function isTimedOutInteractionRow(value: unknown): value is TimedOutInteractionR
|
|
|
1332
1049
|
);
|
|
1333
1050
|
}
|
|
1334
1051
|
|
|
1335
|
-
function isInteractionTimeoutRow(value: unknown): value is InteractionTimeoutRow {
|
|
1336
|
-
return (
|
|
1337
|
-
isRecord(value) &&
|
|
1338
|
-
typeof value.attemptId === "string" &&
|
|
1339
|
-
typeof value.createdAt === "number" &&
|
|
1340
|
-
nullableNumber(value.startedAt) &&
|
|
1341
|
-
nullableNumber(value.deadlineAt) &&
|
|
1342
|
-
nullableNumber(value.previousTurnEndedAt)
|
|
1343
|
-
);
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
function isInactiveInteractionTimeoutRow(value: unknown): value is InactiveInteractionTimeoutRow {
|
|
1347
|
-
return (
|
|
1348
|
-
isRecord(value) &&
|
|
1349
|
-
typeof value.attemptId === "string" &&
|
|
1350
|
-
["pending", "running", "waiting", "interrupted"].includes(value.status as string) &&
|
|
1351
|
-
nullableNumber(value.startedAt) &&
|
|
1352
|
-
nullableNumber(value.deadlineAt) &&
|
|
1353
|
-
typeof value.updatedAt === "number" &&
|
|
1354
|
-
typeof value.turnStartedAt === "number"
|
|
1355
|
-
);
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
1052
|
function isExpiredInteractionRunRow(value: unknown): value is ExpiredInteractionRunRow {
|
|
1359
1053
|
return (
|
|
1360
1054
|
isRecord(value) && typeof value.runId === "string" && typeof value.targetSessionId === "string"
|
|
1361
1055
|
);
|
|
1362
1056
|
}
|
|
1363
1057
|
|
|
1364
|
-
function isPausedAtRow(value: unknown): value is PausedAtRow {
|
|
1365
|
-
return isRecord(value) && typeof value.pausedAt === "number";
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1368
|
-
function isSubmissionRow(value: unknown): value is SubmissionRow {
|
|
1369
|
-
return (
|
|
1370
|
-
isRecord(value) &&
|
|
1371
|
-
typeof value.submissionId === "string" &&
|
|
1372
|
-
Buffer.isBuffer(value.payloadHash) &&
|
|
1373
|
-
typeof value.outcome === "string" &&
|
|
1374
|
-
(value.receiptHash === null || Buffer.isBuffer(value.receiptHash))
|
|
1375
|
-
);
|
|
1376
|
-
}
|
|
1377
|
-
|
|
1378
|
-
function isSubmissionIdRow(value: unknown): value is SubmissionIdRow {
|
|
1379
|
-
return isRecord(value) && typeof value.submissionId === "string";
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
1058
|
function isSubmissionDetailRow(value: unknown): value is SubmissionDetailRow {
|
|
1383
1059
|
return (
|
|
1384
1060
|
isRecord(value) &&
|
|
@@ -1392,26 +1068,6 @@ function isSubmissionDetailRow(value: unknown): value is SubmissionDetailRow {
|
|
|
1392
1068
|
);
|
|
1393
1069
|
}
|
|
1394
1070
|
|
|
1395
|
-
function isInteractiveRequestRow(value: unknown): value is InteractiveRequestRow {
|
|
1396
|
-
return (
|
|
1397
|
-
isRecord(value) &&
|
|
1398
|
-
typeof value.requestId === "string" &&
|
|
1399
|
-
typeof value.runId === "string" &&
|
|
1400
|
-
typeof value.attemptId === "string" &&
|
|
1401
|
-
typeof value.targetSessionId === "string" &&
|
|
1402
|
-
["agent", "assistant", "decision"].includes(value.kind as string) &&
|
|
1403
|
-
Buffer.isBuffer(value.contractHash) &&
|
|
1404
|
-
typeof value.revision === "number" &&
|
|
1405
|
-
["pending", "settled", "cancelled"].includes(value.status as string) &&
|
|
1406
|
-
typeof value.unproductiveTurnEnds === "number" &&
|
|
1407
|
-
nullableString(value.acceptedSubmissionId) &&
|
|
1408
|
-
typeof value.createdAt === "number" &&
|
|
1409
|
-
typeof value.updatedAt === "number" &&
|
|
1410
|
-
nullableNumber(value.settledAt) &&
|
|
1411
|
-
nullableNumber(value.consumedAt)
|
|
1412
|
-
);
|
|
1413
|
-
}
|
|
1414
|
-
|
|
1415
1071
|
function runnerMessageFingerprint(message: WorkflowRunnerMessage): Buffer {
|
|
1416
1072
|
return createHash("sha256").update(canonicalJson(message)).digest();
|
|
1417
1073
|
}
|