@dynamicagents/core 0.9.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 +201 -0
- package/README.md +522 -0
- package/dist/a2a/agent-stub.d.ts +60 -0
- package/dist/a2a/agent-stub.js +1 -0
- package/dist/a2a/caller-token.d.ts +44 -0
- package/dist/a2a/caller-token.js +61 -0
- package/dist/a2a/caller.d.ts +23 -0
- package/dist/a2a/caller.js +33 -0
- package/dist/a2a/card.d.ts +158 -0
- package/dist/a2a/card.js +163 -0
- package/dist/a2a/context.d.ts +19 -0
- package/dist/a2a/context.js +61 -0
- package/dist/a2a/deliver.d.ts +159 -0
- package/dist/a2a/deliver.js +185 -0
- package/dist/a2a/executor.d.ts +84 -0
- package/dist/a2a/executor.js +106 -0
- package/dist/a2a/index.d.ts +35 -0
- package/dist/a2a/index.js +34 -0
- package/dist/a2a/notify.d.ts +106 -0
- package/dist/a2a/notify.js +180 -0
- package/dist/a2a/parts.d.ts +45 -0
- package/dist/a2a/parts.js +94 -0
- package/dist/a2a/push.d.ts +70 -0
- package/dist/a2a/push.js +53 -0
- package/dist/a2a/self-origin.d.ts +91 -0
- package/dist/a2a/self-origin.js +114 -0
- package/dist/a2a/task-store.d.ts +44 -0
- package/dist/a2a/task-store.js +99 -0
- package/dist/a2a/task.d.ts +85 -0
- package/dist/a2a/task.js +15 -0
- package/dist/a2a/verify.d.ts +80 -0
- package/dist/a2a/verify.js +143 -0
- package/dist/agent/budget.d.ts +46 -0
- package/dist/agent/budget.js +42 -0
- package/dist/agent/control.d.ts +109 -0
- package/dist/agent/control.js +115 -0
- package/dist/agent/errors.d.ts +85 -0
- package/dist/agent/errors.js +64 -0
- package/dist/agent/final-reply.d.ts +49 -0
- package/dist/agent/final-reply.js +68 -0
- package/dist/agent/history.d.ts +97 -0
- package/dist/agent/history.js +133 -0
- package/dist/agent/index.d.ts +29 -0
- package/dist/agent/index.js +29 -0
- package/dist/agent/inference.d.ts +110 -0
- package/dist/agent/inference.js +120 -0
- package/dist/agent/model.d.ts +90 -0
- package/dist/agent/model.js +1 -0
- package/dist/agent/session.d.ts +100 -0
- package/dist/agent/session.js +82 -0
- package/dist/agent/workers-ai/index.d.ts +23 -0
- package/dist/agent/workers-ai/index.js +23 -0
- package/dist/agent/workers-ai/runtime.d.ts +42 -0
- package/dist/agent/workers-ai/runtime.js +63 -0
- package/dist/alarm/index.d.ts +77 -0
- package/dist/alarm/index.js +116 -0
- package/dist/config.d.ts +202 -0
- package/dist/config.js +135 -0
- package/dist/contract/index.d.ts +9 -0
- package/dist/contract/index.js +8 -0
- package/dist/contract/plugin.d.ts +324 -0
- package/dist/contract/plugin.js +114 -0
- package/dist/contract/recipe.d.ts +180 -0
- package/dist/contract/recipe.js +1 -0
- package/dist/contract/validation.d.ts +91 -0
- package/dist/contract/validation.js +84 -0
- package/dist/db/db.d.ts +147 -0
- package/dist/db/db.js +90 -0
- package/dist/db/index.d.ts +8 -0
- package/dist/db/index.js +8 -0
- package/dist/db/migrations/index.d.ts +20 -0
- package/dist/db/migrations/index.js +60 -0
- package/dist/db/models/subtasks.d.ts +100 -0
- package/dist/db/models/subtasks.js +241 -0
- package/dist/db/models/tasks.d.ts +118 -0
- package/dist/db/models/tasks.js +274 -0
- package/dist/db/schema.d.ts +468 -0
- package/dist/db/schema.js +88 -0
- package/dist/env.d.ts +53 -0
- package/dist/env.js +47 -0
- package/dist/host/agent.d.ts +305 -0
- package/dist/host/agent.js +400 -0
- package/dist/host/index.d.ts +20 -0
- package/dist/host/index.js +19 -0
- package/dist/host/plugin-host.d.ts +42 -0
- package/dist/host/plugin-host.js +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +17 -0
- package/dist/job/index.d.ts +21 -0
- package/dist/job/index.js +21 -0
- package/dist/job/lifecycle.d.ts +176 -0
- package/dist/job/lifecycle.js +230 -0
- package/dist/job/state.d.ts +92 -0
- package/dist/job/state.js +40 -0
- package/dist/platform.d.ts +138 -0
- package/dist/platform.js +140 -0
- package/dist/round/agent.d.ts +271 -0
- package/dist/round/agent.js +678 -0
- package/dist/round/index.d.ts +25 -0
- package/dist/round/index.js +23 -0
- package/dist/round/policy.d.ts +98 -0
- package/dist/round/policy.js +1 -0
- package/dist/round/subagent.d.ts +87 -0
- package/dist/round/subagent.js +108 -0
- package/dist/round/turn.d.ts +249 -0
- package/dist/round/turn.js +564 -0
- package/dist/round/workflow.d.ts +147 -0
- package/dist/round/workflow.js +387 -0
- package/dist/runtime/index.d.ts +127 -0
- package/dist/runtime/index.js +186 -0
- package/dist/runtime/tool-families.d.ts +32 -0
- package/dist/runtime/tool-families.js +61 -0
- package/dist/subagent/fingerprint.d.ts +37 -0
- package/dist/subagent/fingerprint.js +92 -0
- package/dist/subagent/index.d.ts +169 -0
- package/dist/subagent/index.js +330 -0
- package/dist/subagent/prompt.d.ts +32 -0
- package/dist/subagent/prompt.js +37 -0
- package/dist/subagent/run.d.ts +157 -0
- package/dist/subagent/run.js +540 -0
- package/dist/subagent/workspace.d.ts +85 -0
- package/dist/subagent/workspace.js +127 -0
- package/dist/subtasks/catalog.d.ts +40 -0
- package/dist/subtasks/catalog.js +36 -0
- package/dist/subtasks/decomposition.d.ts +85 -0
- package/dist/subtasks/decomposition.js +156 -0
- package/dist/subtasks/delegate.d.ts +120 -0
- package/dist/subtasks/delegate.js +131 -0
- package/dist/subtasks/index.d.ts +9 -0
- package/dist/subtasks/index.js +9 -0
- package/dist/subtasks/subtask-types.d.ts +91 -0
- package/dist/subtasks/subtask-types.js +103 -0
- package/dist/subtasks/types.d.ts +295 -0
- package/dist/subtasks/types.js +15 -0
- package/dist/testing/auth.d.ts +34 -0
- package/dist/testing/auth.js +35 -0
- package/dist/testing/do.d.ts +29 -0
- package/dist/testing/do.js +25 -0
- package/dist/testing/fake-session.d.ts +26 -0
- package/dist/testing/fake-session.js +37 -0
- package/dist/testing/fixtures.d.ts +64 -0
- package/dist/testing/fixtures.js +104 -0
- package/dist/testing/harness.d.ts +97 -0
- package/dist/testing/harness.js +138 -0
- package/dist/testing/index.d.ts +31 -0
- package/dist/testing/index.js +35 -0
- package/dist/testing/mock-model.d.ts +77 -0
- package/dist/testing/mock-model.js +136 -0
- package/dist/testing/node.d.ts +56 -0
- package/dist/testing/node.js +56 -0
- package/dist/testing/vcr-global-setup.d.ts +12 -0
- package/dist/testing/vcr-global-setup.js +15 -0
- package/dist/testing/vcr-shared.d.ts +38 -0
- package/dist/testing/vcr-shared.js +33 -0
- package/dist/testing/vcr-spec.d.ts +25 -0
- package/dist/testing/vcr-spec.js +124 -0
- package/dist/testing/vcr-store.d.ts +86 -0
- package/dist/testing/vcr-store.js +191 -0
- package/dist/testing/vcr.d.ts +117 -0
- package/dist/testing/vcr.js +275 -0
- package/dist/worker/define-agent.d.ts +123 -0
- package/dist/worker/define-agent.js +20 -0
- package/dist/worker/index.d.ts +218 -0
- package/dist/worker/index.js +369 -0
- package/eslint-rules/index.js +31 -0
- package/eslint-rules/no-deprecated-object-properties.js +81 -0
- package/package.json +178 -0
- package/scripts/generate-keys.mjs +48 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import type { WorkflowStep } from "cloudflare:workers";
|
|
2
|
+
import { type TurnPushContext } from "./push.js";
|
|
3
|
+
import type { PlainTask } from "./task.js";
|
|
4
|
+
/** What {@link deliverTerminalTask} needs to finish a turn. */
|
|
5
|
+
export interface DeliverTerminalOptions {
|
|
6
|
+
/** Which Task this is about, and where its callback goes. */
|
|
7
|
+
push: TurnPushContext;
|
|
8
|
+
/** The agent's card-signing key, for the callback JWT. */
|
|
9
|
+
signingKey: string;
|
|
10
|
+
/**
|
|
11
|
+
* The guarded write, and **the cancellation check**. Return whether it
|
|
12
|
+
* applied: `false` must mean the row was already terminal (canceled), which is
|
|
13
|
+
* what suppresses the callback.
|
|
14
|
+
*
|
|
15
|
+
* Resolve any Durable Object stub *inside* this function, never above it — a
|
|
16
|
+
* stub hoisted out of a step body is a live connection that a replay cannot
|
|
17
|
+
* reconnect.
|
|
18
|
+
*/
|
|
19
|
+
saveTask: (task: PlainTask) => Promise<boolean>;
|
|
20
|
+
/**
|
|
21
|
+
* Build the terminal Task. Called **inside** the `complete` step so that what
|
|
22
|
+
* gets notified is exactly what was persisted: building it outside would
|
|
23
|
+
* re-stamp `new Date()` on every replay and post a Task that differs from the
|
|
24
|
+
* stored one.
|
|
25
|
+
*/
|
|
26
|
+
terminal: () => PlainTask;
|
|
27
|
+
/**
|
|
28
|
+
* Optional best-effort cleanup, run after the write and before the callback.
|
|
29
|
+
* An agent with no managed children omits it and no `sweep` step appears.
|
|
30
|
+
*/
|
|
31
|
+
sweep?: () => Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Prefix for this delivery's three step names. Empty by default, which is the
|
|
34
|
+
* only value an ordinary turn may use.
|
|
35
|
+
*
|
|
36
|
+
* **Step names are durable cache keys**, so this is not cosmetic. A workflow
|
|
37
|
+
* that delivers twice — an ordinary delivery, then
|
|
38
|
+
* {@link deliverAbandonedTask} from a `catch` above it — would otherwise call
|
|
39
|
+
* `step.do("complete")` a second time and be handed the *first* call's cached
|
|
40
|
+
* result, so the failed Task it built would never be written and the outcome
|
|
41
|
+
* would depend on Workflows' caching semantics for a step whose failure was
|
|
42
|
+
* caught. A distinct prefix makes the second delivery its own set of steps,
|
|
43
|
+
* where the guarded write decides the outcome as it is supposed to.
|
|
44
|
+
*/
|
|
45
|
+
stepPrefix?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Thrown when a delivery failed *after* its terminal Task was durably saved.
|
|
49
|
+
*
|
|
50
|
+
* The distinction it carries is the difference between "this turn produced
|
|
51
|
+
* nothing" and "this turn produced a result the gatekeeper has not heard about
|
|
52
|
+
* yet", and only the first of those is an abandoned task.
|
|
53
|
+
*
|
|
54
|
+
* Without it, an ordinary delivery whose `notify` step exhausted its retries
|
|
55
|
+
* would unwind into an abandoned-task recovery, which would write a generic
|
|
56
|
+
* failure over a real answer. The guarded write refuses that now
|
|
57
|
+
* ({@link file://../db/models/tasks.ts}), but refusing it is not enough on its
|
|
58
|
+
* own: the recovery would then succeed at doing nothing, swallow a genuine
|
|
59
|
+
* callback failure, and log the word "abandoned" about a turn that completed.
|
|
60
|
+
* So the two work together — this stops the recovery being attempted, and the
|
|
61
|
+
* guard is the backstop for the narrow window where a `complete` step throws
|
|
62
|
+
* without saying whether its write applied.
|
|
63
|
+
*/
|
|
64
|
+
export declare class TaskAlreadyTerminalError extends Error {
|
|
65
|
+
constructor(cause: unknown);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Persist a turn's terminal Task, then notify the gatekeeper.
|
|
69
|
+
*
|
|
70
|
+
* **The guarded write is the cancellation check.** `saveTask` refuses to write a
|
|
71
|
+
* terminal state over a `canceled` row and says so, doing that read and write in
|
|
72
|
+
* one synchronous pass inside the Durable Object. Probing first and saving
|
|
73
|
+
* second would leave a window — between the two calls, and again between this
|
|
74
|
+
* step and `notify` — in which a `tasks/cancel` lands and the gatekeeper still
|
|
75
|
+
* receives a `completed` callback. Keying the notify on "did the write apply"
|
|
76
|
+
* closes it, and that has already been got wrong once.
|
|
77
|
+
*
|
|
78
|
+
* In `/a2a` rather than `/round` because the shape is not a round's: an agent
|
|
79
|
+
* whose turn is a single inference ends it the same way, and importing this from
|
|
80
|
+
* `/round` would put the whole delegation engine in a bundle that must not carry
|
|
81
|
+
* it. Everything it calls already lives here.
|
|
82
|
+
*/
|
|
83
|
+
export declare function deliverTerminalTask(step: WorkflowStep, options: DeliverTerminalOptions): Promise<void>;
|
|
84
|
+
/** What {@link deliverAbandonedTask} needs to end a turn nobody else will. */
|
|
85
|
+
export interface AbandonedTaskOptions {
|
|
86
|
+
/** Which Task this is about, and where its callback goes. */
|
|
87
|
+
push: TurnPushContext;
|
|
88
|
+
/** The agent's card-signing key, for the callback JWT. */
|
|
89
|
+
signingKey: string;
|
|
90
|
+
/** The guarded write — same contract as {@link DeliverTerminalOptions.saveTask}. */
|
|
91
|
+
saveTask: (task: PlainTask) => Promise<boolean>;
|
|
92
|
+
/**
|
|
93
|
+
* The user-facing words. The host's, always: core ships no prompt copy and no
|
|
94
|
+
* user-facing copy, and a paraphrase invented at the failure site is exactly
|
|
95
|
+
* what this parameter exists to prevent.
|
|
96
|
+
*/
|
|
97
|
+
text: string;
|
|
98
|
+
/** Best-effort child cleanup. Omitted by an agent that delegates to nothing. */
|
|
99
|
+
sweep?: () => Promise<void>;
|
|
100
|
+
/** Log prefix — conventionally the agent's tenant id. */
|
|
101
|
+
label?: string;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Turn an unrecoverable orchestration fault into a delivered failed Task.
|
|
105
|
+
*
|
|
106
|
+
* ## The failure this exists for
|
|
107
|
+
*
|
|
108
|
+
* A turn ends badly in two different ways. A **typed** failure — the models were
|
|
109
|
+
* tried and nothing usable came back — is a value, and the ordinary delivery
|
|
110
|
+
* path carries it. A **transient** fault instead throws, so the step retries and
|
|
111
|
+
* recovers without paying for a second inference. That is the right default.
|
|
112
|
+
*
|
|
113
|
+
* What neither covers is a transient fault that never stops being one. `step.do`
|
|
114
|
+
* retries a bounded number of times and then rethrows, and with nothing above it
|
|
115
|
+
* to catch that, the orchestration unwinds, the delivery path is never reached,
|
|
116
|
+
* and the Workflow instance errors with the Task still sitting in `working`. The
|
|
117
|
+
* user is told nothing at all — and because the instance dies mid-`run`, the
|
|
118
|
+
* runtime records it as *"your Worker's code had hung and would never generate a
|
|
119
|
+
* response"*, which reads like a bug in the workflow rather than a provider that
|
|
120
|
+
* was refusing every request.
|
|
121
|
+
*
|
|
122
|
+
* Observed exactly that way in a deployed agent on 2026-08-19: a turn step
|
|
123
|
+
* exhausted its four attempts against a rate-limited provider, and the agent
|
|
124
|
+
* simply went quiet.
|
|
125
|
+
*
|
|
126
|
+
* ## Three behaviours here are load-bearing
|
|
127
|
+
*
|
|
128
|
+
* **It resolves after a successful delivery — it does not rethrow.** Rethrowing
|
|
129
|
+
* would reproduce the very "hung Worker" record this exists to remove, and the
|
|
130
|
+
* instance genuinely has finished its job: the Task is terminal and the gatekeeper
|
|
131
|
+
* has been told.
|
|
132
|
+
*
|
|
133
|
+
* **It rethrows the *original* `cause` when the delivery itself fails.**
|
|
134
|
+
* Swallowing there would mark the instance successful while the user got
|
|
135
|
+
* nothing — strictly worse than the erroring instance being replaced, because it
|
|
136
|
+
* would also be silent in the Workflows console. `cause` is what an operator
|
|
137
|
+
* needs to see, not the delivery's own secondary fault.
|
|
138
|
+
*
|
|
139
|
+
* **It refuses to run at all once a terminal Task exists.** A
|
|
140
|
+
* {@link TaskAlreadyTerminalError} means the ordinary delivery persisted its
|
|
141
|
+
* result and only the callback failed, so the original fault is rethrown and
|
|
142
|
+
* nothing is written. An earlier draft of this claimed the guarded write alone
|
|
143
|
+
* made that safe; it did not. The write refused only cancellation conflicts, so
|
|
144
|
+
* `completed → failed` applied cleanly and a turn that succeeded would have been
|
|
145
|
+
* rewritten as a generic failure because a webhook was flaky. The guard now
|
|
146
|
+
* refuses any terminal-over-different-terminal write as a backstop, and this
|
|
147
|
+
* check is what stops the attempt being made in the first place — without it the
|
|
148
|
+
* recovery would quietly succeed at doing nothing, swallowing a real callback
|
|
149
|
+
* failure and logging "abandoned" about a completed turn.
|
|
150
|
+
*
|
|
151
|
+
* ## Why it is exported rather than private to `/round`
|
|
152
|
+
*
|
|
153
|
+
* `runHandleTask` wraps itself in it, so every round agent gets this without
|
|
154
|
+
* asking. But an agent whose turn is a single inference writes its own workflow
|
|
155
|
+
* body and has the identical exposure — and the alternative to exporting this is
|
|
156
|
+
* that each such host reimplements the delivery, which is precisely the mistake
|
|
157
|
+
* this function was extracted from.
|
|
158
|
+
*/
|
|
159
|
+
export declare function deliverAbandonedTask(step: WorkflowStep, cause: unknown, options: AbandonedTaskOptions): Promise<void>;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { buildFailedTask } from "./notify.js";
|
|
2
|
+
import { createPushChannel } from "./push.js";
|
|
3
|
+
/**
|
|
4
|
+
* Thrown when a delivery failed *after* its terminal Task was durably saved.
|
|
5
|
+
*
|
|
6
|
+
* The distinction it carries is the difference between "this turn produced
|
|
7
|
+
* nothing" and "this turn produced a result the gatekeeper has not heard about
|
|
8
|
+
* yet", and only the first of those is an abandoned task.
|
|
9
|
+
*
|
|
10
|
+
* Without it, an ordinary delivery whose `notify` step exhausted its retries
|
|
11
|
+
* would unwind into an abandoned-task recovery, which would write a generic
|
|
12
|
+
* failure over a real answer. The guarded write refuses that now
|
|
13
|
+
* ({@link file://../db/models/tasks.ts}), but refusing it is not enough on its
|
|
14
|
+
* own: the recovery would then succeed at doing nothing, swallow a genuine
|
|
15
|
+
* callback failure, and log the word "abandoned" about a turn that completed.
|
|
16
|
+
* So the two work together — this stops the recovery being attempted, and the
|
|
17
|
+
* guard is the backstop for the narrow window where a `complete` step throws
|
|
18
|
+
* without saying whether its write applied.
|
|
19
|
+
*/
|
|
20
|
+
export class TaskAlreadyTerminalError extends Error {
|
|
21
|
+
constructor(cause) {
|
|
22
|
+
super("the terminal Task was saved; the callback after it failed", {
|
|
23
|
+
cause
|
|
24
|
+
});
|
|
25
|
+
this.name = "TaskAlreadyTerminalError";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Persist a turn's terminal Task, then notify the gatekeeper.
|
|
30
|
+
*
|
|
31
|
+
* **The guarded write is the cancellation check.** `saveTask` refuses to write a
|
|
32
|
+
* terminal state over a `canceled` row and says so, doing that read and write in
|
|
33
|
+
* one synchronous pass inside the Durable Object. Probing first and saving
|
|
34
|
+
* second would leave a window — between the two calls, and again between this
|
|
35
|
+
* step and `notify` — in which a `tasks/cancel` lands and the gatekeeper still
|
|
36
|
+
* receives a `completed` callback. Keying the notify on "did the write apply"
|
|
37
|
+
* closes it, and that has already been got wrong once.
|
|
38
|
+
*
|
|
39
|
+
* In `/a2a` rather than `/round` because the shape is not a round's: an agent
|
|
40
|
+
* whose turn is a single inference ends it the same way, and importing this from
|
|
41
|
+
* `/round` would put the whole delegation engine in a bundle that must not carry
|
|
42
|
+
* it. Everything it calls already lives here.
|
|
43
|
+
*/
|
|
44
|
+
export async function deliverTerminalTask(step, options) {
|
|
45
|
+
const at = options.stepPrefix ?? "";
|
|
46
|
+
const task = await step.do(`${at}complete`, async () => {
|
|
47
|
+
const terminal = options.terminal();
|
|
48
|
+
return (await options.saveTask(terminal)) ? terminal : null;
|
|
49
|
+
});
|
|
50
|
+
if (!task)
|
|
51
|
+
return;
|
|
52
|
+
// Caught, not left to propagate: the terminal Task is already durably saved,
|
|
53
|
+
// so a sweep that still fails once the step's own retries are exhausted must
|
|
54
|
+
// not block the callback — the gatekeeper is owed its result either way.
|
|
55
|
+
if (options.sweep) {
|
|
56
|
+
const sweep = options.sweep;
|
|
57
|
+
try {
|
|
58
|
+
await step.do(`${at}sweep`, async () => {
|
|
59
|
+
await sweep();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
console.error("[deliver] sweep failed after retries", {
|
|
64
|
+
taskId: options.push.taskId,
|
|
65
|
+
err: String(err)
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// A card-key-signed callback POST. Retried by the step on a non-2xx; the
|
|
70
|
+
// terminal messageId is deterministic and the gatekeeper is idempotent and
|
|
71
|
+
// single-use, so retries are safe. If it ultimately fails, the gatekeeper's own
|
|
72
|
+
// reaction backstop clears the pending marker.
|
|
73
|
+
//
|
|
74
|
+
// Wrapped, because by this line the Task is durably terminal: anything that
|
|
75
|
+
// fails from here is a callback that did not land, never a turn that produced
|
|
76
|
+
// nothing. See {@link TaskAlreadyTerminalError}.
|
|
77
|
+
try {
|
|
78
|
+
await step.do(`${at}notify`, async () => {
|
|
79
|
+
await createPushChannel(options.signingKey, options.push).deliver(task);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
throw new TaskAlreadyTerminalError(err);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The step-name prefix an abandoned delivery runs under.
|
|
88
|
+
*
|
|
89
|
+
* Its own namespace so it can never collide with the ordinary delivery's — see
|
|
90
|
+
* {@link DeliverTerminalOptions.stepPrefix}.
|
|
91
|
+
*/
|
|
92
|
+
const ABANDONED_PREFIX = "abandoned:";
|
|
93
|
+
/**
|
|
94
|
+
* Turn an unrecoverable orchestration fault into a delivered failed Task.
|
|
95
|
+
*
|
|
96
|
+
* ## The failure this exists for
|
|
97
|
+
*
|
|
98
|
+
* A turn ends badly in two different ways. A **typed** failure — the models were
|
|
99
|
+
* tried and nothing usable came back — is a value, and the ordinary delivery
|
|
100
|
+
* path carries it. A **transient** fault instead throws, so the step retries and
|
|
101
|
+
* recovers without paying for a second inference. That is the right default.
|
|
102
|
+
*
|
|
103
|
+
* What neither covers is a transient fault that never stops being one. `step.do`
|
|
104
|
+
* retries a bounded number of times and then rethrows, and with nothing above it
|
|
105
|
+
* to catch that, the orchestration unwinds, the delivery path is never reached,
|
|
106
|
+
* and the Workflow instance errors with the Task still sitting in `working`. The
|
|
107
|
+
* user is told nothing at all — and because the instance dies mid-`run`, the
|
|
108
|
+
* runtime records it as *"your Worker's code had hung and would never generate a
|
|
109
|
+
* response"*, which reads like a bug in the workflow rather than a provider that
|
|
110
|
+
* was refusing every request.
|
|
111
|
+
*
|
|
112
|
+
* Observed exactly that way in a deployed agent on 2026-08-19: a turn step
|
|
113
|
+
* exhausted its four attempts against a rate-limited provider, and the agent
|
|
114
|
+
* simply went quiet.
|
|
115
|
+
*
|
|
116
|
+
* ## Three behaviours here are load-bearing
|
|
117
|
+
*
|
|
118
|
+
* **It resolves after a successful delivery — it does not rethrow.** Rethrowing
|
|
119
|
+
* would reproduce the very "hung Worker" record this exists to remove, and the
|
|
120
|
+
* instance genuinely has finished its job: the Task is terminal and the gatekeeper
|
|
121
|
+
* has been told.
|
|
122
|
+
*
|
|
123
|
+
* **It rethrows the *original* `cause` when the delivery itself fails.**
|
|
124
|
+
* Swallowing there would mark the instance successful while the user got
|
|
125
|
+
* nothing — strictly worse than the erroring instance being replaced, because it
|
|
126
|
+
* would also be silent in the Workflows console. `cause` is what an operator
|
|
127
|
+
* needs to see, not the delivery's own secondary fault.
|
|
128
|
+
*
|
|
129
|
+
* **It refuses to run at all once a terminal Task exists.** A
|
|
130
|
+
* {@link TaskAlreadyTerminalError} means the ordinary delivery persisted its
|
|
131
|
+
* result and only the callback failed, so the original fault is rethrown and
|
|
132
|
+
* nothing is written. An earlier draft of this claimed the guarded write alone
|
|
133
|
+
* made that safe; it did not. The write refused only cancellation conflicts, so
|
|
134
|
+
* `completed → failed` applied cleanly and a turn that succeeded would have been
|
|
135
|
+
* rewritten as a generic failure because a webhook was flaky. The guard now
|
|
136
|
+
* refuses any terminal-over-different-terminal write as a backstop, and this
|
|
137
|
+
* check is what stops the attempt being made in the first place — without it the
|
|
138
|
+
* recovery would quietly succeed at doing nothing, swallowing a real callback
|
|
139
|
+
* failure and logging "abandoned" about a completed turn.
|
|
140
|
+
*
|
|
141
|
+
* ## Why it is exported rather than private to `/round`
|
|
142
|
+
*
|
|
143
|
+
* `runHandleTask` wraps itself in it, so every round agent gets this without
|
|
144
|
+
* asking. But an agent whose turn is a single inference writes its own workflow
|
|
145
|
+
* body and has the identical exposure — and the alternative to exporting this is
|
|
146
|
+
* that each such host reimplements the delivery, which is precisely the mistake
|
|
147
|
+
* this function was extracted from.
|
|
148
|
+
*/
|
|
149
|
+
export async function deliverAbandonedTask(step, cause, options) {
|
|
150
|
+
// Nothing was abandoned: the ordinary delivery already persisted a terminal
|
|
151
|
+
// Task and only its callback failed. Rethrow the fault that actually
|
|
152
|
+
// happened, so the instance still errors exactly as it did before any
|
|
153
|
+
// recovery existed and the gatekeeper's own backstop clears the pending marker.
|
|
154
|
+
//
|
|
155
|
+
// Checked **here** rather than in each caller's `catch` on purpose. This whole
|
|
156
|
+
// change exists because a recovery every host had to remember to wire was one
|
|
157
|
+
// three hosts forgot; a caveat every host had to remember to check would be
|
|
158
|
+
// the same mistake a second time.
|
|
159
|
+
if (cause instanceof TaskAlreadyTerminalError)
|
|
160
|
+
throw cause.cause;
|
|
161
|
+
const label = options.label ?? "agent";
|
|
162
|
+
// Logged before anything is attempted: if the delivery below also fails, this
|
|
163
|
+
// line is the only record of what actually went wrong.
|
|
164
|
+
console.error(`[${label}] task abandoned after retries were exhausted`, {
|
|
165
|
+
taskId: options.push.taskId,
|
|
166
|
+
error: String(cause)
|
|
167
|
+
});
|
|
168
|
+
try {
|
|
169
|
+
await deliverTerminalTask(step, {
|
|
170
|
+
push: options.push,
|
|
171
|
+
signingKey: options.signingKey,
|
|
172
|
+
saveTask: options.saveTask,
|
|
173
|
+
terminal: () => buildFailedTask(options.push.taskId, options.push.contextId, options.text),
|
|
174
|
+
sweep: options.sweep,
|
|
175
|
+
stepPrefix: ABANDONED_PREFIX
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
catch (deliveryFailed) {
|
|
179
|
+
console.error(`[${label}] could not deliver the failed Task`, {
|
|
180
|
+
taskId: options.push.taskId,
|
|
181
|
+
error: String(deliveryFailed)
|
|
182
|
+
});
|
|
183
|
+
throw cause;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { type AgentExecutor, type ExecutionEventBus, type RequestContext } from "@a2a-js/sdk/server";
|
|
2
|
+
import type { GatekeeperIdentity } from "./verify.js";
|
|
3
|
+
import type { AgentResolver } from "./agent-stub.js";
|
|
4
|
+
/**
|
|
5
|
+
* Derive a deterministic workflow instance id for a turn. Keyed on the gatekeeper's
|
|
6
|
+
* `messageId` (stable across dispatch retries), so re-creating it is a no-op —
|
|
7
|
+
* the turn runs exactly once. Sanitized to the id charset.
|
|
8
|
+
*/
|
|
9
|
+
export declare function workflowIdForMessage(messageId: string, prefix?: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Everything the executor knows about an accepted turn, handed to the
|
|
12
|
+
* consumer's {@link TurnStarter}.
|
|
13
|
+
*
|
|
14
|
+
* Core stops here deliberately: which workflow runs the turn, what else its
|
|
15
|
+
* params carry, and which binding it is created on are all the agent's, so core
|
|
16
|
+
* describes the turn and the agent starts it.
|
|
17
|
+
*/
|
|
18
|
+
export interface AcceptedTurn {
|
|
19
|
+
/** The gatekeeper's message id — the idempotency key for the whole turn. */
|
|
20
|
+
messageId: string;
|
|
21
|
+
taskId: string;
|
|
22
|
+
contextId: string;
|
|
23
|
+
/** The caller's message, flattened to text. */
|
|
24
|
+
text: string;
|
|
25
|
+
identity: GatekeeperIdentity;
|
|
26
|
+
/** Webhook the terminal task is POSTed to. */
|
|
27
|
+
pushUrl: string;
|
|
28
|
+
/** Per-task validation token echoed on the callback. */
|
|
29
|
+
pushToken: string;
|
|
30
|
+
/** This agent's card-signing JWKS URL — the callback JWT `jku`. */
|
|
31
|
+
jku: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Start the durable turn. **Must be idempotent**: a dispatch retry calls it
|
|
35
|
+
* again with the same {@link AcceptedTurn.messageId}, and the conventional
|
|
36
|
+
* implementation swallows the workflow's "instance already exists" race — see
|
|
37
|
+
* {@link workflowIdForMessage} and {@link ignoreAlreadyExists}.
|
|
38
|
+
*/
|
|
39
|
+
export type TurnStarter = (turn: AcceptedTurn) => Promise<void>;
|
|
40
|
+
export interface ExecutorConfig {
|
|
41
|
+
identity: GatekeeperIdentity;
|
|
42
|
+
/** This agent's card-signing JWKS URL — the callback JWT `jku`. */
|
|
43
|
+
jku: string;
|
|
44
|
+
resolveAgent: AgentResolver;
|
|
45
|
+
startTurn: TurnStarter;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Run `create` and swallow the "instance already exists" retry race, so a
|
|
49
|
+
* {@link TurnStarter} is idempotent in the one way that actually happens.
|
|
50
|
+
*
|
|
51
|
+
* ```ts
|
|
52
|
+
* startTurn: (turn) =>
|
|
53
|
+
* ignoreAlreadyExists(() =>
|
|
54
|
+
* env.HANDLE_TASK_WORKFLOW.create({
|
|
55
|
+
* id: workflowIdForMessage(turn.messageId),
|
|
56
|
+
* params: { ...turn }
|
|
57
|
+
* })
|
|
58
|
+
* )
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export declare function ignoreAlreadyExists(create: () => Promise<unknown>): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* A2A executor for the **async accept + notify** contract. On `SendMessage` it
|
|
64
|
+
* does not block on generation: it records a `submitted` task in the caller's DO
|
|
65
|
+
* (idempotent on `messageId`), hands the turn to a durable workflow, and
|
|
66
|
+
* publishes the accepted task immediately as the response. The workflow
|
|
67
|
+
* generates the reply and POSTs it to the gatekeeper's push-notification webhook
|
|
68
|
+
* out of band.
|
|
69
|
+
*
|
|
70
|
+
* The verified caller identity comes from the config — the outer Worker builds
|
|
71
|
+
* one executor per verified request. The push config comes from the request
|
|
72
|
+
* itself: v1.0 hands the executor the whole `SendMessageRequest` via
|
|
73
|
+
* {@link RequestContext.request}, so nothing has to be threaded around it.
|
|
74
|
+
*/
|
|
75
|
+
export declare class A2AExecutor implements AgentExecutor {
|
|
76
|
+
private readonly config;
|
|
77
|
+
constructor(config: ExecutorConfig);
|
|
78
|
+
execute: (requestContext: RequestContext, eventBus: ExecutionEventBus) => Promise<void>;
|
|
79
|
+
/**
|
|
80
|
+
* `CancelTask`: best-effort mark the task canceled in the DO and publish the
|
|
81
|
+
* canceled task. The in-flight workflow's `notify` step skips a canceled task.
|
|
82
|
+
*/
|
|
83
|
+
cancelTask: (taskId: string, eventBus: ExecutionEventBus) => Promise<void>;
|
|
84
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { AgentEvent } from "@a2a-js/sdk/server";
|
|
2
|
+
import { textOf } from "./parts.js";
|
|
3
|
+
/**
|
|
4
|
+
* Derive a deterministic workflow instance id for a turn. Keyed on the gatekeeper's
|
|
5
|
+
* `messageId` (stable across dispatch retries), so re-creating it is a no-op —
|
|
6
|
+
* the turn runs exactly once. Sanitized to the id charset.
|
|
7
|
+
*/
|
|
8
|
+
export function workflowIdForMessage(messageId, prefix = "turn") {
|
|
9
|
+
return `${prefix}-${messageId.replace(/[^A-Za-z0-9_-]/g, "-")}`;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Run `create` and swallow the "instance already exists" retry race, so a
|
|
13
|
+
* {@link TurnStarter} is idempotent in the one way that actually happens.
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* startTurn: (turn) =>
|
|
17
|
+
* ignoreAlreadyExists(() =>
|
|
18
|
+
* env.HANDLE_TASK_WORKFLOW.create({
|
|
19
|
+
* id: workflowIdForMessage(turn.messageId),
|
|
20
|
+
* params: { ...turn }
|
|
21
|
+
* })
|
|
22
|
+
* )
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export async function ignoreAlreadyExists(create) {
|
|
26
|
+
try {
|
|
27
|
+
await create();
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
if (err instanceof Error && /already exists|exist/i.test(err.message)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A2A executor for the **async accept + notify** contract. On `SendMessage` it
|
|
38
|
+
* does not block on generation: it records a `submitted` task in the caller's DO
|
|
39
|
+
* (idempotent on `messageId`), hands the turn to a durable workflow, and
|
|
40
|
+
* publishes the accepted task immediately as the response. The workflow
|
|
41
|
+
* generates the reply and POSTs it to the gatekeeper's push-notification webhook
|
|
42
|
+
* out of band.
|
|
43
|
+
*
|
|
44
|
+
* The verified caller identity comes from the config — the outer Worker builds
|
|
45
|
+
* one executor per verified request. The push config comes from the request
|
|
46
|
+
* itself: v1.0 hands the executor the whole `SendMessageRequest` via
|
|
47
|
+
* {@link RequestContext.request}, so nothing has to be threaded around it.
|
|
48
|
+
*/
|
|
49
|
+
export class A2AExecutor {
|
|
50
|
+
config;
|
|
51
|
+
constructor(config) {
|
|
52
|
+
this.config = config;
|
|
53
|
+
}
|
|
54
|
+
execute = async (requestContext, eventBus) => {
|
|
55
|
+
const pushConfig = requestContext.request.configuration?.taskPushNotificationConfig;
|
|
56
|
+
// Defensive: the Worker validates url + token before the executor runs, and
|
|
57
|
+
// must keep doing so — a throw here is turned into a `failed` task by the
|
|
58
|
+
// request handler, not into the JSON-RPC error the caller needs to see.
|
|
59
|
+
if (!pushConfig?.url || !pushConfig.token) {
|
|
60
|
+
throw new Error("taskPushNotificationConfig url and token are required");
|
|
61
|
+
}
|
|
62
|
+
const text = textOf(requestContext.userMessage);
|
|
63
|
+
const messageId = requestContext.userMessage.messageId;
|
|
64
|
+
const contextId = requestContext.contextId;
|
|
65
|
+
// `identity.key` is guaranteed non-null: the Worker rejects a keyless
|
|
66
|
+
// identity (400) before constructing this executor.
|
|
67
|
+
const stub = this.config.resolveAgent(this.config.identity);
|
|
68
|
+
// Record (or reuse) the submitted task, then start the durable turn. Both
|
|
69
|
+
// are idempotent, so a dispatch retry heals a crash between the two.
|
|
70
|
+
const accepted = await stub.beginTask({
|
|
71
|
+
messageId,
|
|
72
|
+
taskId: requestContext.taskId,
|
|
73
|
+
contextId
|
|
74
|
+
});
|
|
75
|
+
// Widened in one explicit step: DO-stub returns come back through
|
|
76
|
+
// Cloudflare's RPC type mapping, and letting that mapped type flow into a
|
|
77
|
+
// generic SDK call site instead exceeds TypeScript's instantiation depth on
|
|
78
|
+
// the v1.0 (proto-generated) model.
|
|
79
|
+
const task = accepted;
|
|
80
|
+
await this.config.startTurn({
|
|
81
|
+
messageId,
|
|
82
|
+
taskId: accepted.id,
|
|
83
|
+
contextId,
|
|
84
|
+
text,
|
|
85
|
+
identity: this.config.identity,
|
|
86
|
+
pushUrl: pushConfig.url,
|
|
87
|
+
pushToken: pushConfig.token,
|
|
88
|
+
jku: this.config.jku
|
|
89
|
+
});
|
|
90
|
+
// The accept ack: a `submitted` task, not a Message. Returned synchronously.
|
|
91
|
+
eventBus.publish(AgentEvent.task(task));
|
|
92
|
+
eventBus.finished();
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* `CancelTask`: best-effort mark the task canceled in the DO and publish the
|
|
96
|
+
* canceled task. The in-flight workflow's `notify` step skips a canceled task.
|
|
97
|
+
*/
|
|
98
|
+
cancelTask = async (taskId, eventBus) => {
|
|
99
|
+
const task = await this.config
|
|
100
|
+
.resolveAgent(this.config.identity)
|
|
101
|
+
.cancelTask(taskId);
|
|
102
|
+
if (task)
|
|
103
|
+
eventBus.publish(AgentEvent.task(task));
|
|
104
|
+
eventBus.finished();
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@dynamicagents/core/a2a` — the A2A protocol adapter.
|
|
3
|
+
*
|
|
4
|
+
* The zero-trust contract in both directions (verify the gatekeeper with its public
|
|
5
|
+
* JWKS, prove ourselves with ours), the accept-and-notify task lifecycle, and the
|
|
6
|
+
* narrowed task types that survive Durable Object RPC. Nothing here knows what an
|
|
7
|
+
* agent *does* — past {@link A2AExecutor} everything is plain strings.
|
|
8
|
+
*
|
|
9
|
+
* ## The wire values come from `@dynamicagents/g2a-protocol`
|
|
10
|
+
*
|
|
11
|
+
* Claim names, the algorithm, the well-known paths and the audience rule are a
|
|
12
|
+
* two-sided contract with a token issuer, and the issuer — slack-gatekeeper — is
|
|
13
|
+
* not an agent and must not import this package. They live in a zero-dependency
|
|
14
|
+
* leaf both sides depend on, and are re-exported here so an agent's imports are
|
|
15
|
+
* unchanged: `IDENTITY_CLAIM`, `TENANT_CLAIM`, `A2A_RPC_PATH` and
|
|
16
|
+
* `GatekeeperIdentity` still come from `@dynamicagents/core/a2a`.
|
|
17
|
+
*
|
|
18
|
+
* The pure helpers are re-exported too, because an agent that builds a URL by
|
|
19
|
+
* hand is an agent that can disagree with the gatekeeper about it.
|
|
20
|
+
*/
|
|
21
|
+
export { A2A_JWS_ALG, audienceFor, endpointUrl, jwksUrl } from "@dynamicagents/g2a-protocol";
|
|
22
|
+
export { IDENTITY_CLAIM, TENANT_CLAIM, GatekeeperAuthError, bearerToken, normalizeGatekeeperOrigins, verifyGatekeeperToken, type GatekeeperIdentity, type VerifyOptions } from "./verify.js";
|
|
23
|
+
export { A2A_RPC_PATH, buildBaseCard, signCard, wireCard, parsePrivateJwk, publicCardJwks, type AgentManifest, type BuildCardOptions, type CardSigningConfig, type WireAgentCard } from "./card.js";
|
|
24
|
+
export { NOTIFICATION_TOKEN_HEADER, buildSubmittedTask, buildWorkingTask, buildCompletedTask, buildFailedTask, buildNoReplyCompletedTask, signCallbackJwt, postNotification } from "./notify.js";
|
|
25
|
+
export { deliverTerminalTask, deliverAbandonedTask, TaskAlreadyTerminalError, type DeliverTerminalOptions, type AbandonedTaskOptions } from "./deliver.js";
|
|
26
|
+
export { signCallerToken, type CallerTokenOptions } from "./caller-token.js";
|
|
27
|
+
export { SelfOrigin } from "./self-origin.js";
|
|
28
|
+
export { callerContext } from "./caller.js";
|
|
29
|
+
export { createPushChannel, type PushChannel, type TurnPushContext } from "./push.js";
|
|
30
|
+
export { taskStateLabel, type PlainArtifact, type PlainMessage, type PlainPart, type PlainStatus, type PlainTask } from "./task.js";
|
|
31
|
+
export { textPart, partsText, textOf, agentTextMessage, inboundText, InboundPartError, MAX_INBOUND_TEXT_BYTES } from "./parts.js";
|
|
32
|
+
export { buildCallContext, extensionHeaders } from "./context.js";
|
|
33
|
+
export { DurableTaskStore } from "./task-store.js";
|
|
34
|
+
export { A2AExecutor, workflowIdForMessage, ignoreAlreadyExists, type AcceptedTurn, type ExecutorConfig, type TurnStarter } from "./executor.js";
|
|
35
|
+
export type { AgentResolver, TaskAgent, TaskListPage, TaskListQuery } from "./agent-stub.js";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@dynamicagents/core/a2a` — the A2A protocol adapter.
|
|
3
|
+
*
|
|
4
|
+
* The zero-trust contract in both directions (verify the gatekeeper with its public
|
|
5
|
+
* JWKS, prove ourselves with ours), the accept-and-notify task lifecycle, and the
|
|
6
|
+
* narrowed task types that survive Durable Object RPC. Nothing here knows what an
|
|
7
|
+
* agent *does* — past {@link A2AExecutor} everything is plain strings.
|
|
8
|
+
*
|
|
9
|
+
* ## The wire values come from `@dynamicagents/g2a-protocol`
|
|
10
|
+
*
|
|
11
|
+
* Claim names, the algorithm, the well-known paths and the audience rule are a
|
|
12
|
+
* two-sided contract with a token issuer, and the issuer — slack-gatekeeper — is
|
|
13
|
+
* not an agent and must not import this package. They live in a zero-dependency
|
|
14
|
+
* leaf both sides depend on, and are re-exported here so an agent's imports are
|
|
15
|
+
* unchanged: `IDENTITY_CLAIM`, `TENANT_CLAIM`, `A2A_RPC_PATH` and
|
|
16
|
+
* `GatekeeperIdentity` still come from `@dynamicagents/core/a2a`.
|
|
17
|
+
*
|
|
18
|
+
* The pure helpers are re-exported too, because an agent that builds a URL by
|
|
19
|
+
* hand is an agent that can disagree with the gatekeeper about it.
|
|
20
|
+
*/
|
|
21
|
+
export { A2A_JWS_ALG, audienceFor, endpointUrl, jwksUrl } from "@dynamicagents/g2a-protocol";
|
|
22
|
+
export { IDENTITY_CLAIM, TENANT_CLAIM, GatekeeperAuthError, bearerToken, normalizeGatekeeperOrigins, verifyGatekeeperToken } from "./verify.js";
|
|
23
|
+
export { A2A_RPC_PATH, buildBaseCard, signCard, wireCard, parsePrivateJwk, publicCardJwks } from "./card.js";
|
|
24
|
+
export { NOTIFICATION_TOKEN_HEADER, buildSubmittedTask, buildWorkingTask, buildCompletedTask, buildFailedTask, buildNoReplyCompletedTask, signCallbackJwt, postNotification } from "./notify.js";
|
|
25
|
+
export { deliverTerminalTask, deliverAbandonedTask, TaskAlreadyTerminalError } from "./deliver.js";
|
|
26
|
+
export { signCallerToken } from "./caller-token.js";
|
|
27
|
+
export { SelfOrigin } from "./self-origin.js";
|
|
28
|
+
export { callerContext } from "./caller.js";
|
|
29
|
+
export { createPushChannel } from "./push.js";
|
|
30
|
+
export { taskStateLabel } from "./task.js";
|
|
31
|
+
export { textPart, partsText, textOf, agentTextMessage, inboundText, InboundPartError, MAX_INBOUND_TEXT_BYTES } from "./parts.js";
|
|
32
|
+
export { buildCallContext, extensionHeaders } from "./context.js";
|
|
33
|
+
export { DurableTaskStore } from "./task-store.js";
|
|
34
|
+
export { A2AExecutor, workflowIdForMessage, ignoreAlreadyExists } from "./executor.js";
|