@groundnuty/macf-channel-server 0.2.28 → 0.2.32

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.
@@ -0,0 +1,297 @@
1
+ /**
2
+ * A2A v1.0 task lifecycle state machine + in-memory `TaskStore`.
3
+ *
4
+ * Per spec § 4.1.1–4.1.3 (Task / TaskStatus / TaskState). State machine
5
+ * declares all v1.0 transitions; Phase 2a exercises only the happy-path
6
+ * subset (SUBMITTED → WORKING → COMPLETED). Phase 2b will exercise
7
+ * INPUT_REQUIRED / AUTH_REQUIRED + resume via `Message.taskId`.
8
+ *
9
+ * **Persistence**: in-memory only for Phase 2 (per design decision 2 on
10
+ * macf#390). `Map<taskId, Task>` indexed by UUIDv4. Process-exit sweep
11
+ * is implicit (no on-disk state). Phase 2.5 sub-issue if longer-lived
12
+ * persistence becomes a need.
13
+ *
14
+ * **Transition validation**: `transition()` rejects illegal moves
15
+ * (e.g., COMPLETED → WORKING). Sister-shape to Pattern A from
16
+ * `silent-fallback-hazards.md` — result-invariant on the state field,
17
+ * not just exit-code success. The state machine is the authoritative
18
+ * source for "what's allowed."
19
+ */
20
+ import { randomUUID } from 'node:crypto';
21
+ import { TERMINAL_TASK_STATES, INTERRUPTED_TASK_STATES } from './a2a-types.js';
22
+ // ---------------------------------------------------------------------------
23
+ // Transition table — per A2A v1.0 § 4.1.3 (TaskState + lifecycle text)
24
+ // ---------------------------------------------------------------------------
25
+ /**
26
+ * Map of from-state → set of legal to-states.
27
+ *
28
+ * v1.0 canonical transitions:
29
+ *
30
+ * SUBMITTED → WORKING | REJECTED | CANCELED | FAILED
31
+ * WORKING → COMPLETED | FAILED | CANCELED | INPUT_REQUIRED | AUTH_REQUIRED
32
+ * INPUT_REQUIRED → WORKING | CANCELED | FAILED (resume on Message.taskId)
33
+ * AUTH_REQUIRED → WORKING | CANCELED | FAILED (resume on Message.taskId after auth)
34
+ * COMPLETED → (terminal — no transitions)
35
+ * FAILED → (terminal)
36
+ * CANCELED → (terminal)
37
+ * REJECTED → (terminal)
38
+ *
39
+ * REJECTED is a terminal state introduced in v1.0 for "agent declined to
40
+ * process" — distinct from FAILED (which is "agent tried + errored").
41
+ */
42
+ const ALLOWED_TRANSITIONS = {
43
+ TASK_STATE_SUBMITTED: new Set([
44
+ 'TASK_STATE_WORKING',
45
+ 'TASK_STATE_REJECTED',
46
+ 'TASK_STATE_CANCELED',
47
+ 'TASK_STATE_FAILED',
48
+ ]),
49
+ TASK_STATE_WORKING: new Set([
50
+ 'TASK_STATE_COMPLETED',
51
+ 'TASK_STATE_FAILED',
52
+ 'TASK_STATE_CANCELED',
53
+ 'TASK_STATE_INPUT_REQUIRED',
54
+ 'TASK_STATE_AUTH_REQUIRED',
55
+ ]),
56
+ TASK_STATE_INPUT_REQUIRED: new Set([
57
+ 'TASK_STATE_WORKING',
58
+ 'TASK_STATE_CANCELED',
59
+ 'TASK_STATE_FAILED',
60
+ ]),
61
+ TASK_STATE_AUTH_REQUIRED: new Set([
62
+ 'TASK_STATE_WORKING',
63
+ 'TASK_STATE_CANCELED',
64
+ 'TASK_STATE_FAILED',
65
+ ]),
66
+ TASK_STATE_COMPLETED: new Set(),
67
+ TASK_STATE_FAILED: new Set(),
68
+ TASK_STATE_CANCELED: new Set(),
69
+ TASK_STATE_REJECTED: new Set(),
70
+ };
71
+ /** True iff `from → to` is a legal transition per the v1.0 spec table. */
72
+ export function isTransitionAllowed(from, to) {
73
+ return ALLOWED_TRANSITIONS[from]?.has(to) ?? false;
74
+ }
75
+ /** Error class for illegal state transitions. */
76
+ export class InvalidTaskTransitionError extends Error {
77
+ from;
78
+ to;
79
+ taskId;
80
+ code = 'INVALID_TASK_TRANSITION';
81
+ constructor(from, to, taskId) {
82
+ super(`Illegal task transition ${from} → ${to} for task ${taskId}`);
83
+ this.from = from;
84
+ this.to = to;
85
+ this.taskId = taskId;
86
+ this.name = 'InvalidTaskTransitionError';
87
+ }
88
+ }
89
+ /** Error class for resume attempts against unknown task ids (macf#392 Phase 2b). */
90
+ export class TaskNotFoundError extends Error {
91
+ taskId;
92
+ code = 'TASK_NOT_FOUND';
93
+ constructor(taskId) {
94
+ super(`Task not found: ${taskId}`);
95
+ this.taskId = taskId;
96
+ this.name = 'TaskNotFoundError';
97
+ }
98
+ }
99
+ /** Error class for resume attempts against non-resumable from-states (macf#392 Phase 2b). */
100
+ export class TaskNotResumableError extends Error {
101
+ taskId;
102
+ currentState;
103
+ code = 'TASK_NOT_RESUMABLE';
104
+ constructor(taskId, currentState) {
105
+ super(`Task ${taskId} in state ${currentState} is not resumable (must be INPUT_REQUIRED or AUTH_REQUIRED)`);
106
+ this.taskId = taskId;
107
+ this.currentState = currentState;
108
+ this.name = 'TaskNotResumableError';
109
+ }
110
+ }
111
+ /** Error class for cancel attempts against terminal tasks (macf#398 Phase 2d). */
112
+ export class TaskNotCancelableError extends Error {
113
+ taskId;
114
+ currentState;
115
+ code = 'TASK_NOT_CANCELABLE';
116
+ constructor(taskId, currentState) {
117
+ super(`Task ${taskId} in state ${currentState} cannot be canceled (already terminal)`);
118
+ this.taskId = taskId;
119
+ this.currentState = currentState;
120
+ this.name = 'TaskNotCancelableError';
121
+ }
122
+ }
123
+ // ---------------------------------------------------------------------------
124
+ // TaskStore — in-memory; Phase 2 scope
125
+ // ---------------------------------------------------------------------------
126
+ /**
127
+ * In-memory task store. NOT durable across channel-server restarts;
128
+ * Phase 2.5 will revisit if longer-lived persistence becomes a need.
129
+ *
130
+ * Concurrency: single-threaded Node.js event loop; no locking needed
131
+ * between request handlers. Map operations are atomic per-event-loop-turn.
132
+ */
133
+ export class TaskStore {
134
+ #tasks = new Map();
135
+ /**
136
+ * Create a fresh task in SUBMITTED state. Returns the task; caller
137
+ * is responsible for any subsequent transition.
138
+ *
139
+ * `contextId` is propagated from the inbound Message if present
140
+ * (per spec § 4.1.4 — contextId associates with a conversational
141
+ * group); otherwise undefined.
142
+ */
143
+ create(initialMessage, opts) {
144
+ const id = randomUUID();
145
+ const task = {
146
+ id,
147
+ status: {
148
+ state: 'TASK_STATE_SUBMITTED',
149
+ timestamp: opts.nowIso,
150
+ },
151
+ contextId: initialMessage.contextId,
152
+ history: [initialMessage],
153
+ };
154
+ this.#tasks.set(id, task);
155
+ return task;
156
+ }
157
+ /**
158
+ * Look up a task by ID. Used for resume flows (Message.taskId set)
159
+ * + idempotent retries. Returns `undefined` if no such task.
160
+ */
161
+ get(taskId) {
162
+ return this.#tasks.get(taskId);
163
+ }
164
+ /**
165
+ * Apply a state transition. Validates against the spec table; throws
166
+ * `InvalidTaskTransitionError` on illegal moves. Returns the updated
167
+ * task (mutated in-place + immutable references returned for callers).
168
+ *
169
+ * Side effect: updates `task.status.state` + `task.status.timestamp`.
170
+ * `task.status.message` is set when the caller provides an
171
+ * accompanying message (e.g., agent's response).
172
+ */
173
+ transition(taskId, to, opts) {
174
+ const task = this.#tasks.get(taskId);
175
+ if (task === undefined) {
176
+ throw new InvalidTaskTransitionError('TASK_STATE_SUBMITTED', to, taskId);
177
+ }
178
+ const from = task.status.state;
179
+ if (!isTransitionAllowed(from, to)) {
180
+ throw new InvalidTaskTransitionError(from, to, taskId);
181
+ }
182
+ const updated = {
183
+ ...task,
184
+ status: {
185
+ state: to,
186
+ timestamp: opts.nowIso,
187
+ ...(opts.message !== undefined ? { message: opts.message } : {}),
188
+ },
189
+ ...(opts.message !== undefined
190
+ ? { history: [...(task.history ?? []), opts.message] }
191
+ : {}),
192
+ };
193
+ this.#tasks.set(taskId, updated);
194
+ return updated;
195
+ }
196
+ /** True iff a task's current state is terminal. */
197
+ isTerminal(taskId) {
198
+ const task = this.#tasks.get(taskId);
199
+ if (task === undefined)
200
+ return false;
201
+ return TERMINAL_TASK_STATES.has(task.status.state);
202
+ }
203
+ /** Diagnostic: count of currently-tracked tasks. */
204
+ size() {
205
+ return this.#tasks.size;
206
+ }
207
+ /**
208
+ * Phase 2a helper: drive a fresh task through the happy path
209
+ * SUBMITTED → WORKING → COMPLETED in one call. Returns the final
210
+ * task. Used by the inbound `message/send` route when the message
211
+ * is a non-resume submission with no intermediate-state needs.
212
+ *
213
+ * `responseMessage` is the agent's reply (role=ROLE_AGENT) attached
214
+ * to the COMPLETED status. Phase 2b will replace this with a real
215
+ * dispatch to MACF tooling that can return INPUT_REQUIRED.
216
+ */
217
+ completeHappyPath(initialMessage, responseMessage, opts) {
218
+ const created = this.create(initialMessage, opts);
219
+ this.transition(created.id, 'TASK_STATE_WORKING', opts);
220
+ return this.transition(created.id, 'TASK_STATE_COMPLETED', {
221
+ nowIso: opts.nowIso,
222
+ message: responseMessage,
223
+ });
224
+ }
225
+ /**
226
+ * Resume a task currently in INPUT_REQUIRED or AUTH_REQUIRED state
227
+ * (macf#392 Phase 2b). Validates ROLE_USER on the resume message
228
+ * (canonical client→server direction per spec § 4.1.5), the
229
+ * from-state is resumable, and transitions back to WORKING with
230
+ * the resume message appended to history.
231
+ *
232
+ * Errors:
233
+ * - `TaskNotFoundError` if `taskId` doesn't match any tracked task
234
+ * - `TaskNotResumableError` if from-state isn't INPUT_REQUIRED or AUTH_REQUIRED
235
+ * - `Error` (generic) if message.role !== 'ROLE_USER'
236
+ */
237
+ resume(taskId, resumeMessage, opts) {
238
+ const task = this.#tasks.get(taskId);
239
+ if (task === undefined) {
240
+ throw new TaskNotFoundError(taskId);
241
+ }
242
+ if (!INTERRUPTED_TASK_STATES.has(task.status.state)) {
243
+ throw new TaskNotResumableError(taskId, task.status.state);
244
+ }
245
+ if (resumeMessage.role !== 'ROLE_USER') {
246
+ throw new Error(`Resume Message must have role=ROLE_USER per spec § 4.1.5 (got ${resumeMessage.role})`);
247
+ }
248
+ return this.transition(taskId, 'TASK_STATE_WORKING', {
249
+ nowIso: opts.nowIso,
250
+ message: resumeMessage,
251
+ });
252
+ }
253
+ /**
254
+ * Drive a fresh task to REJECTED state (macf#392 Phase 2b test
255
+ * fixture). SUBMITTED → REJECTED is a legal transition per the
256
+ * canonical proto table. Used by route handler test trigger when
257
+ * the inbound Message text matches a synthetic rejection pattern.
258
+ *
259
+ * NOT a production-policy rejection layer — real refusal logic
260
+ * comes in Phase 3+ when the skill-name → MCP-tool dispatcher
261
+ * needs to refuse certain inputs.
262
+ */
263
+ rejectFresh(initialMessage, reason, opts) {
264
+ const created = this.create(initialMessage, opts);
265
+ return this.transition(created.id, 'TASK_STATE_REJECTED', {
266
+ nowIso: opts.nowIso,
267
+ message: reason,
268
+ });
269
+ }
270
+ /**
271
+ * Cancel a non-terminal task (macf#398 Phase 2d). Cancellable from
272
+ * SUBMITTED / WORKING / INPUT_REQUIRED / AUTH_REQUIRED per the spec
273
+ * transition table; terminal tasks (COMPLETED / FAILED / CANCELED /
274
+ * REJECTED) reject with TaskNotCancelableError.
275
+ *
276
+ * Idempotent on already-CANCELED tasks would be ergonomic, but the
277
+ * spec doesn't mandate it + the transition table makes CANCELED a
278
+ * terminal state — so re-cancel returns the error. Callers can
279
+ * detect the terminal state via tasks/get first if they need to
280
+ * disambiguate.
281
+ *
282
+ * Errors:
283
+ * - `TaskNotFoundError` if `taskId` is unknown
284
+ * - `TaskNotCancelableError` if task is already terminal
285
+ */
286
+ cancel(taskId, opts) {
287
+ const task = this.#tasks.get(taskId);
288
+ if (task === undefined) {
289
+ throw new TaskNotFoundError(taskId);
290
+ }
291
+ if (TERMINAL_TASK_STATES.has(task.status.state)) {
292
+ throw new TaskNotCancelableError(taskId, task.status.state);
293
+ }
294
+ return this.transition(taskId, 'TASK_STATE_CANCELED', { nowIso: opts.nowIso });
295
+ }
296
+ }
297
+ //# sourceMappingURL=a2a-task.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a2a-task.js","sourceRoot":"","sources":["../src/a2a-task.ts"],"names":[],"mappings":"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"}