@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.
- package/LICENSE +21 -0
- package/README.md +16 -0
- package/package.json +13 -9
- package/src/accounting/format.ts +4 -3
- package/src/accounting/spent.ts +128 -0
- package/src/accounting/summary.ts +40 -32
- package/src/agent-cli/cli.ts +28 -55
- package/src/agent-cli/client.ts +1 -27
- package/src/allowance-command.ts +8 -10
- package/src/attempt-ending.ts +4 -4
- package/src/attempt-output.ts +13 -31
- package/src/attempt-view.ts +33 -32
- package/src/author-typebox.ts +13 -0
- package/src/client/checks.ts +94 -0
- package/src/client/exported.ts +17 -0
- package/src/client/index.ts +122 -0
- package/src/client/locate.ts +34 -0
- package/src/client/published.ts +3 -0
- package/src/client/runs.ts +337 -0
- package/src/command-line.ts +64 -0
- package/src/commands.ts +7 -1
- package/src/control-plane.ts +4 -6
- package/src/coordinator-prompt.ts +4 -3
- package/src/coordinator-socket.ts +27 -28
- package/src/deadlines.ts +6 -40
- package/src/decisions/directory.ts +50 -37
- package/src/decisions/fake.ts +1 -1
- package/src/decisions/openrouter.ts +4 -2
- package/src/decisions/seam.ts +4 -4
- package/src/display-path.ts +3 -3
- package/src/duration.ts +19 -33
- package/src/emit-command.ts +38 -22
- package/src/emit.ts +148 -59
- package/src/event-ids.ts +31 -0
- package/src/files.ts +44 -4
- package/src/hand-over.ts +150 -0
- package/src/help/allowance.txt +4 -3
- package/src/help/emit.txt +11 -7
- package/src/help/inbox.txt +5 -3
- package/src/help/logs.txt +7 -5
- package/src/help/open.txt +29 -0
- package/src/help/reply.txt +6 -8
- package/src/help/run.txt +13 -8
- package/src/help/runs.txt +4 -2
- package/src/help/tick.txt +9 -4
- package/src/herdr-placement.ts +3 -33
- package/src/herdr-run-session.ts +5 -13
- package/src/herdr-workspace-marks.ts +7 -17
- package/src/holder-command.ts +54 -136
- package/src/in-lines.ts +28 -0
- package/src/inbox-command.ts +42 -56
- package/src/index.ts +1 -1
- package/src/init-command.ts +8 -17
- package/src/init-template/hello/workflow.ts.template +3 -2
- package/src/jsonl.ts +10 -6
- package/src/keep-alive.ts +7 -19
- package/src/logs-command.ts +134 -53
- package/src/message-operation.ts +6 -3
- package/src/messaging/messaging.ts +8 -10
- package/src/messaging/text.ts +2 -2
- package/src/open-command.ts +770 -0
- package/src/operation-events.ts +4 -6
- package/src/operation-liveness.ts +19 -4
- package/src/operator-cli.ts +206 -90
- package/src/operator-runtime.ts +24 -2
- package/src/ops.ts +270 -0
- package/src/pane-layout.ts +49 -96
- package/src/progress-view.ts +2 -2
- package/src/project-engine.ts +79 -0
- package/src/projects.ts +13 -2
- package/src/refusals.ts +119 -0
- package/src/result-slots.ts +11 -31
- package/src/result-validation.ts +2 -2
- package/src/run-command.ts +27 -31
- package/src/run-commands.ts +42 -114
- package/src/run-event-log.ts +1 -1
- package/src/run-lines.ts +159 -0
- package/src/run-lock.ts +57 -1
- package/src/run-policy/stage-plan.ts +154 -0
- package/src/run-policy/stages.ts +113 -0
- package/src/run-policy/waits.ts +452 -0
- package/src/run-prepare.ts +165 -41
- package/src/run-read.ts +109 -41
- package/src/run-state.ts +170 -50
- package/src/run-text.ts +54 -0
- package/src/run-unreachable.ts +18 -0
- package/src/run-usage.ts +17 -11
- package/src/run-view.ts +73 -14
- package/src/runs-command.ts +81 -63
- package/src/runs.ts +284 -222
- package/src/sandbox-homes.ts +65 -13
- package/src/sandboxes.ts +182 -28
- package/src/session-lines.ts +93 -0
- package/src/skills/run-skills.ts +8 -1
- package/src/sources.ts +2 -39
- package/src/spend.ts +333 -0
- package/src/stage-ledger.ts +111 -153
- package/src/stop-crashed.ts +6 -5
- package/src/stopped.ts +4 -4
- package/src/test-command.ts +37 -19
- package/src/testing/operation-events.ts +3 -23
- package/src/testing.ts +66 -18
- package/src/tick-command.ts +145 -15
- package/src/tools.ts +11 -30
- package/src/turn-ends.ts +37 -0
- package/src/waits.ts +651 -0
- package/src/wake.ts +84 -54
- package/src/workflow-loader.ts +49 -2
- package/src/workflow-runner.ts +525 -618
- package/src/workflow-test-preload.ts +4 -1
- package/src/workflow-testing/coordinators.ts +1 -1
- package/src/workflow-testing/fake-client.ts +387 -0
- package/src/workflow-testing/host.ts +28 -29
- package/src/workflow-testing/index.ts +9 -27
- package/src/workflow-testing/run-test.ts +201 -286
- package/src/workflow-testing/script.ts +7 -39
- package/src/deprecations.ts +0 -177
- package/src/hold-run-lock.fixture.ts +0 -8
- package/src/mailbox.ts +0 -972
- package/src/stage-plan.ts +0 -186
- package/src/workflow-testing/coordinator.typecheck.ts +0 -54
- 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`
|
|
128
|
-
* `id`
|
|
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 = {
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
}
|
package/src/deprecations.ts
DELETED
|
@@ -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
|
-
});
|