@agentswf/engine 0.1.0-beta.5 → 0.1.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.
Files changed (122) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +16 -0
  3. package/package.json +13 -9
  4. package/src/accounting/format.ts +4 -3
  5. package/src/accounting/spent.ts +128 -0
  6. package/src/accounting/summary.ts +40 -32
  7. package/src/agent-cli/cli.ts +28 -55
  8. package/src/agent-cli/client.ts +1 -27
  9. package/src/allowance-command.ts +8 -10
  10. package/src/attempt-ending.ts +4 -4
  11. package/src/attempt-output.ts +13 -31
  12. package/src/attempt-view.ts +33 -32
  13. package/src/author-typebox.ts +13 -0
  14. package/src/client/checks.ts +94 -0
  15. package/src/client/exported.ts +17 -0
  16. package/src/client/index.ts +122 -0
  17. package/src/client/locate.ts +34 -0
  18. package/src/client/published.ts +3 -0
  19. package/src/client/runs.ts +337 -0
  20. package/src/command-line.ts +64 -0
  21. package/src/commands.ts +7 -1
  22. package/src/control-plane.ts +4 -6
  23. package/src/coordinator-prompt.ts +4 -3
  24. package/src/coordinator-socket.ts +27 -28
  25. package/src/deadlines.ts +6 -40
  26. package/src/decisions/directory.ts +50 -37
  27. package/src/decisions/fake.ts +1 -1
  28. package/src/decisions/openrouter.ts +4 -2
  29. package/src/decisions/seam.ts +4 -4
  30. package/src/display-path.ts +3 -3
  31. package/src/duration.ts +19 -33
  32. package/src/emit-command.ts +38 -22
  33. package/src/emit.ts +148 -59
  34. package/src/event-ids.ts +31 -0
  35. package/src/files.ts +44 -4
  36. package/src/hand-over.ts +150 -0
  37. package/src/help/allowance.txt +4 -3
  38. package/src/help/emit.txt +11 -7
  39. package/src/help/inbox.txt +5 -3
  40. package/src/help/logs.txt +7 -5
  41. package/src/help/open.txt +29 -0
  42. package/src/help/reply.txt +6 -8
  43. package/src/help/run.txt +13 -8
  44. package/src/help/runs.txt +4 -2
  45. package/src/help/tick.txt +9 -4
  46. package/src/herdr-placement.ts +3 -33
  47. package/src/herdr-run-session.ts +5 -13
  48. package/src/herdr-workspace-marks.ts +7 -17
  49. package/src/holder-command.ts +54 -136
  50. package/src/in-lines.ts +28 -0
  51. package/src/inbox-command.ts +42 -56
  52. package/src/index.ts +1 -1
  53. package/src/init-command.ts +8 -17
  54. package/src/init-template/hello/workflow.ts.template +3 -2
  55. package/src/jsonl.ts +10 -6
  56. package/src/keep-alive.ts +7 -19
  57. package/src/logs-command.ts +134 -53
  58. package/src/message-operation.ts +6 -3
  59. package/src/messaging/messaging.ts +8 -10
  60. package/src/messaging/text.ts +2 -2
  61. package/src/open-command.ts +770 -0
  62. package/src/operation-events.ts +4 -6
  63. package/src/operation-liveness.ts +19 -4
  64. package/src/operator-cli.ts +206 -90
  65. package/src/operator-runtime.ts +24 -2
  66. package/src/ops.ts +270 -0
  67. package/src/pane-layout.ts +49 -96
  68. package/src/progress-view.ts +2 -2
  69. package/src/project-engine.ts +79 -0
  70. package/src/projects.ts +13 -2
  71. package/src/refusals.ts +119 -0
  72. package/src/result-slots.ts +11 -31
  73. package/src/result-validation.ts +2 -2
  74. package/src/run-command.ts +27 -31
  75. package/src/run-commands.ts +42 -114
  76. package/src/run-event-log.ts +1 -1
  77. package/src/run-lines.ts +159 -0
  78. package/src/run-lock.ts +57 -1
  79. package/src/run-policy/stage-plan.ts +154 -0
  80. package/src/run-policy/stages.ts +113 -0
  81. package/src/run-policy/waits.ts +452 -0
  82. package/src/run-prepare.ts +165 -41
  83. package/src/run-read.ts +109 -41
  84. package/src/run-state.ts +170 -50
  85. package/src/run-text.ts +54 -0
  86. package/src/run-unreachable.ts +18 -0
  87. package/src/run-usage.ts +17 -11
  88. package/src/run-view.ts +73 -14
  89. package/src/runs-command.ts +81 -63
  90. package/src/runs.ts +284 -222
  91. package/src/sandbox-homes.ts +65 -13
  92. package/src/sandboxes.ts +182 -28
  93. package/src/session-lines.ts +93 -0
  94. package/src/skills/run-skills.ts +8 -1
  95. package/src/sources.ts +2 -39
  96. package/src/spend.ts +333 -0
  97. package/src/stage-ledger.ts +111 -153
  98. package/src/stop-crashed.ts +6 -5
  99. package/src/stopped.ts +4 -4
  100. package/src/test-command.ts +37 -19
  101. package/src/testing/operation-events.ts +3 -23
  102. package/src/testing.ts +66 -18
  103. package/src/tick-command.ts +145 -15
  104. package/src/tools.ts +11 -30
  105. package/src/turn-ends.ts +37 -0
  106. package/src/waits.ts +651 -0
  107. package/src/wake.ts +84 -54
  108. package/src/workflow-loader.ts +49 -2
  109. package/src/workflow-runner.ts +525 -618
  110. package/src/workflow-test-preload.ts +4 -1
  111. package/src/workflow-testing/coordinators.ts +1 -1
  112. package/src/workflow-testing/fake-client.ts +387 -0
  113. package/src/workflow-testing/host.ts +28 -29
  114. package/src/workflow-testing/index.ts +9 -27
  115. package/src/workflow-testing/run-test.ts +201 -286
  116. package/src/workflow-testing/script.ts +7 -39
  117. package/src/deprecations.ts +0 -177
  118. package/src/hold-run-lock.fixture.ts +0 -8
  119. package/src/mailbox.ts +0 -972
  120. package/src/stage-plan.ts +0 -186
  121. package/src/workflow-testing/coordinator.typecheck.ts +0 -54
  122. package/src/workflow-testing/workflow-testing.typecheck.ts +0 -67
@@ -15,7 +15,6 @@ import type {
15
15
  } from "@agentswf/contract/workflow";
16
16
  import type Type from "typebox";
17
17
  import { canonical } from "../canonical-json";
18
- import type { Deprecated } from "../deprecations";
19
18
  import { messageOf } from "../errors";
20
19
 
21
20
  /** One turn of a scripted agent, as the workflow wrote it. */
@@ -84,8 +83,6 @@ export type SendOptions = {
84
83
  awaitReply?: boolean;
85
84
  /** How long an awaited reply is waited for, as `wfa send --timeout` gives it. */
86
85
  timeout?: Duration;
87
- /** @deprecated Give `timeout`, a `Duration` such as `"30s"`. */
88
- timeoutMs?: number;
89
86
  };
90
87
 
91
88
  /** How a send or reply ended; a refusal is a value to test, not a throw. */
@@ -106,10 +103,6 @@ export type ReceivedMessage = Message & {
106
103
  reply(text: string, options?: SendOptions): Promise<MessageOutcome>;
107
104
  send(peer: string, text: string, options?: SendOptions): Promise<MessageOutcome>;
108
105
  tool(name: string, input?: JsonObject): Promise<ToolOutcome>;
109
- /** @deprecated Return nothing, which ends the turn without a reply. */
110
- silent(): NoAnswer;
111
- /** @deprecated Return `noAnswer.failed(reason)`. */
112
- fail(reason?: string): NoAnswer;
113
106
  };
114
107
 
115
108
  /**
@@ -124,10 +117,10 @@ export type MessageScript = (
124
117
  ) => void | NoAnswer | MessageOutcome | Promise<void | NoAnswer | MessageOutcome>;
125
118
 
126
119
  /**
127
- * How a participant's script's `reply` or `send` ended, as `awf reply --json` reports it: a reply's
128
- * `id` is the question's, a send's the new message's.
120
+ * How a participant's script's `reply` or `send` ended, as `awf reply --json` prints it without the
121
+ * `v`: delivered, `id` a reply's question or a send's new message, or refused with its `code`.
129
122
  */
130
- export type ParticipantOutcome = { code: "delivered"; id: string } | { code: ParticipantCode };
123
+ export type ParticipantOutcome = { id: string } | { code: ParticipantCode };
131
124
 
132
125
  /**
133
126
  * A message an agent sent a participant, and what the participant's script may do about it, as
@@ -185,7 +178,7 @@ type Ending =
185
178
 
186
179
  /** A turn ended without an answer. Made by `noAnswer`. */
187
180
  export type NoAnswer = {
188
- readonly [ENTRY]: { kind: "no-answer"; ending: Ending; deprecated?: Deprecated };
181
+ readonly [ENTRY]: { kind: "no-answer"; ending: Ending };
189
182
  };
190
183
 
191
184
  /** Every turn the agent is asked, alike; or a list, one entry per turn in order. */
@@ -219,13 +212,7 @@ export function answer(...args: [unknown] | [unknown, unknown]): Answer {
219
212
  return { [ENTRY]: { kind: "answer", schema: schema as OutputSchema | undefined, respond } };
220
213
  }
221
214
 
222
- const ending = (value: Ending, deprecated?: Deprecated): NoAnswer => ({
223
- [ENTRY]: {
224
- kind: "no-answer",
225
- ending: value,
226
- ...(deprecated === undefined ? {} : { deprecated }),
227
- },
228
- });
215
+ const ending = (value: Ending): NoAnswer => ({ [ENTRY]: { kind: "no-answer", ending: value } });
229
216
 
230
217
  /** Ends a turn without an answer, so it takes no schema. */
231
218
  export const noAnswer = {
@@ -253,25 +240,6 @@ export const noAnswer = {
253
240
  interrupted: (): NoAnswer => ending({ kind: "interrupted" }),
254
241
  };
255
242
 
256
- /** What `make` ends a turn with, marked as made through a deprecated name for the run to log. */
257
- export function deprecatedAs<A extends unknown[]>(
258
- name: Deprecated,
259
- make: (...args: A) => NoAnswer,
260
- ): (...args: A) => NoAnswer {
261
- return (...args) => ending(make(...args)[ENTRY].ending, name);
262
- }
263
-
264
- /** @deprecated Use `noAnswer`, the same calls: a reply answers, these end a turn without one. */
265
- export const reply: typeof noAnswer = {
266
- silent: deprecatedAs("test-reply", noAnswer.silent),
267
- blocked: deprecatedAs("test-reply", noAnswer.blocked),
268
- failed: deprecatedAs("test-reply", noAnswer.failed),
269
- needsLogin: deprecatedAs("test-reply", noAnswer.needsLogin),
270
- timedOut: deprecatedAs("test-reply", noAnswer.timedOut),
271
- hang: deprecatedAs("test-reply", noAnswer.hang),
272
- interrupted: deprecatedAs("test-reply", noAnswer.interrupted),
273
- };
274
-
275
243
  /**
276
244
  * Reports a cooperative wait, `wfa waiting`; later check-ins invoke the same answer function again.
277
245
  * Only the engine's own tests use it: whether an agent may wait is its harness's, not a workflow's.
@@ -306,7 +274,7 @@ export function outcomeOf(kind: Exclude<Ending["kind"], "waiting">): TestTurnOut
306
274
 
307
275
  export type Step =
308
276
  | { kind: "answer"; value: JsonValue }
309
- | { kind: "no-answer"; ending: Ending; deprecated?: Deprecated }
277
+ | { kind: "no-answer"; ending: Ending }
310
278
  /** The script cannot meet this turn: the test's mistake, not the workflow's. */
311
279
  | { kind: "script-error"; message: string };
312
280
 
@@ -550,7 +518,7 @@ function globMatches(pattern: string, key: string): boolean {
550
518
  return new RegExp(`^${escaped.join(".*")}$`).test(key);
551
519
  }
552
520
 
553
- export function firstLine(text: string): string {
521
+ function firstLine(text: string): string {
554
522
  const line = text.split("\n", 1)[0]!;
555
523
  return line.length > 100 ? `${line.slice(0, 100)}…` : line;
556
524
  }
@@ -1,177 +0,0 @@
1
- /**
2
- * Every name deprecated before 0.1.0, with what replaces it: each is logged once per attempt it is
3
- * used in, so a continued or woken run logs it again, and removed one beta after the beta that
4
- * deprecated it (story 039).
5
- */
6
- export const DEPRECATIONS = {
7
- labels: {
8
- old: "an agent's labels option",
9
- instead: "drop them: nothing reads an agent's labels",
10
- },
11
- whenToUse: { old: "meta.whenToUse", instead: "drop it: nothing reads it" },
12
- "stage-receive": {
13
- old: "a stage around wf.receive()",
14
- instead: "wait with wf.race(name, …), which is recorded under its name, outside a stage",
15
- },
16
- isAnswered: { old: "isAnswered(outcome)", instead: "check outcome.answered" },
17
- defineExecutableWorkflow: {
18
- old: "defineExecutableWorkflow",
19
- instead: "use defineWorkflow, with meta and run beside prepare",
20
- },
21
- "call-id": {
22
- old: "the id of one run, start, compact or set",
23
- instead:
24
- "drop it: a later attempt doesn't know it, and a stage keeps a call from running twice",
25
- },
26
- "turn-source": {
27
- old: "a started turn as a wait's branch",
28
- instead: "name the agent, { done: agent }, which has at most one started turn",
29
- },
30
- "turn-is": { old: "turn.is(e)", instead: "use agent.is(e)" },
31
- "test-reply": {
32
- old: "reply.… in a test's script",
33
- instead: "use noAnswer.…, the same calls: a reply answers, these end a turn without an answer",
34
- },
35
- "message-silent": {
36
- old: "message.silent() in a test's message script",
37
- instead: "return nothing, which ends the turn without a reply",
38
- },
39
- "message-fail": {
40
- old: "message.fail() in a test's message script",
41
- instead: "return noAnswer.failed(reason), which fails the turn as a failed harness does",
42
- },
43
- "test-event-id": {
44
- old: "an event's id in a test",
45
- instead: "give it as eventId, as awf emit --event-id does",
46
- },
47
- "run-stopped": { old: "run.stopped", instead: 'read run.ending, whose kind is "stopped"' },
48
- "run-parked": { old: "run.parked", instead: 'read run.ending, whose kind is "parked"' },
49
- "colon-key": {
50
- old: "a key with a colon",
51
- instead: (key: string) =>
52
- `${key === "" ? "use - in its place" : `name it ${key.replaceAll(":", "-")}`}: a colon is kept for awf's own event names`,
53
- },
54
- "agents-stop": {
55
- old: "wf.agents.stop(key)",
56
- instead: "call close(reason?) on the agent's ref",
57
- },
58
- "fork-runtime": {
59
- old: "a fork's own placement, metered or effort",
60
- instead:
61
- "give them in its runtime, as open does: fork({ key, runtime: { placement, metered, effort } })",
62
- },
63
- "timeout-ms": {
64
- old: "timeoutMs",
65
- instead: 'give timeout, a Duration such as "90s" or "10m"',
66
- },
67
- "deadline-option": {
68
- old: "a deadline: { unixMilliseconds } option",
69
- instead: 'give timeout: a Date, or a Duration such as "20m"',
70
- },
71
- "nudge-deadline": {
72
- old: "nudge.deadline",
73
- instead: "drop it: the turn's timeout bounds its check-ins",
74
- },
75
- "keep-alive-timeout-ms": {
76
- old: "keepAlive.timeoutMs",
77
- instead: 'give keepAlive.window, a Duration such as "90s"',
78
- },
79
- "run-deadline-ms": {
80
- old: "wf.deadline.unixMilliseconds",
81
- instead: "wf.deadline is a Date: read wf.deadline.getTime()",
82
- },
83
- "stall-ms": { old: "a test's stallMs", instead: 'give stall, a Duration such as "2s"' },
84
- "test-args": {
85
- old: "testWorkflow(workflow, args, options)",
86
- instead: "give args among the options: testWorkflow(workflow, { args, … })",
87
- },
88
- "test-recorded": { old: "a test's recorded", instead: "give it as earlier: { stages }" },
89
- "test-mailbox": { old: "a test's mailbox", instead: "give it as earlier: { mailbox }" },
90
- "to-code": {
91
- old: 'to: "code"',
92
- instead: (key: string) =>
93
- `declare "${key}:reply": ParticipantReply in events and connect it without to: the code takes its messages as ${key}:reply`,
94
- },
95
- "undeclared-ended": {
96
- old: "start() for an agent whose end isn't declared",
97
- instead: (key: string) => {
98
- const named = key.replaceAll(":", "-");
99
- const declare = `declare "${named}:ended": AgentEnded in events: its end then arrives as ${named}:ended, not agent:ended`;
100
- return key.includes(":")
101
- ? `rename the key ${named}, which can name its end, and ${declare}`
102
- : declare;
103
- },
104
- },
105
- "participant-is": {
106
- old: "is(e) on a participant",
107
- instead: 'read e.name === "{key}:reply", declared in events',
108
- },
109
- "test-participant-reply": {
110
- old: 'a scripted { name: "participant:reply", from } in a test',
111
- instead: 'name the declared event, { name: "{key}:reply", data }',
112
- },
113
- "send-event-id": {
114
- old: "awf send --event-id",
115
- instead:
116
- 'use awf emit {run} {key}:reply \'{"text":…,"author":…}\' --event-id {id}, with {key}:reply declared in the workflow\'s events',
117
- },
118
- "pane-layout": {
119
- old: "an agent's layout",
120
- instead:
121
- "give pane: { tab, front } or pane: { beside, side, front }; the workspace is the operator's, awf run --workspace, and the session AWF_HERDR_SESSION",
122
- },
123
- "keep-pane": {
124
- old: "an agent's keepPane",
125
- instead: "drop it: keeping panes is the operator's, awf run --keep-panes",
126
- },
127
- "duration-ms": {
128
- old: "a duration in ms",
129
- instead: (value: string) =>
130
- `give ${Number.parseInt(value, 10) / 1000}s: a duration is a decimal and s, m, h or d`,
131
- },
132
- "wait-seconds": {
133
- old: "a bare number for --wait",
134
- instead: (value: string) => `give a duration, --wait ${value}s`,
135
- },
136
- } as const satisfies Record<
137
- string,
138
- { old: string; instead: string | ((subject: string) => string) }
139
- >;
140
-
141
- export type Deprecated = keyof typeof DEPRECATIONS;
142
-
143
- /** One attempt's deprecated uses: each name said once, as a warning in the run's log. */
144
- export class RunDeprecations {
145
- /** Each name said, with its subject where it has one: each key with a colon is named. */
146
- readonly #said = new Set<string>();
147
- /** The attempt has ended: an outcome kept past it reports to no log. */
148
- #settled = false;
149
-
150
- constructor(private readonly warn: (text: string) => void) {}
151
-
152
- /** `subject`, where given, is what was used, and each subject is said once. */
153
- used(name: Deprecated, subject?: string): void {
154
- const said = subject === undefined ? name : `${name}\0${subject}`;
155
- if (this.#said.has(said)) return;
156
- this.#said.add(said);
157
- this.warn(deprecationLine(name, subject));
158
- }
159
-
160
- settle(): void {
161
- this.#settled = true;
162
- }
163
-
164
- /** As `used`, for a name the contract reports by its own spelling; one not listed is ignored. */
165
- reported = (name: unknown): void => {
166
- if (this.#settled) return;
167
- if (typeof name === "string" && Object.hasOwn(DEPRECATIONS, name))
168
- this.used(name as Deprecated);
169
- };
170
- }
171
-
172
- /** The line a deprecated use logs. */
173
- export function deprecationLine(name: Deprecated, subject = ""): string {
174
- const { old, instead } = DEPRECATIONS[name];
175
- const named = subject === "" ? "" : `, ${subject},`;
176
- return `awf: ${old}${named} is deprecated; ${typeof instead === "function" ? instead(subject) : instead}`;
177
- }
@@ -1,8 +0,0 @@
1
- // Holds a run's lock from another process for `emit.test.ts`: `bun hold-run-lock.fixture.ts {file} {ms}`.
2
- import { withRunLock } from "./run-lock";
3
-
4
- const [file, ms] = process.argv.slice(2);
5
- await withRunLock(file!, async () => {
6
- console.log("held");
7
- await Bun.sleep(Number(ms));
8
- });