@agentswf/contract 0.0.0-stage → 0.1.0-beta.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/package.json +32 -4
- package/src/call.ts +6 -0
- package/src/index.ts +4 -0
- package/src/participant.ts +15 -0
- package/src/records.ts +893 -0
- package/src/records.typecheck.ts +5 -0
- package/src/schema.ts +342 -0
- package/src/semantic.ts +15 -0
- package/src/testing/fixtures.ts +11 -0
- package/src/wire.ts +578 -0
- package/src/workflow/agents.ts +427 -0
- package/src/workflow/composition.ts +18 -0
- package/src/workflow/deadlines.typecheck.ts +76 -0
- package/src/workflow/decisions.ts +176 -0
- package/src/workflow/decisions.typecheck.ts +97 -0
- package/src/workflow/executable.ts +87 -0
- package/src/workflow/index.ts +10 -0
- package/src/workflow/json.ts +74 -0
- package/src/workflow/messaging.ts +31 -0
- package/src/workflow/messaging.typecheck.ts +114 -0
- package/src/workflow/participants.ts +33 -0
- package/src/workflow/sandboxes.ts +87 -0
- package/src/workflow/sandboxes.typecheck.ts +64 -0
- package/src/workflow/timing.ts +14 -0
- package/src/workflow/workflow.ts +128 -0
- package/README.md +0 -3
package/src/records.ts
ADDED
|
@@ -0,0 +1,893 @@
|
|
|
1
|
+
import type { JsonSchema } from "./schema";
|
|
2
|
+
import type { ReplyCode, SendCode } from "./wire";
|
|
3
|
+
import type {
|
|
4
|
+
AgentExecution,
|
|
5
|
+
AgentKey,
|
|
6
|
+
HarnessKind,
|
|
7
|
+
KeepPane,
|
|
8
|
+
NativeSessionRef,
|
|
9
|
+
OperationRecord,
|
|
10
|
+
PaneLayout,
|
|
11
|
+
PaneWorkspace,
|
|
12
|
+
SkillSource,
|
|
13
|
+
TurnOutcome,
|
|
14
|
+
} from "./workflow/agents";
|
|
15
|
+
import type { DecisionRecord, Question } from "./workflow/decisions";
|
|
16
|
+
import type { AttemptOutcome, StageSummary, UnfinishedOutcome } from "./workflow/executable";
|
|
17
|
+
import type { JsonObject, JsonValue } from "./workflow/json";
|
|
18
|
+
import type {
|
|
19
|
+
Domain,
|
|
20
|
+
Gitdir,
|
|
21
|
+
SandboxEnvironmentKey,
|
|
22
|
+
SandboxKey,
|
|
23
|
+
SandboxSpec,
|
|
24
|
+
} from "./workflow/sandboxes";
|
|
25
|
+
import type { StageOutcome } from "./workflow/workflow";
|
|
26
|
+
|
|
27
|
+
/** What the run directory records about one call. The format only; the engine does the I/O. */
|
|
28
|
+
export type CallSpec = {
|
|
29
|
+
callId: string;
|
|
30
|
+
question: string;
|
|
31
|
+
schema?: JsonSchema;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Which channel carried a candidate value — `control-plane` in production. A string, because
|
|
36
|
+
* candidates recorded by the archived experiments name channels of their own.
|
|
37
|
+
*/
|
|
38
|
+
export type CandidateSource = string;
|
|
39
|
+
|
|
40
|
+
/** Every candidate value, accepted or not. A rejection is evidence, so it is never dropped. */
|
|
41
|
+
export type Candidate = {
|
|
42
|
+
at: string;
|
|
43
|
+
source: CandidateSource;
|
|
44
|
+
accepted: boolean;
|
|
45
|
+
raw: string;
|
|
46
|
+
error?: string;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export type Money = {
|
|
50
|
+
/** Non-negative amount in `currency`. */
|
|
51
|
+
amount: number;
|
|
52
|
+
/** ISO 4217 currency code. */
|
|
53
|
+
currency: string;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** Non-negative integer token counts, in classes that are priced differently. */
|
|
57
|
+
export type TokenUsage = {
|
|
58
|
+
/** Uncached input only. */
|
|
59
|
+
input: number;
|
|
60
|
+
cacheRead: number;
|
|
61
|
+
/** Every cache write, whatever its lifetime. */
|
|
62
|
+
cacheWrite: number;
|
|
63
|
+
/** The part of `cacheWrite` with a one-hour lifetime, where the harness reports it. */
|
|
64
|
+
cacheWrite1h?: number;
|
|
65
|
+
/** Includes reasoning. */
|
|
66
|
+
output: number;
|
|
67
|
+
/** The part of `output` spent reasoning, where the harness reports it. */
|
|
68
|
+
reasoning?: number;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/** One model's tokens; `delegated` marks what the agent's own subagents spent. */
|
|
72
|
+
export type ModelSpend = { model: string; delegated: boolean; tokens: TokenUsage };
|
|
73
|
+
|
|
74
|
+
/** Who paid for an agent: a plan, or per token. `unknown` only when that could not be told. */
|
|
75
|
+
export type Billing = "subscription" | "metered" | "unknown";
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* An operation's record once the run has ended and its agent's spend was read. It records no
|
|
79
|
+
* price, so a finished run can be priced again with another table. Records are deliberately
|
|
80
|
+
* self-contained: an agent's sessions and billing repeat on each of its operations, so a flat list
|
|
81
|
+
* can be summarised and re-priced alone.
|
|
82
|
+
*/
|
|
83
|
+
export type SettledOperation = OperationRecord & {
|
|
84
|
+
billing: Billing;
|
|
85
|
+
/** Absent means unknown. */
|
|
86
|
+
spend?: ModelSpend[];
|
|
87
|
+
/** Only what was actually billed per token; decided with `billing`. */
|
|
88
|
+
charged?: Money;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* What `decisions/{n}.json` in a run's artifacts holds: everything asked and answered, so a
|
|
93
|
+
* threshold can be fitted again without calling the model.
|
|
94
|
+
*/
|
|
95
|
+
export type DecisionArtifact = {
|
|
96
|
+
record: SettledDecision;
|
|
97
|
+
request: { state: string | JsonObject | JsonValue[]; questions: Record<string, Question> };
|
|
98
|
+
/** As `decide` returned them; absent when the call did not answer. */
|
|
99
|
+
answers?: JsonObject;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/** A decision once the run has ended: what the provider said it charged, beside its record. */
|
|
103
|
+
export type SettledDecision = DecisionRecord & {
|
|
104
|
+
/** As the provider reported it; absent when it reported none. */
|
|
105
|
+
charged?: Money;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* What a slice's decisions cost. Kept apart from the agent figures, which count agents: a decision
|
|
110
|
+
* model with no rate folded into those would lower `estimate` with no gap showing.
|
|
111
|
+
*/
|
|
112
|
+
export type DecisionFigures = {
|
|
113
|
+
calls: number;
|
|
114
|
+
/** Requests sent, retries included. */
|
|
115
|
+
attempts: number;
|
|
116
|
+
tokens: { input: number; output: number };
|
|
117
|
+
/** USD at list price, over the calls whose tokens are known and priced. */
|
|
118
|
+
estimate?: number;
|
|
119
|
+
/** USD the provider reported charging. */
|
|
120
|
+
charged?: number;
|
|
121
|
+
/** How many of `calls` have known tokens. */
|
|
122
|
+
known: number;
|
|
123
|
+
/** How many of `calls` were known and priced. */
|
|
124
|
+
priced: number;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/** One slice of a run: all of it, a stage, or one agent. */
|
|
128
|
+
export type AccountingFigures = {
|
|
129
|
+
agents: number;
|
|
130
|
+
/** The sum, over operations, of the time from delivery to settle. */
|
|
131
|
+
agentMs: number;
|
|
132
|
+
tokens: TokenUsage;
|
|
133
|
+
/** The part of `tokens` the agents' own subagents spent. */
|
|
134
|
+
delegated: TokenUsage;
|
|
135
|
+
/**
|
|
136
|
+
* USD at list price, over the priced tokens. Absent only when no agent's usage was both known
|
|
137
|
+
* and priced: a known zero is 0, an unknown is never one.
|
|
138
|
+
*/
|
|
139
|
+
estimate?: number;
|
|
140
|
+
/** USD actually billed per token. */
|
|
141
|
+
charged?: number;
|
|
142
|
+
/** How many of `agents` have known usage. */
|
|
143
|
+
known: number;
|
|
144
|
+
/** How many of `agents` had all their usage priced. */
|
|
145
|
+
priced: number;
|
|
146
|
+
/**
|
|
147
|
+
* How many of `agents` have a known billing and every charge counted in `charged`; the rest may
|
|
148
|
+
* have been charged unseen.
|
|
149
|
+
*/
|
|
150
|
+
billed: number;
|
|
151
|
+
/** Absent when the slice asked no decision model anything. */
|
|
152
|
+
decisions?: DecisionFigures;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/** One model's share. Time and charges belong to an operation, not a model, so are not split. */
|
|
156
|
+
export type ModelFigures = {
|
|
157
|
+
model: string;
|
|
158
|
+
agents: number;
|
|
159
|
+
/** Decision calls whose tokens are known, as this model; absent when there are none. */
|
|
160
|
+
decisionCalls?: number;
|
|
161
|
+
tokens: TokenUsage;
|
|
162
|
+
delegated: TokenUsage;
|
|
163
|
+
/** Absent when the table has no rate for the model; see `unpriced`. */
|
|
164
|
+
estimate?: number;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/** What a run cost and how long it took, derived from its settled operations and its two times. */
|
|
168
|
+
export type RunAccounting = {
|
|
169
|
+
/** The price table `estimate` came from. */
|
|
170
|
+
basis: string;
|
|
171
|
+
startedAt: string;
|
|
172
|
+
finishedAt: string;
|
|
173
|
+
wallMs: number;
|
|
174
|
+
/**
|
|
175
|
+
* What `byStage` groups by: the workflow's stages, or, for a run without any, the call path and
|
|
176
|
+
* key prefix.
|
|
177
|
+
*/
|
|
178
|
+
grouping: "stages" | "prefix";
|
|
179
|
+
/** `mixed` when agents whose billing is known disagree; `unknown` when none is known. */
|
|
180
|
+
billing: Billing | "mixed";
|
|
181
|
+
totals: AccountingFigures;
|
|
182
|
+
/**
|
|
183
|
+
* Each workflow stage entered, in order, a reused one at zero, then `(no stage)` for what ran
|
|
184
|
+
* between stages. A run without stages groups by the call path and the agent or decision key's
|
|
185
|
+
* prefix before `:`, joined with `/`.
|
|
186
|
+
*/
|
|
187
|
+
byStage: (AccountingFigures & { stage: string; spanMs: number })[];
|
|
188
|
+
byModel: ModelFigures[];
|
|
189
|
+
byAgent: (Omit<AccountingFigures, "decisions"> & {
|
|
190
|
+
callPath: string[];
|
|
191
|
+
agent: string;
|
|
192
|
+
execution: AgentExecution;
|
|
193
|
+
billing: Billing;
|
|
194
|
+
})[];
|
|
195
|
+
/** Models, agents' or decisions', the table has no rate for. Their tokens are counted; their cost is not. */
|
|
196
|
+
unpriced: string[];
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
/** A sandbox a run opened, with every agent it admitted. */
|
|
200
|
+
export type SandboxRecord = {
|
|
201
|
+
callPath: string[];
|
|
202
|
+
/** `agent:{key}` for a private sandbox, a namespace explicit keys cannot use. */
|
|
203
|
+
key: SandboxKey;
|
|
204
|
+
/** An environment key such as `srt` or `docker`; more come with new providers, so a reader never switches on it exhaustively. */
|
|
205
|
+
provider: SandboxEnvironmentKey;
|
|
206
|
+
/** Absolute paths: `~` expanded and `.` resolved. */
|
|
207
|
+
spec: SandboxSpec & { cwd: string };
|
|
208
|
+
/** The engine-minted directory holding its homes. */
|
|
209
|
+
directory: string;
|
|
210
|
+
/** The git directories its paths belong to, reachable beyond `spec`. */
|
|
211
|
+
gitdirs: Gitdir[];
|
|
212
|
+
/** Every domain reachable in the end: `network` plus the admitted harnesses' model domains. */
|
|
213
|
+
domains: Domain[];
|
|
214
|
+
/**
|
|
215
|
+
* What only its provider knew about how it ran, in the provider's own shape, as `spec` holds its
|
|
216
|
+
* settings under its key: docker's `image`, the digest the box ran; srt's `toolchain`, the host
|
|
217
|
+
* paths it re-allowed beyond reach.
|
|
218
|
+
*/
|
|
219
|
+
provided?: JsonObject;
|
|
220
|
+
/** Every agent admitted, including one whose turns never completed. */
|
|
221
|
+
agents: { callPath: string[]; agent: string; home: string }[];
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/** Where a pane agent's pane was placed, and why not where its layout said, for the record. */
|
|
225
|
+
export type PanePlacement = {
|
|
226
|
+
/** The terminal session it is in. */
|
|
227
|
+
session: string;
|
|
228
|
+
workspace: PaneWorkspace;
|
|
229
|
+
/** The new tab's label; absent for a pane beside another. */
|
|
230
|
+
tab?: string;
|
|
231
|
+
/** The agent whose pane it split. */
|
|
232
|
+
beside?: string;
|
|
233
|
+
/** Why its layout was not used, where it wasn't. */
|
|
234
|
+
fallback?: string;
|
|
235
|
+
/** Left open when the agent was done, its harness running. */
|
|
236
|
+
kept?: true;
|
|
237
|
+
/** Why a pane asked to be kept was closed instead. */
|
|
238
|
+
notKept?: string;
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
/** Where a pane agent's pane went: its layout as the workflow wrote it, and where it was placed. */
|
|
242
|
+
export type AgentPaneRecord = {
|
|
243
|
+
callPath: string[];
|
|
244
|
+
agent: string;
|
|
245
|
+
layout?: PaneLayout;
|
|
246
|
+
keepPane?: KeepPane;
|
|
247
|
+
placed: PanePlacement;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
/** One skill an agent was given, as it was copied to it. */
|
|
251
|
+
export type SkillRecord = {
|
|
252
|
+
/** The name in its `SKILL.md`, and the directory it was copied to. */
|
|
253
|
+
name: string;
|
|
254
|
+
/** As the workflow named it, a URL made a path. */
|
|
255
|
+
source: SkillSource;
|
|
256
|
+
/** The commit a `repo` source resolved to. */
|
|
257
|
+
commit?: string;
|
|
258
|
+
/** Where in the repository it was found. */
|
|
259
|
+
within?: string;
|
|
260
|
+
/** `sha256:` over the copied tree: each file's path, whether it is executable, and its bytes. */
|
|
261
|
+
digest: string;
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
/** The skills an agent was given. */
|
|
265
|
+
export type AgentSkillsRecord = {
|
|
266
|
+
callPath: string[];
|
|
267
|
+
agent: string;
|
|
268
|
+
/**
|
|
269
|
+
* `operator` for an agent on the host the workflow named none for, which had the operator's. One
|
|
270
|
+
* in a sandbox had none of them: `[]`.
|
|
271
|
+
*/
|
|
272
|
+
skills: "operator" | SkillRecord[];
|
|
273
|
+
/** The harness home of its own it ran with on the host, when its harness needs one for skills. */
|
|
274
|
+
home?: string;
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
export const RUN_RECORD_VERSION = 1 as const;
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* `run.json`: what a run is, written once as its folder is claimed. A run's status, current stage
|
|
281
|
+
* and last attempt are read off its other files, so none of them is here to go stale.
|
|
282
|
+
*/
|
|
283
|
+
export type RunRecord = {
|
|
284
|
+
version: typeof RUN_RECORD_VERSION;
|
|
285
|
+
/** Unique within its workflow; its folder's name. */
|
|
286
|
+
id: string;
|
|
287
|
+
/** The workflow's `meta.name`, never its file. */
|
|
288
|
+
workflow: string;
|
|
289
|
+
/** As given, not as parsed: a continue prepares it again with the code as it is then. */
|
|
290
|
+
argv: string[];
|
|
291
|
+
/** Where every attempt works. */
|
|
292
|
+
cwd: string;
|
|
293
|
+
/** `awf run --sandbox`'s spec, as its file gave it, which every attempt runs in. */
|
|
294
|
+
sandbox: JsonValue | null;
|
|
295
|
+
created: string;
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
export const ATTEMPT_RECORD_VERSION = 1 as const;
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* `attempts/{attempt}.json`: one `awf run` of a run. Written whole when the attempt claims its number, and
|
|
302
|
+
* again when it ends, with its ending.
|
|
303
|
+
*/
|
|
304
|
+
export type AttemptRecord = {
|
|
305
|
+
version: typeof ATTEMPT_RECORD_VERSION;
|
|
306
|
+
attempt: number;
|
|
307
|
+
/** The file it ran, for the record; never identity. */
|
|
308
|
+
file: string;
|
|
309
|
+
/** The workflow's `meta.version`, when it gives one. */
|
|
310
|
+
workflowVersion?: string;
|
|
311
|
+
flags: { timeout: string; fromStage?: string };
|
|
312
|
+
/** With `processStart`, what makes the attempt checkably live: a pid alone may be reused. */
|
|
313
|
+
pid: number;
|
|
314
|
+
/** As `ps -o lstart=` gives it, as an ISO time to the second. */
|
|
315
|
+
processStart: string;
|
|
316
|
+
started: string;
|
|
317
|
+
ended?: string;
|
|
318
|
+
outcome?: AttemptOutcome;
|
|
319
|
+
/** The stage the attempt ended in; absent when it ended between stages. */
|
|
320
|
+
stage?: string;
|
|
321
|
+
/** Why it did not complete. */
|
|
322
|
+
reason?: string;
|
|
323
|
+
/** Each stage it entered that ran or was reused, in order. */
|
|
324
|
+
stages?: AttemptStage[];
|
|
325
|
+
/**
|
|
326
|
+
* What it cost, as far as a run's total needs: `output.json` holds the last attempt's in full,
|
|
327
|
+
* and turns carry no spend, so an earlier attempt's is kept here. Absent for an interrupted one.
|
|
328
|
+
*/
|
|
329
|
+
accounting?: AttemptAccounting;
|
|
330
|
+
/** The messaging ceilings in force; absent on attempts an earlier awf ran. */
|
|
331
|
+
messageLimits?: MessageLimits;
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
/** What an attempt's file keeps of its accounting: what a run's total sums. */
|
|
335
|
+
export type AttemptAccounting = Pick<
|
|
336
|
+
RunAccounting,
|
|
337
|
+
"basis" | "wallMs" | "grouping" | "billing" | "totals" | "byStage" | "unpriced"
|
|
338
|
+
>;
|
|
339
|
+
|
|
340
|
+
/** A stage an attempt entered, as its files record it: no value, which `stages/` holds. */
|
|
341
|
+
export type AttemptStage = Omit<StageSummary, "value">;
|
|
342
|
+
|
|
343
|
+
export const STAGE_RECORD_VERSION = 1 as const;
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* `stages/{stage}.json`: the run's current record of a stage, written whole when the stage ends,
|
|
347
|
+
* by the attempt that ran it.
|
|
348
|
+
*/
|
|
349
|
+
export type StageRecord = {
|
|
350
|
+
version: typeof STAGE_RECORD_VERSION;
|
|
351
|
+
stage: string;
|
|
352
|
+
/** The attempt that ran it. */
|
|
353
|
+
attempt: number;
|
|
354
|
+
outcome: StageOutcome;
|
|
355
|
+
/** Why it did not succeed. */
|
|
356
|
+
reason?: string;
|
|
357
|
+
started: string;
|
|
358
|
+
ended: string;
|
|
359
|
+
workflowVersion?: string;
|
|
360
|
+
/** The agents' native sessions its turns used. */
|
|
361
|
+
sessions: { agent: AgentKey; harness: HarnessKind; session: string }[];
|
|
362
|
+
/** The stage's own one line, from its `summary`. */
|
|
363
|
+
summary?: string;
|
|
364
|
+
/** As returned, after a JSON round trip; absent for a stage that returns nothing. */
|
|
365
|
+
value?: JsonValue;
|
|
366
|
+
/**
|
|
367
|
+
* Its value was given to start the run at a later stage, `awf run --values`, or it returns
|
|
368
|
+
* nothing and was passed: no turn ran it. Reused as a succeeded stage is.
|
|
369
|
+
*/
|
|
370
|
+
provided?: true;
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
/** A stage a run can't start at a later stage without: its value, which `--values` gives. */
|
|
374
|
+
export type StageNeed = { stage: string; schema: JsonSchema };
|
|
375
|
+
|
|
376
|
+
export const TURN_RECORD_VERSION = 1 as const;
|
|
377
|
+
|
|
378
|
+
/** What a line of `turns.jsonl` keeps of its operation's record. */
|
|
379
|
+
export const TURN_OPERATION_FIELDS = [
|
|
380
|
+
"agent",
|
|
381
|
+
"operationId",
|
|
382
|
+
"execution",
|
|
383
|
+
"stage",
|
|
384
|
+
"label",
|
|
385
|
+
"deliveredAt",
|
|
386
|
+
"settledAt",
|
|
387
|
+
"sessions",
|
|
388
|
+
"cause",
|
|
389
|
+
] as const satisfies readonly (keyof OperationRecord)[];
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* A line of `turns.jsonl`: a turn, nudges included, or a compaction, as it settled, with the attempt
|
|
393
|
+
* it was in. Its `sessions` are its agent's as of then: the last is the one it ran on. What it spent
|
|
394
|
+
* is read from its sessions once the attempt ends, so a line has none.
|
|
395
|
+
*/
|
|
396
|
+
export type TurnRecord = {
|
|
397
|
+
version: typeof TURN_RECORD_VERSION;
|
|
398
|
+
attempt: number;
|
|
399
|
+
kind: "turn" | "compact";
|
|
400
|
+
outcome: TurnOutcome<JsonValue>["kind"];
|
|
401
|
+
/** Absent on lines an earlier awf wrote. */
|
|
402
|
+
callPath?: string[];
|
|
403
|
+
} & Pick<OperationRecord, (typeof TURN_OPERATION_FIELDS)[number]>;
|
|
404
|
+
|
|
405
|
+
/** An operation's fields on its start or failure: a turn line's, but its settling. */
|
|
406
|
+
type OperationEvent = { of: TurnRecord["kind"]; callPath: string[] } & Pick<
|
|
407
|
+
OperationRecord,
|
|
408
|
+
Exclude<(typeof TURN_OPERATION_FIELDS)[number], "settledAt">
|
|
409
|
+
>;
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Every line of the run's event log but a settled turn's. Keyed, never given an id: a span's start
|
|
413
|
+
* and end carry the same keys (attempt, stage, `operationId`, decision), and the tree is read off
|
|
414
|
+
* them. Observation only: `--continue` never reads it (ADR 0011).
|
|
415
|
+
*/
|
|
416
|
+
export type RunEvent = {
|
|
417
|
+
/** Its kind's, from `RUN_EVENT_VERSIONS`. */
|
|
418
|
+
version: number;
|
|
419
|
+
attempt: number;
|
|
420
|
+
at: string;
|
|
421
|
+
} & (
|
|
422
|
+
| { kind: "attempt-started" }
|
|
423
|
+
| { kind: "attempt-ended"; outcome: AttemptOutcome }
|
|
424
|
+
| { kind: "stage-started"; stage: string }
|
|
425
|
+
/** A stage not run in this attempt, its value reused or given, writes only its end. */
|
|
426
|
+
| { kind: "stage-ended"; stage: string; outcome: StageOutcome; source?: "reused" | "provided" }
|
|
427
|
+
/** Written at delivery; its `sessions` name the native session when it is known by then. */
|
|
428
|
+
| ({ kind: "operation-started" } & OperationEvent)
|
|
429
|
+
/** May have no start: an operation can fail before it is delivered. */
|
|
430
|
+
| ({ kind: "operation-failed"; error: string } & OperationEvent)
|
|
431
|
+
| {
|
|
432
|
+
kind: "session-known";
|
|
433
|
+
session: NativeSessionRef;
|
|
434
|
+
/** A headless turn whose harness named its session while it ran. */
|
|
435
|
+
operationId?: string;
|
|
436
|
+
/** A fork, once its session exists. */
|
|
437
|
+
agent?: AgentKey;
|
|
438
|
+
}
|
|
439
|
+
| {
|
|
440
|
+
kind: "agent-opened";
|
|
441
|
+
agent: AgentKey;
|
|
442
|
+
callPath: string[];
|
|
443
|
+
harness: HarnessKind;
|
|
444
|
+
/** Where it runs, a sandbox's when in one: its native files are found by it. */
|
|
445
|
+
cwd: string;
|
|
446
|
+
/** A sandboxed agent's own harness home; absent for the operator's. */
|
|
447
|
+
home?: string;
|
|
448
|
+
/** The calling session (ADR 0010). */
|
|
449
|
+
caller?: true;
|
|
450
|
+
}
|
|
451
|
+
| { kind: "agent-stopped"; agent: AgentKey; callPath: string[]; harness: HarnessKind }
|
|
452
|
+
| {
|
|
453
|
+
kind: "agent-forked";
|
|
454
|
+
agent: AgentKey;
|
|
455
|
+
/** The agent it was copied from; null for a calling session the run never opened. */
|
|
456
|
+
from: AgentKey | null;
|
|
457
|
+
/** The parent's operation it was copied after; null when the parent ran nothing in this run. */
|
|
458
|
+
after: string | null;
|
|
459
|
+
}
|
|
460
|
+
| { kind: "log" | "warning"; text: string; fields?: JsonObject; stage?: string }
|
|
461
|
+
/** `decision` numbers it in the order asked within the attempt. */
|
|
462
|
+
| ({ kind: "decision-asked"; decision: number } & Pick<
|
|
463
|
+
DecisionRecord,
|
|
464
|
+
"stage" | "key" | "alias" | "provider" | "model"
|
|
465
|
+
>)
|
|
466
|
+
| ({ kind: "decision-ended"; decision: number } & Pick<
|
|
467
|
+
DecisionRecord,
|
|
468
|
+
"stage" | "outcome" | "error"
|
|
469
|
+
>)
|
|
470
|
+
| MessagingEvent
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* A route's ends, as the workflow granted it: `between` is one route both ways, with one count, its
|
|
475
|
+
* two ends in the order given, which means nothing. A pair has one open route at a time, so its ends
|
|
476
|
+
* name it.
|
|
477
|
+
*/
|
|
478
|
+
export type RouteEnds =
|
|
479
|
+
| { from: AgentKey; to: AgentKey; between?: never }
|
|
480
|
+
| { between: [AgentKey, AgentKey]; from?: never; to?: never };
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Why a message admitted was never shown: its sender stopped waiting before its peer saw it, its
|
|
484
|
+
* route closed, or the attempt ended.
|
|
485
|
+
*/
|
|
486
|
+
export type UndeliveredReason = "withdrawn" | "route-closed" | "attempt-ended";
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* How an owed answer ended: answered with `wf reply`; failed (its peer stopped, failed, or still owed
|
|
490
|
+
* it after its reminder); the asker's `--timeout` passed; or the asker finished first, before its
|
|
491
|
+
* peer saw the question (`withdrawn`) or after (`released`).
|
|
492
|
+
*/
|
|
493
|
+
export type RequestEnd = "replied" | "failed" | "timed-out" | "withdrawn" | "released";
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Messaging in the run's event log, keyed by attempt and `message`, numbered in the order admitted
|
|
497
|
+
* within the attempt. Written by the side that saw each: the engine on admission and its end, the
|
|
498
|
+
* delivery with what the harness confirmed.
|
|
499
|
+
*/
|
|
500
|
+
export type MessagingEvent =
|
|
501
|
+
| ({
|
|
502
|
+
kind: "route-opened";
|
|
503
|
+
callPath: string[];
|
|
504
|
+
purpose: string;
|
|
505
|
+
/** The limit in force, answers included, and what the workflow asked for when it differed. */
|
|
506
|
+
maxMessages: number;
|
|
507
|
+
asked?: number;
|
|
508
|
+
/** The stage it closes with; absent, it lasts the attempt. */
|
|
509
|
+
stage?: string;
|
|
510
|
+
} & RouteEnds)
|
|
511
|
+
| ({
|
|
512
|
+
kind: "route-closed";
|
|
513
|
+
callPath: string[];
|
|
514
|
+
reason: "stage-ended" | "agent-stopped" | "attempt-ended";
|
|
515
|
+
} & RouteEnds)
|
|
516
|
+
/**
|
|
517
|
+
* Admitted, before `wf send` or `wf reply` returned. `operationId` is the sender's open operation.
|
|
518
|
+
* A question carries the answer's deadline; an answer, the question it answers.
|
|
519
|
+
*/
|
|
520
|
+
| {
|
|
521
|
+
kind: "message-sent";
|
|
522
|
+
callPath: string[];
|
|
523
|
+
message: number;
|
|
524
|
+
from: AgentKey;
|
|
525
|
+
to: AgentKey;
|
|
526
|
+
operationId: string;
|
|
527
|
+
text: string;
|
|
528
|
+
bytes: number;
|
|
529
|
+
/** It awaits an answer by this time. */
|
|
530
|
+
awaitsReply?: { until: string };
|
|
531
|
+
/** The question it answers. */
|
|
532
|
+
replyTo?: number;
|
|
533
|
+
}
|
|
534
|
+
/** Refused before admission, with the code `wf` printed. */
|
|
535
|
+
| {
|
|
536
|
+
kind: "message-refused";
|
|
537
|
+
callPath: string[];
|
|
538
|
+
from: AgentKey;
|
|
539
|
+
to: AgentKey;
|
|
540
|
+
/** Absent for `no-operation`. */
|
|
541
|
+
operationId?: string;
|
|
542
|
+
command: "send" | "reply";
|
|
543
|
+
code: SendCode | ReplyCode;
|
|
544
|
+
bytes: number;
|
|
545
|
+
awaitReply?: true;
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Shown to its recipient in `operationId`: `offered` once typed or its resumed turn started, then
|
|
549
|
+
* `observed` where the harness confirmed the model read it, else `unconfirmed`.
|
|
550
|
+
*/
|
|
551
|
+
| {
|
|
552
|
+
kind: "message-delivery";
|
|
553
|
+
callPath: string[];
|
|
554
|
+
message: number;
|
|
555
|
+
from: AgentKey;
|
|
556
|
+
to: AgentKey;
|
|
557
|
+
operationId: string;
|
|
558
|
+
state: "offered" | "observed" | "unconfirmed";
|
|
559
|
+
}
|
|
560
|
+
| {
|
|
561
|
+
kind: "message-undelivered";
|
|
562
|
+
callPath: string[];
|
|
563
|
+
message: number;
|
|
564
|
+
from: AgentKey;
|
|
565
|
+
to: AgentKey;
|
|
566
|
+
reason: UndeliveredReason;
|
|
567
|
+
}
|
|
568
|
+
/** An owed answer's end. `message` is the question; `reply`, its answer's number. */
|
|
569
|
+
| {
|
|
570
|
+
kind: "request-ended";
|
|
571
|
+
callPath: string[];
|
|
572
|
+
message: number;
|
|
573
|
+
/** The agent that owed it, and the asker. */
|
|
574
|
+
owedBy: AgentKey;
|
|
575
|
+
asker: AgentKey;
|
|
576
|
+
end: RequestEnd;
|
|
577
|
+
reason?: string;
|
|
578
|
+
reply?: number;
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
/** The messaging ceilings an attempt ran under. A route's `maxMessages` may only lower `perRoute`. */
|
|
582
|
+
export type MessageLimits = {
|
|
583
|
+
/** Messages a route carries an attempt, answers included. */
|
|
584
|
+
perRoute: number;
|
|
585
|
+
/** A message's size in UTF-8 bytes. */
|
|
586
|
+
bytes: number;
|
|
587
|
+
/** Messages admitted for one agent and not yet shown to it. */
|
|
588
|
+
unread: number;
|
|
589
|
+
/** A message operation's own time, before its waiters' deadlines lengthen it. */
|
|
590
|
+
operationMs: number;
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* What an agent did, read from its native session files when asked and never copied into the run.
|
|
595
|
+
* A message is its length only, and a tool its name and a short argument, cut and with common
|
|
596
|
+
* credential shapes masked: the native files hold the rest.
|
|
597
|
+
*/
|
|
598
|
+
export type Activity =
|
|
599
|
+
| { type: "message"; from: "agent" | "user"; chars: number }
|
|
600
|
+
| {
|
|
601
|
+
type: "tool";
|
|
602
|
+
tool: string;
|
|
603
|
+
input?: string;
|
|
604
|
+
/** Pairs it with its result. */
|
|
605
|
+
call?: string;
|
|
606
|
+
/** It goes on running after the call returns. */
|
|
607
|
+
background?: true;
|
|
608
|
+
}
|
|
609
|
+
/** `exit` only where the harness logs one as data: codex's shell calls; claude logs none. */
|
|
610
|
+
| { type: "tool-result"; call?: string; error?: true; exit?: number }
|
|
611
|
+
/** A task started in the background said it ended: claude's task notification. */
|
|
612
|
+
| { type: "background-ended" }
|
|
613
|
+
| { type: "compaction" };
|
|
614
|
+
|
|
615
|
+
/** A line of `turns.jsonl`, the run's event log. */
|
|
616
|
+
export type RunLogLine = TurnRecord | RunEvent;
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* What `awf runs --json` and `awf logs --json` print: one document per call. A reader ignores the
|
|
620
|
+
* kinds and fields it does not know; `v` changes only when one is renamed or removed. Times are ISO.
|
|
621
|
+
*/
|
|
622
|
+
export type RunStateJson = "running" | "interrupted" | AttemptOutcome;
|
|
623
|
+
|
|
624
|
+
/** A run as both commands head it. `checked: false`: a `running` whose process was not asked. */
|
|
625
|
+
export type RunHeadJson = {
|
|
626
|
+
ref: string;
|
|
627
|
+
state: RunStateJson;
|
|
628
|
+
checked: boolean;
|
|
629
|
+
stage?: string;
|
|
630
|
+
/** Once the run has ended. */
|
|
631
|
+
duration?: number;
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
/** `awf runs --json`. */
|
|
635
|
+
export type RunsJson = {
|
|
636
|
+
v: 1;
|
|
637
|
+
runs: (
|
|
638
|
+
| {
|
|
639
|
+
ref: string;
|
|
640
|
+
state: RunStateJson;
|
|
641
|
+
checked: boolean;
|
|
642
|
+
started: string;
|
|
643
|
+
duration?: number;
|
|
644
|
+
estimate?: number;
|
|
645
|
+
}
|
|
646
|
+
| { ref: string; unreadable: string }
|
|
647
|
+
)[];
|
|
648
|
+
more: { older: boolean };
|
|
649
|
+
};
|
|
650
|
+
|
|
651
|
+
/** `awf runs {ref} --json`: durable fields, and best effort under each agent's `live`. */
|
|
652
|
+
export type RunJson = {
|
|
653
|
+
v: 1;
|
|
654
|
+
run: RunHeadJson & { estimate?: number; dir: string };
|
|
655
|
+
attempts: {
|
|
656
|
+
attempt: number;
|
|
657
|
+
started: string;
|
|
658
|
+
ended?: string;
|
|
659
|
+
state: RunStateJson;
|
|
660
|
+
stage?: string;
|
|
661
|
+
reason?: string;
|
|
662
|
+
}[];
|
|
663
|
+
stages: { stage: string; state: string; started?: string; ended?: string }[];
|
|
664
|
+
agents: {
|
|
665
|
+
key: AgentKey;
|
|
666
|
+
harness?: HarnessKind;
|
|
667
|
+
caller?: true;
|
|
668
|
+
stage?: string;
|
|
669
|
+
state: string;
|
|
670
|
+
turns: number;
|
|
671
|
+
sessions: { harness: HarnessKind; id: string; files: string[] }[];
|
|
672
|
+
live: {
|
|
673
|
+
/** `waiting` after `wf waiting`; `awaiting-reply` on `peer`'s answer. */
|
|
674
|
+
phase?: "waiting" | "awaiting-reply";
|
|
675
|
+
reason?: string;
|
|
676
|
+
peer?: AgentKey;
|
|
677
|
+
until?: string;
|
|
678
|
+
lastActivity?: { activity: Activity; at: string };
|
|
679
|
+
observedAt: string;
|
|
680
|
+
};
|
|
681
|
+
}[];
|
|
682
|
+
decisions: { decision: number; key: string; asked: string; outcome?: string }[];
|
|
683
|
+
cursor: string;
|
|
684
|
+
/** While it runs. */
|
|
685
|
+
next?: string[];
|
|
686
|
+
/** Where it can go on. */
|
|
687
|
+
continue?: string;
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
/** An event as `awf logs --json` publishes it, with the keys that place it. */
|
|
691
|
+
export type LogEventJson = { handle: string; at: string } & (
|
|
692
|
+
| {
|
|
693
|
+
kind: "activity";
|
|
694
|
+
/** `null` between the agent's operations. */
|
|
695
|
+
operation: string | null;
|
|
696
|
+
agent?: AgentKey;
|
|
697
|
+
stage?: string;
|
|
698
|
+
activity: Activity;
|
|
699
|
+
/** The native text: the message, the tool's whole argument, or its output. */
|
|
700
|
+
text?: string;
|
|
701
|
+
}
|
|
702
|
+
| (DistributiveOmit<RunLogLine, "version"> & { operation?: string })
|
|
703
|
+
);
|
|
704
|
+
|
|
705
|
+
type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* `awf logs --json`. In a page, a string the run wrote is cut at 50,000 characters; a read of one
|
|
709
|
+
* `@handle` gives it whole, and has no `cursor`, `more` or `next`: it pages nothing.
|
|
710
|
+
*/
|
|
711
|
+
export type LogsJson = {
|
|
712
|
+
v: 1;
|
|
713
|
+
run: RunHeadJson & { ambiguous?: boolean };
|
|
714
|
+
events: LogEventJson[];
|
|
715
|
+
cursor?: string;
|
|
716
|
+
more?: { older?: true; newer?: true };
|
|
717
|
+
/** While the run goes on, or while the page stopped before the newest event. */
|
|
718
|
+
next?: string[];
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
/** Each kind's own version, which changes when that kind's shape does. */
|
|
722
|
+
export const RUN_EVENT_VERSIONS = {
|
|
723
|
+
"attempt-started": 1,
|
|
724
|
+
"attempt-ended": 1,
|
|
725
|
+
"stage-started": 1,
|
|
726
|
+
"stage-ended": 1,
|
|
727
|
+
"operation-started": 1,
|
|
728
|
+
"operation-failed": 1,
|
|
729
|
+
"session-known": 1,
|
|
730
|
+
"agent-opened": 1,
|
|
731
|
+
"agent-stopped": 1,
|
|
732
|
+
"agent-forked": 1,
|
|
733
|
+
log: 1,
|
|
734
|
+
warning: 1,
|
|
735
|
+
"decision-asked": 1,
|
|
736
|
+
"decision-ended": 1,
|
|
737
|
+
"route-opened": 1,
|
|
738
|
+
"route-closed": 1,
|
|
739
|
+
"message-sent": 1,
|
|
740
|
+
"message-refused": 1,
|
|
741
|
+
"message-delivery": 1,
|
|
742
|
+
"message-undelivered": 1,
|
|
743
|
+
"request-ended": 1,
|
|
744
|
+
} as const satisfies Record<RunEvent["kind"], number>;
|
|
745
|
+
|
|
746
|
+
/**
|
|
747
|
+
* The line as this reader knows it, or undefined for a kind or version it does not know, which
|
|
748
|
+
* every reader skips so that a later awf can add kinds.
|
|
749
|
+
*/
|
|
750
|
+
export function runLogLine(value: object): RunLogLine | undefined {
|
|
751
|
+
const { kind, version, attempt } = value as Record<string, unknown>;
|
|
752
|
+
if (typeof attempt !== "number") return undefined;
|
|
753
|
+
// The first turn lines, written for a day before turns had a kind, were all turns.
|
|
754
|
+
if (kind === "turn" || kind === "compact" || kind === undefined) {
|
|
755
|
+
return version === TURN_RECORD_VERSION ? ({ kind: "turn", ...value } as TurnRecord) : undefined;
|
|
756
|
+
}
|
|
757
|
+
if (typeof kind !== "string") return undefined;
|
|
758
|
+
return Object.hasOwn(RUN_EVENT_VERSIONS, kind) &&
|
|
759
|
+
version === RUN_EVENT_VERSIONS[kind as RunEvent["kind"]]
|
|
760
|
+
? (value as RunEvent)
|
|
761
|
+
: undefined;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
export const OUTPUT_RECORD_VERSION = 5 as const;
|
|
765
|
+
|
|
766
|
+
/**
|
|
767
|
+
* An attempt, as the operator CLI keeps it in `output.json` and prints it with `--json`, in the
|
|
768
|
+
* words of its attempt file. One that didn't complete keeps what it spent too; only a completed one
|
|
769
|
+
* has a value.
|
|
770
|
+
*/
|
|
771
|
+
export type OutputRecord = {
|
|
772
|
+
version: typeof OUTPUT_RECORD_VERSION;
|
|
773
|
+
/** The run's id, the same in every attempt. */
|
|
774
|
+
runId: string;
|
|
775
|
+
/** The attempt that wrote this record: `output.json` is the last ended attempt's. */
|
|
776
|
+
attempt: number;
|
|
777
|
+
workflow: { name: string; file: string };
|
|
778
|
+
accounting: RunAccounting;
|
|
779
|
+
usage: SettledOperation[];
|
|
780
|
+
/** The run's folder. */
|
|
781
|
+
artifacts: string;
|
|
782
|
+
/**
|
|
783
|
+
* Each sandbox the run opened, once. Absent when it opened none. Readers must not switch
|
|
784
|
+
* exhaustively over a sandbox's `provider`.
|
|
785
|
+
*/
|
|
786
|
+
sandboxes?: SandboxRecord[];
|
|
787
|
+
/** Each agent's skills, once per agent, including one that never completed a turn. */
|
|
788
|
+
skills?: AgentSkillsRecord[];
|
|
789
|
+
/** Where each pane agent's pane went, once per agent. Absent when the run placed none. */
|
|
790
|
+
panes?: AgentPaneRecord[];
|
|
791
|
+
/** Every decision the run asked, in the order asked. Absent when it asked none. */
|
|
792
|
+
decisions?: SettledDecision[];
|
|
793
|
+
/** Each stage the attempt entered, as its attempt file has them; absent when none. */
|
|
794
|
+
stages?: AttemptStage[];
|
|
795
|
+
/** The workflow's Markdown report, when it wrote one. */
|
|
796
|
+
report?: string;
|
|
797
|
+
} & (
|
|
798
|
+
| { outcome: "completed"; value: JsonValue }
|
|
799
|
+
| {
|
|
800
|
+
outcome: UnfinishedOutcome;
|
|
801
|
+
reason: string;
|
|
802
|
+
/** The stage the attempt ended in; absent when it ended between stages. */
|
|
803
|
+
stage?: string;
|
|
804
|
+
/** The stages whose values it stopped for, to start at a later one, in the order reached. */
|
|
805
|
+
needs?: StageNeed[];
|
|
806
|
+
}
|
|
807
|
+
);
|
|
808
|
+
|
|
809
|
+
export const ALLOWANCE_VERSION = "awf.allowance/1";
|
|
810
|
+
|
|
811
|
+
/** What `awf allowance --json` prints: each harness's plan, as the harness itself showed it. */
|
|
812
|
+
export type AllowanceReport = {
|
|
813
|
+
version: typeof ALLOWANCE_VERSION;
|
|
814
|
+
/** When the reads started, ISO 8601. */
|
|
815
|
+
readAt: string;
|
|
816
|
+
harnesses: HarnessAllowance[];
|
|
817
|
+
};
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* One harness's plan, or why it has none to read: no reader, no login, a login that is billed per
|
|
821
|
+
* token, or a read that failed. A `none` is unknown, not room to spare.
|
|
822
|
+
*/
|
|
823
|
+
export type HarnessAllowance =
|
|
824
|
+
| {
|
|
825
|
+
harness: HarnessKind;
|
|
826
|
+
read: "plan";
|
|
827
|
+
/** What was read: `claude /usage`, `codex account/rateLimits/read`, `cursor /usage`. */
|
|
828
|
+
source: string;
|
|
829
|
+
/** The plan's name, as the harness words it: `max`, `prolite`, `Team`. */
|
|
830
|
+
plan?: string;
|
|
831
|
+
/** Its tier within the plan, where the harness tells one apart: `default_claude_max_20x`. */
|
|
832
|
+
tier?: string;
|
|
833
|
+
windows: AllowanceWindow[];
|
|
834
|
+
}
|
|
835
|
+
| { harness: HarnessKind; read: "none"; reason: string };
|
|
836
|
+
|
|
837
|
+
/** A limit of the plan's, over a window that resets. Nothing the harness did not show is filled in. */
|
|
838
|
+
export type AllowanceWindow = {
|
|
839
|
+
/** Named from the harness's wording, unique within it: `session`, `week`, `included`. */
|
|
840
|
+
id: string;
|
|
841
|
+
/** As the harness words it. */
|
|
842
|
+
label: string;
|
|
843
|
+
usedPercent: number;
|
|
844
|
+
/** When it resets, ISO 8601 in UTC; as precise as the harness shows it. */
|
|
845
|
+
resetsAt?: string;
|
|
846
|
+
/** The models it alone limits, as the harness names them; absent, it limits every model. */
|
|
847
|
+
models?: string[];
|
|
848
|
+
};
|
|
849
|
+
|
|
850
|
+
export const OPERATION_LIVENESS_RECORD_VERSION = 1 as const;
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* Messaging's kinds: `awaiting-reply`, a turn ended with its operation awaiting `peer`'s answer until
|
|
854
|
+
* `until`; `continued`, a delivery went on with it, `reason` saying what (`reply`, `no-reply`,
|
|
855
|
+
* `request`, `messages`); `reminder-due`, a turn ended owing an answer; `deadline-extended`, a
|
|
856
|
+
* message operation's deadline moved later, to `until`.
|
|
857
|
+
*/
|
|
858
|
+
export const OPERATION_LIVENESS_KINDS = [
|
|
859
|
+
"opened",
|
|
860
|
+
"waiting",
|
|
861
|
+
"admitted",
|
|
862
|
+
"persisted",
|
|
863
|
+
"dispatched",
|
|
864
|
+
"queue-accepted",
|
|
865
|
+
"received",
|
|
866
|
+
"check-in-due",
|
|
867
|
+
"awaiting-reply",
|
|
868
|
+
"continued",
|
|
869
|
+
"reminder-due",
|
|
870
|
+
"deadline-extended",
|
|
871
|
+
"releasing",
|
|
872
|
+
"release",
|
|
873
|
+
"terminal",
|
|
874
|
+
] as const;
|
|
875
|
+
export type OperationLivenessKind = (typeof OPERATION_LIVENESS_KINDS)[number];
|
|
876
|
+
|
|
877
|
+
/** Optional diagnostics in calls/{operationId}/liveness.jsonl; never settlement authority. */
|
|
878
|
+
export type OperationLivenessRecord = {
|
|
879
|
+
version: typeof OPERATION_LIVENESS_RECORD_VERSION;
|
|
880
|
+
operationId: string;
|
|
881
|
+
kind: OperationLivenessKind;
|
|
882
|
+
/** Unix milliseconds, sampled by the engine. */
|
|
883
|
+
at: number;
|
|
884
|
+
/** Check-in ordinal: zero is the initial prompt; several events share an ordinal. */
|
|
885
|
+
sequence: number;
|
|
886
|
+
reason?: string;
|
|
887
|
+
until?: number;
|
|
888
|
+
/** On `awaiting-reply`, whose answer the operation awaits. */
|
|
889
|
+
peer?: AgentKey;
|
|
890
|
+
/** Events omitted since the previous successfully written record. */
|
|
891
|
+
dropped?: number;
|
|
892
|
+
truncated?: true;
|
|
893
|
+
};
|