@agentswf/engine 0.1.0-beta.2 → 0.1.0-beta.4

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 (82) hide show
  1. package/package.json +5 -6
  2. package/src/agent-cli/cli.ts +571 -0
  3. package/src/agent-cli/client.ts +115 -0
  4. package/src/agent-launcher.ts +15 -25
  5. package/src/allowance-command.ts +31 -21
  6. package/src/attempt-ending.ts +23 -5
  7. package/src/attempt-output.ts +48 -18
  8. package/src/attempt-view.ts +25 -15
  9. package/src/commands.ts +176 -0
  10. package/src/control-plane.ts +73 -22
  11. package/src/coordinator-port.ts +29 -0
  12. package/src/coordinator-prompt.ts +237 -0
  13. package/src/coordinator-socket.ts +236 -0
  14. package/src/deadlines.ts +47 -10
  15. package/src/decisions/directory.ts +8 -6
  16. package/src/duration.ts +29 -0
  17. package/src/emit-command.ts +119 -0
  18. package/src/emit.ts +246 -0
  19. package/src/files.ts +11 -1
  20. package/src/help/allowance.txt +13 -0
  21. package/src/help/emit.txt +13 -0
  22. package/src/help/inbox.txt +12 -0
  23. package/src/help/init.txt +12 -0
  24. package/src/help/logs.txt +13 -0
  25. package/src/help/reply.txt +18 -0
  26. package/src/help/run.txt +44 -0
  27. package/src/help/runs.txt +11 -0
  28. package/src/help/test.txt +9 -0
  29. package/src/help/tick.txt +12 -0
  30. package/src/here.ts +9 -3
  31. package/src/hold-run-lock.fixture.ts +8 -0
  32. package/src/holder-command.ts +279 -0
  33. package/src/inbox-command.ts +715 -0
  34. package/src/init-command.ts +29 -11
  35. package/src/init-template/AGENTS.md.template +4 -4
  36. package/src/init-template/hello/workflow.test.ts.template +2 -2
  37. package/src/init-template/hello/workflow.ts.template +16 -20
  38. package/src/keep-alive.ts +40 -0
  39. package/src/logs-command.ts +106 -35
  40. package/src/machine.ts +12 -1
  41. package/src/mailbox.ts +990 -0
  42. package/src/message-operation.ts +26 -3
  43. package/src/messaging/limits.ts +1 -1
  44. package/src/messaging/messaging.ts +478 -85
  45. package/src/messaging/routes.ts +11 -4
  46. package/src/messaging/text.ts +25 -22
  47. package/src/operation-liveness.ts +55 -9
  48. package/src/operator-cli.ts +290 -119
  49. package/src/operator-runtime.ts +8 -8
  50. package/src/progress-view.ts +49 -7
  51. package/src/projects.ts +57 -0
  52. package/src/result-slots.ts +43 -19
  53. package/src/result-validation.ts +18 -27
  54. package/src/run-command.ts +29 -66
  55. package/src/run-commands.ts +13 -2
  56. package/src/run-lock.ts +123 -0
  57. package/src/run-prepare.ts +94 -22
  58. package/src/run-state.ts +125 -6
  59. package/src/run-usage.ts +2 -2
  60. package/src/run-view.ts +0 -9
  61. package/src/runs-command.ts +43 -25
  62. package/src/runs.ts +142 -23
  63. package/src/sandboxes.ts +11 -4
  64. package/src/simple-input.ts +268 -0
  65. package/src/socket-exchange.ts +26 -0
  66. package/src/sources.ts +70 -0
  67. package/src/stage-ledger.ts +89 -14
  68. package/src/stage-plan.ts +23 -0
  69. package/src/stop-crashed.ts +39 -0
  70. package/src/test-command.ts +34 -11
  71. package/src/testing.ts +72 -4
  72. package/src/tick-command.ts +76 -0
  73. package/src/tools.ts +645 -0
  74. package/src/wake.ts +264 -0
  75. package/src/workflow-loader.ts +32 -22
  76. package/src/workflow-runner.ts +1277 -97
  77. package/src/workflow-testing/coordinator.typecheck.ts +54 -0
  78. package/src/workflow-testing/coordinators.ts +137 -0
  79. package/src/workflow-testing/host.ts +67 -7
  80. package/src/workflow-testing/index.ts +488 -34
  81. package/src/workflow-testing/script.ts +74 -4
  82. package/src/workflow-testing/workflow-testing.typecheck.ts +19 -2
@@ -1,5 +1,17 @@
1
- import type { ReplyCode, SendCode } from "@agentswf/contract/wire";
2
- import type { Effort, JsonValue, OperationCause, OutputSchema } from "@agentswf/contract/workflow";
1
+ import type {
2
+ ParticipantCode,
3
+ ReplyCode,
4
+ SendCode,
5
+ ToolCode,
6
+ ToolFailure,
7
+ } from "@agentswf/contract/wire";
8
+ import type {
9
+ Effort,
10
+ JsonObject,
11
+ JsonValue,
12
+ OperationCause,
13
+ OutputSchema,
14
+ } from "@agentswf/contract/workflow";
3
15
  import type Type from "typebox";
4
16
  import { canonical } from "../canonical-json";
5
17
  import { messageOf } from "../errors";
@@ -30,18 +42,32 @@ export type Turn = {
30
42
  */
31
43
  readonly messages: readonly Message[];
32
44
  /**
33
- * `wf send`: resolves once admitted, `sent`; or, with `awaitReply`, with the reply, which arrives
45
+ * `wfa send`: resolves once admitted, `sent`; or, with `awaitReply`, with the reply, which arrives
34
46
  * in a later turn of the same call. Returning a result without awaiting it drops the wait, and it
35
47
  * resolves `failed` with reason `dropped`. While a reply is awaited the script awaits only the
36
48
  * host's calls: once it is still, its turn ends.
37
49
  */
38
50
  send(peer: string, text: string, options?: SendOptions): Promise<MessageOutcome>;
39
- /** `wf reply`: answers the question `peer` asked. A result is refused while one is owed. */
51
+ /** `wfa reply`: answers the question `peer` asked. A result is refused while one is owed. */
40
52
  reply(peer: string, text: string, options?: SendOptions): Promise<MessageOutcome>;
53
+ /**
54
+ * `wfa tool`: calls the agent's tool `name` with `input` as its JSON, or none, through the run's
55
+ * real checks and handler; resolves with what the agent would read.
56
+ */
57
+ tool(name: string, input?: JsonObject): Promise<ToolOutcome>;
41
58
  /** Fires when the engine cancels the turn. */
42
59
  signal: AbortSignal;
43
60
  };
44
61
 
62
+ /**
63
+ * How a tool call ended, as `wfa tool` reports it; a failure or a refusal is a value to test. A
64
+ * refusal carries what the agent is told, which for `invalid-input` is what it corrects from.
65
+ */
66
+ export type ToolOutcome =
67
+ | { kind: "result"; output?: string }
68
+ | { kind: "failed"; outcome: ToolFailure; error: string }
69
+ | { kind: "refused"; code: ToolCode; error: string };
70
+
45
71
  /** A message as the agent was shown it. */
46
72
  export type Message = {
47
73
  from: string;
@@ -70,6 +96,7 @@ export type ReceivedMessage = Message & {
70
96
  turn: Turn;
71
97
  reply(text: string, options?: SendOptions): Promise<MessageOutcome>;
72
98
  send(peer: string, text: string, options?: SendOptions): Promise<MessageOutcome>;
99
+ tool(name: string, input?: JsonObject): Promise<ToolOutcome>;
73
100
  /** Ends the turn without a reply; one owed is reminded once, then fails. */
74
101
  silent(): Reply;
75
102
  /** The harness failed: the message operation fails, and the agent stops. */
@@ -85,6 +112,49 @@ export type MessageScript = (
85
112
  message: ReceivedMessage,
86
113
  ) => Reply | MessageOutcome | undefined | Promise<Reply | MessageOutcome | undefined>;
87
114
 
115
+ /**
116
+ * How a participant's script's `reply` or `send` ended, as `awf reply --json` reports it: a reply's
117
+ * `id` is the question's, a send's the new message's.
118
+ */
119
+ export type ParticipantOutcome = { code: "delivered"; id: string } | { code: ParticipantCode };
120
+
121
+ /**
122
+ * A message an agent sent a participant, and what the participant's script may do about it, as
123
+ * whoever answers for it would with `awf reply` and `awf send`. An answer is recorded as coming through `test`.
124
+ */
125
+ export type ParticipantMessage = {
126
+ kind: "message";
127
+ /** `{attempt}.{n}`, as `awf inbox` prints it. */
128
+ id: string;
129
+ /** The agent's peer address. */
130
+ from: string;
131
+ text: string;
132
+ /** A question: it stays open until the script answers it, or it expires. */
133
+ asks?: true;
134
+ /** Answers this question, as `awf reply --to` does. */
135
+ reply(text: string): Promise<ParticipantOutcome>;
136
+ /** Messages an agent over a route the workflow allowed, as `awf send` does. */
137
+ send(agent: string, text: string): Promise<ParticipantOutcome>;
138
+ };
139
+
140
+ /** A route to or from a participant, as it opens: where a script that speaks first starts. */
141
+ export type ParticipantRoute = {
142
+ kind: "route";
143
+ /** The agent's peer address. */
144
+ agent: string;
145
+ direction: "both" | "to-participant" | "from-participant";
146
+ purpose: string;
147
+ /** Messages the agent, as `awf send` does. */
148
+ send(text: string): Promise<ParticipantOutcome>;
149
+ };
150
+
151
+ /**
152
+ * A participant's script: called with each route to it as it opens, and each message an agent sends
153
+ * it, once recorded; the `awf inbox` items whoever answers for it would act on. What it returns is awaited and
154
+ * otherwise ignored: a question it does not answer stays open, and a throw fails the test.
155
+ */
156
+ export type ParticipantScript = (event: ParticipantRoute | ParticipantMessage) => unknown;
157
+
88
158
  const ENTRY: unique symbol = Symbol("agentswf.script-entry");
89
159
 
90
160
  type Respond = (turn: Turn) => JsonValue | Reply | Promise<JsonValue | Reply>;
@@ -1,7 +1,7 @@
1
1
  // Checked by `tsc --noEmit`, never run: each wrong answer is refused where it is written.
2
- import type { OutputSchema } from "@agentswf/contract/workflow";
2
+ import { defineWorkflow, type OutputSchema } from "@agentswf/contract/workflow";
3
3
  import Type from "typebox";
4
- import { answer, reply, type Script } from ".";
4
+ import { answer, reply, type Script, testWorkflow } from ".";
5
5
 
6
6
  const VERDICT = Type.Union([
7
7
  Type.Object({ kind: Type.Literal("ready"), summary: Type.String() }),
@@ -45,3 +45,20 @@ export const invalid: Script[] = [
45
45
 
46
46
  // @ts-expect-error raw JSON where a script expects answer(…)
47
47
  export const raw: Script = { kind: "ready", summary: "ok" };
48
+
49
+ const withEvents = defineWorkflow({
50
+ meta: { name: "w", description: "w" },
51
+ file: import.meta.path,
52
+ events: { ping: { type: "object" } as OutputSchema<{ n: number }> },
53
+ run: async (wf) => (await wf.receive()).length,
54
+ });
55
+ export const tested = () => testWorkflow(withEvents, { id: "k" });
56
+ // @ts-expect-error a workflow with events is tested from { id, event? }, never its args
57
+ export const byArgs = () => testWorkflow(withEvents, null);
58
+ const plain = defineWorkflow({
59
+ meta: { name: "p", description: "p" },
60
+ prepare: ({ argv }) => ({ n: argv.length }),
61
+ run: async (_wf, args) => args.n,
62
+ });
63
+ // @ts-expect-error a workflow without events is tested from its args, not { id }
64
+ export const byOptions = () => testWorkflow(plain, { id: "k" });