@cleocode/lafs 1.8.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 +235 -0
- package/dist/schemas/v1/conformance-profiles.json +39 -0
- package/dist/schemas/v1/envelope.schema.json +306 -0
- package/dist/schemas/v1/error-registry.json +162 -0
- package/dist/src/a2a/bindings/grpc.d.ts +67 -0
- package/dist/src/a2a/bindings/grpc.js +148 -0
- package/dist/src/a2a/bindings/http.d.ts +102 -0
- package/dist/src/a2a/bindings/http.js +120 -0
- package/dist/src/a2a/bindings/index.d.ts +35 -0
- package/dist/src/a2a/bindings/index.js +79 -0
- package/dist/src/a2a/bindings/jsonrpc.d.ts +77 -0
- package/dist/src/a2a/bindings/jsonrpc.js +114 -0
- package/dist/src/a2a/bridge.d.ts +175 -0
- package/dist/src/a2a/bridge.js +286 -0
- package/dist/src/a2a/extensions.d.ts +121 -0
- package/dist/src/a2a/extensions.js +205 -0
- package/dist/src/a2a/index.d.ts +40 -0
- package/dist/src/a2a/index.js +76 -0
- package/dist/src/a2a/streaming.d.ts +74 -0
- package/dist/src/a2a/streaming.js +265 -0
- package/dist/src/a2a/task-lifecycle.d.ts +109 -0
- package/dist/src/a2a/task-lifecycle.js +313 -0
- package/dist/src/budgetEnforcement.d.ts +84 -0
- package/dist/src/budgetEnforcement.js +328 -0
- package/dist/src/circuit-breaker/index.d.ts +121 -0
- package/dist/src/circuit-breaker/index.js +249 -0
- package/dist/src/cli.d.ts +16 -0
- package/dist/src/cli.js +63 -0
- package/dist/src/compliance.d.ts +31 -0
- package/dist/src/compliance.js +89 -0
- package/dist/src/conformance.d.ts +7 -0
- package/dist/src/conformance.js +248 -0
- package/dist/src/conformanceProfiles.d.ts +11 -0
- package/dist/src/conformanceProfiles.js +34 -0
- package/dist/src/deprecationRegistry.d.ts +13 -0
- package/dist/src/deprecationRegistry.js +39 -0
- package/dist/src/discovery.d.ts +286 -0
- package/dist/src/discovery.js +350 -0
- package/dist/src/envelope.d.ts +60 -0
- package/dist/src/envelope.js +136 -0
- package/dist/src/errorRegistry.d.ts +28 -0
- package/dist/src/errorRegistry.js +36 -0
- package/dist/src/fieldExtraction.d.ts +67 -0
- package/dist/src/fieldExtraction.js +133 -0
- package/dist/src/flagResolver.d.ts +46 -0
- package/dist/src/flagResolver.js +47 -0
- package/dist/src/flagSemantics.d.ts +16 -0
- package/dist/src/flagSemantics.js +45 -0
- package/dist/src/health/index.d.ts +105 -0
- package/dist/src/health/index.js +220 -0
- package/dist/src/index.d.ts +24 -0
- package/dist/src/index.js +34 -0
- package/dist/src/mcpAdapter.d.ts +28 -0
- package/dist/src/mcpAdapter.js +281 -0
- package/dist/src/mviProjection.d.ts +19 -0
- package/dist/src/mviProjection.js +116 -0
- package/dist/src/problemDetails.d.ts +34 -0
- package/dist/src/problemDetails.js +45 -0
- package/dist/src/shutdown/index.d.ts +69 -0
- package/dist/src/shutdown/index.js +160 -0
- package/dist/src/tokenEstimator.d.ts +87 -0
- package/dist/src/tokenEstimator.js +238 -0
- package/dist/src/types.d.ts +135 -0
- package/dist/src/types.js +12 -0
- package/dist/src/validateEnvelope.d.ts +15 -0
- package/dist/src/validateEnvelope.js +31 -0
- package/lafs.md +819 -0
- package/package.json +88 -0
- package/schemas/v1/agent-card.schema.json +230 -0
- package/schemas/v1/conformance-profiles.json +39 -0
- package/schemas/v1/context-ledger.schema.json +70 -0
- package/schemas/v1/discovery.schema.json +132 -0
- package/schemas/v1/envelope.schema.json +306 -0
- package/schemas/v1/error-registry.json +162 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LAFS Agent-to-Agent (A2A) Integration v2.0
|
|
3
|
+
*
|
|
4
|
+
* Full integration with the official @a2a-js/sdk for Agent-to-Agent communication.
|
|
5
|
+
* Implements A2A Protocol v1.0+ specification.
|
|
6
|
+
*
|
|
7
|
+
* Reference: specs/external/specification.md
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import type { AgentCard, Task } from '@cleocode/lafs/a2a';
|
|
12
|
+
* import {
|
|
13
|
+
* createLafsArtifact,
|
|
14
|
+
* createTextArtifact,
|
|
15
|
+
* LafsA2AResult,
|
|
16
|
+
* isExtensionRequired
|
|
17
|
+
* } from '@cleocode/lafs/a2a';
|
|
18
|
+
*
|
|
19
|
+
* // Use A2A SDK directly for client operations
|
|
20
|
+
* import { ClientFactory } from '@a2a-js/sdk/client';
|
|
21
|
+
*
|
|
22
|
+
* const factory = new ClientFactory();
|
|
23
|
+
* const client = await factory.createFromUrl('https://agent.example.com');
|
|
24
|
+
* const result = await client.sendMessage({...});
|
|
25
|
+
*
|
|
26
|
+
* // Wrap result with LAFS helpers
|
|
27
|
+
* const lafsResult = new LafsA2AResult(result, {}, 'req-001');
|
|
28
|
+
* const envelope = lafsResult.getLafsEnvelope();
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// Core Exports
|
|
33
|
+
// ============================================================================
|
|
34
|
+
export {
|
|
35
|
+
// Result wrapper
|
|
36
|
+
LafsA2AResult,
|
|
37
|
+
// Artifact helpers
|
|
38
|
+
createLafsArtifact, createTextArtifact, createFileArtifact,
|
|
39
|
+
// Extension helpers
|
|
40
|
+
isExtensionRequired, getExtensionParams,
|
|
41
|
+
// Constants
|
|
42
|
+
AGENT_CARD_PATH, HTTP_EXTENSION_HEADER, } from './bridge.js';
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// Extensions (T098)
|
|
45
|
+
// ============================================================================
|
|
46
|
+
export {
|
|
47
|
+
// Constants
|
|
48
|
+
LAFS_EXTENSION_URI, A2A_EXTENSIONS_HEADER,
|
|
49
|
+
// Functions
|
|
50
|
+
parseExtensionsHeader, negotiateExtensions, formatExtensionsHeader, buildLafsExtension,
|
|
51
|
+
// Error class
|
|
52
|
+
ExtensionSupportRequiredError,
|
|
53
|
+
// Middleware
|
|
54
|
+
extensionNegotiationMiddleware, } from './extensions.js';
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// Task Lifecycle (T099)
|
|
57
|
+
// ============================================================================
|
|
58
|
+
export {
|
|
59
|
+
// State constants
|
|
60
|
+
TERMINAL_STATES, INTERRUPTED_STATES, VALID_TRANSITIONS,
|
|
61
|
+
// State functions
|
|
62
|
+
isValidTransition, isTerminalState, isInterruptedState,
|
|
63
|
+
// Error classes
|
|
64
|
+
InvalidStateTransitionError, TaskImmutabilityError, TaskNotFoundError, TaskRefinementError,
|
|
65
|
+
// Task manager
|
|
66
|
+
TaskManager,
|
|
67
|
+
// LAFS integration
|
|
68
|
+
attachLafsEnvelope, } from './task-lifecycle.js';
|
|
69
|
+
// ============================================================================
|
|
70
|
+
// Streaming and Async (T101)
|
|
71
|
+
// ============================================================================
|
|
72
|
+
export { TaskEventBus, PushNotificationConfigStore, PushNotificationDispatcher, TaskArtifactAssembler, streamTaskEvents, } from './streaming.js';
|
|
73
|
+
// ============================================================================
|
|
74
|
+
// Protocol Bindings (T100)
|
|
75
|
+
// ============================================================================
|
|
76
|
+
export * from './bindings/index.js';
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A2A streaming and async runtime primitives (T101).
|
|
3
|
+
*
|
|
4
|
+
* Provides an in-memory event bus for task status/artifact updates and
|
|
5
|
+
* push-notification config storage helpers.
|
|
6
|
+
*/
|
|
7
|
+
import type { Artifact, PushNotificationConfig, TaskArtifactUpdateEvent, TaskStatusUpdateEvent } from "@a2a-js/sdk";
|
|
8
|
+
export type TaskStreamEvent = TaskStatusUpdateEvent | TaskArtifactUpdateEvent;
|
|
9
|
+
type StreamListener = (event: TaskStreamEvent) => void;
|
|
10
|
+
/**
|
|
11
|
+
* In-memory event bus for task lifecycle streaming events.
|
|
12
|
+
*/
|
|
13
|
+
export declare class TaskEventBus {
|
|
14
|
+
private history;
|
|
15
|
+
private listeners;
|
|
16
|
+
publishStatusUpdate(event: TaskStatusUpdateEvent): void;
|
|
17
|
+
publishArtifactUpdate(event: TaskArtifactUpdateEvent): void;
|
|
18
|
+
publish(event: TaskStreamEvent): void;
|
|
19
|
+
subscribe(taskId: string, listener: StreamListener): () => void;
|
|
20
|
+
getHistory(taskId: string): TaskStreamEvent[];
|
|
21
|
+
}
|
|
22
|
+
export interface StreamIteratorOptions {
|
|
23
|
+
timeoutMs?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Build an async iterator for real-time task stream events.
|
|
27
|
+
*/
|
|
28
|
+
export declare function streamTaskEvents(bus: TaskEventBus, taskId: string, options?: StreamIteratorOptions): AsyncGenerator<TaskStreamEvent>;
|
|
29
|
+
/**
|
|
30
|
+
* In-memory manager for async push-notification configs.
|
|
31
|
+
*/
|
|
32
|
+
export declare class PushNotificationConfigStore {
|
|
33
|
+
private configs;
|
|
34
|
+
set(taskId: string, configId: string, config: PushNotificationConfig): void;
|
|
35
|
+
get(taskId: string, configId: string): PushNotificationConfig | undefined;
|
|
36
|
+
list(taskId: string): PushNotificationConfig[];
|
|
37
|
+
delete(taskId: string, configId: string): boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface PushNotificationDeliveryResult {
|
|
40
|
+
configId: string;
|
|
41
|
+
ok: boolean;
|
|
42
|
+
status?: number;
|
|
43
|
+
error?: string;
|
|
44
|
+
}
|
|
45
|
+
export type PushTransport = (input: string, init?: {
|
|
46
|
+
method?: string;
|
|
47
|
+
headers?: Record<string, string>;
|
|
48
|
+
body?: string;
|
|
49
|
+
}) => Promise<{
|
|
50
|
+
ok: boolean;
|
|
51
|
+
status: number;
|
|
52
|
+
}>;
|
|
53
|
+
/**
|
|
54
|
+
* Deliver task updates to registered push-notification webhooks.
|
|
55
|
+
*/
|
|
56
|
+
export declare class PushNotificationDispatcher {
|
|
57
|
+
private readonly store;
|
|
58
|
+
private readonly transport;
|
|
59
|
+
constructor(store: PushNotificationConfigStore, transport?: PushTransport);
|
|
60
|
+
dispatch(taskId: string, event: TaskStreamEvent): Promise<PushNotificationDeliveryResult[]>;
|
|
61
|
+
private buildHeaders;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Applies append/lastChunk artifact deltas into task-local snapshots.
|
|
65
|
+
*/
|
|
66
|
+
export declare class TaskArtifactAssembler {
|
|
67
|
+
private artifacts;
|
|
68
|
+
applyUpdate(event: TaskArtifactUpdateEvent): Artifact;
|
|
69
|
+
get(taskId: string, artifactId: string): Artifact | undefined;
|
|
70
|
+
list(taskId: string): Artifact[];
|
|
71
|
+
private mergeArtifact;
|
|
72
|
+
private withLastChunk;
|
|
73
|
+
}
|
|
74
|
+
export {};
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A2A streaming and async runtime primitives (T101).
|
|
3
|
+
*
|
|
4
|
+
* Provides an in-memory event bus for task status/artifact updates and
|
|
5
|
+
* push-notification config storage helpers.
|
|
6
|
+
*/
|
|
7
|
+
function resolveTaskId(event) {
|
|
8
|
+
const candidate = event;
|
|
9
|
+
const taskId = candidate.taskId ?? candidate.task?.id;
|
|
10
|
+
if (!taskId) {
|
|
11
|
+
throw new Error("Task stream event is missing task identifier");
|
|
12
|
+
}
|
|
13
|
+
return taskId;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* In-memory event bus for task lifecycle streaming events.
|
|
17
|
+
*/
|
|
18
|
+
export class TaskEventBus {
|
|
19
|
+
history = new Map();
|
|
20
|
+
listeners = new Map();
|
|
21
|
+
publishStatusUpdate(event) {
|
|
22
|
+
this.publish(event);
|
|
23
|
+
}
|
|
24
|
+
publishArtifactUpdate(event) {
|
|
25
|
+
this.publish(event);
|
|
26
|
+
}
|
|
27
|
+
publish(event) {
|
|
28
|
+
const taskId = resolveTaskId(event);
|
|
29
|
+
const events = this.history.get(taskId) ?? [];
|
|
30
|
+
events.push(event);
|
|
31
|
+
this.history.set(taskId, events);
|
|
32
|
+
const listeners = this.listeners.get(taskId);
|
|
33
|
+
if (listeners) {
|
|
34
|
+
for (const listener of listeners) {
|
|
35
|
+
listener(event);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
subscribe(taskId, listener) {
|
|
40
|
+
let set = this.listeners.get(taskId);
|
|
41
|
+
if (!set) {
|
|
42
|
+
set = new Set();
|
|
43
|
+
this.listeners.set(taskId, set);
|
|
44
|
+
}
|
|
45
|
+
set.add(listener);
|
|
46
|
+
return () => {
|
|
47
|
+
const active = this.listeners.get(taskId);
|
|
48
|
+
if (!active)
|
|
49
|
+
return;
|
|
50
|
+
active.delete(listener);
|
|
51
|
+
if (active.size === 0) {
|
|
52
|
+
this.listeners.delete(taskId);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
getHistory(taskId) {
|
|
57
|
+
return [...(this.history.get(taskId) ?? [])];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Build an async iterator for real-time task stream events.
|
|
62
|
+
*/
|
|
63
|
+
export async function* streamTaskEvents(bus, taskId, options = {}) {
|
|
64
|
+
const queue = [];
|
|
65
|
+
let wakeUp = null;
|
|
66
|
+
const unsubscribe = bus.subscribe(taskId, (event) => {
|
|
67
|
+
queue.push(event);
|
|
68
|
+
if (wakeUp) {
|
|
69
|
+
wakeUp();
|
|
70
|
+
wakeUp = null;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
const timeoutMs = options.timeoutMs ?? 30_000;
|
|
74
|
+
try {
|
|
75
|
+
// Emit existing history first for catch-up behavior.
|
|
76
|
+
for (const event of bus.getHistory(taskId)) {
|
|
77
|
+
yield event;
|
|
78
|
+
}
|
|
79
|
+
while (true) {
|
|
80
|
+
if (queue.length > 0) {
|
|
81
|
+
const event = queue.shift();
|
|
82
|
+
if (event) {
|
|
83
|
+
yield event;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
await new Promise((resolve) => {
|
|
88
|
+
const timer = setTimeout(() => {
|
|
89
|
+
if (wakeUp === resolve) {
|
|
90
|
+
wakeUp = null;
|
|
91
|
+
}
|
|
92
|
+
resolve();
|
|
93
|
+
}, timeoutMs);
|
|
94
|
+
wakeUp = () => {
|
|
95
|
+
clearTimeout(timer);
|
|
96
|
+
resolve();
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
unsubscribe();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* In-memory manager for async push-notification configs.
|
|
107
|
+
*/
|
|
108
|
+
export class PushNotificationConfigStore {
|
|
109
|
+
configs = new Map();
|
|
110
|
+
set(taskId, configId, config) {
|
|
111
|
+
let taskConfigs = this.configs.get(taskId);
|
|
112
|
+
if (!taskConfigs) {
|
|
113
|
+
taskConfigs = new Map();
|
|
114
|
+
this.configs.set(taskId, taskConfigs);
|
|
115
|
+
}
|
|
116
|
+
taskConfigs.set(configId, config);
|
|
117
|
+
}
|
|
118
|
+
get(taskId, configId) {
|
|
119
|
+
return this.configs.get(taskId)?.get(configId);
|
|
120
|
+
}
|
|
121
|
+
list(taskId) {
|
|
122
|
+
return [...(this.configs.get(taskId)?.values() ?? [])];
|
|
123
|
+
}
|
|
124
|
+
delete(taskId, configId) {
|
|
125
|
+
const taskConfigs = this.configs.get(taskId);
|
|
126
|
+
if (!taskConfigs) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
const removed = taskConfigs.delete(configId);
|
|
130
|
+
if (taskConfigs.size === 0) {
|
|
131
|
+
this.configs.delete(taskId);
|
|
132
|
+
}
|
|
133
|
+
return removed;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Deliver task updates to registered push-notification webhooks.
|
|
138
|
+
*/
|
|
139
|
+
export class PushNotificationDispatcher {
|
|
140
|
+
store;
|
|
141
|
+
transport;
|
|
142
|
+
constructor(store, transport = async (input, init) => {
|
|
143
|
+
if (typeof fetch !== "function") {
|
|
144
|
+
throw new Error("Global fetch is not available for push dispatch");
|
|
145
|
+
}
|
|
146
|
+
return fetch(input, init);
|
|
147
|
+
}) {
|
|
148
|
+
this.store = store;
|
|
149
|
+
this.transport = transport;
|
|
150
|
+
}
|
|
151
|
+
async dispatch(taskId, event) {
|
|
152
|
+
const configs = this.store.list(taskId);
|
|
153
|
+
if (configs.length === 0) {
|
|
154
|
+
return [];
|
|
155
|
+
}
|
|
156
|
+
const payload = {
|
|
157
|
+
taskId,
|
|
158
|
+
event,
|
|
159
|
+
timestamp: new Date().toISOString(),
|
|
160
|
+
};
|
|
161
|
+
const deliveries = configs.map(async (config, index) => {
|
|
162
|
+
const configId = config.id ?? `${taskId}:${index}`;
|
|
163
|
+
if (!config.url) {
|
|
164
|
+
return {
|
|
165
|
+
configId,
|
|
166
|
+
ok: false,
|
|
167
|
+
error: "Push notification config is missing url",
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const headers = this.buildHeaders(config);
|
|
171
|
+
try {
|
|
172
|
+
const response = await this.transport(config.url, {
|
|
173
|
+
method: "POST",
|
|
174
|
+
headers,
|
|
175
|
+
body: JSON.stringify(payload),
|
|
176
|
+
});
|
|
177
|
+
return {
|
|
178
|
+
configId,
|
|
179
|
+
ok: response.ok,
|
|
180
|
+
status: response.status,
|
|
181
|
+
...(response.ok ? {} : { error: `HTTP ${response.status}` }),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
return {
|
|
186
|
+
configId,
|
|
187
|
+
ok: false,
|
|
188
|
+
error: error instanceof Error ? error.message : String(error),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
return Promise.all(deliveries);
|
|
193
|
+
}
|
|
194
|
+
buildHeaders(config) {
|
|
195
|
+
const headers = {
|
|
196
|
+
"content-type": "application/json",
|
|
197
|
+
};
|
|
198
|
+
if (config.token) {
|
|
199
|
+
headers["x-a2a-task-token"] = config.token;
|
|
200
|
+
}
|
|
201
|
+
const scheme = config.authentication?.schemes?.[0];
|
|
202
|
+
const credentials = config.authentication?.credentials;
|
|
203
|
+
if (scheme && credentials) {
|
|
204
|
+
headers.authorization = `${scheme} ${credentials}`;
|
|
205
|
+
}
|
|
206
|
+
else if (config.token) {
|
|
207
|
+
headers.authorization = `Bearer ${config.token}`;
|
|
208
|
+
}
|
|
209
|
+
return headers;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Applies append/lastChunk artifact deltas into task-local snapshots.
|
|
214
|
+
*/
|
|
215
|
+
export class TaskArtifactAssembler {
|
|
216
|
+
artifacts = new Map();
|
|
217
|
+
applyUpdate(event) {
|
|
218
|
+
if (!event.artifact?.artifactId) {
|
|
219
|
+
throw new Error("Task artifact update is missing artifactId");
|
|
220
|
+
}
|
|
221
|
+
const taskId = resolveTaskId(event);
|
|
222
|
+
const artifactId = event.artifact.artifactId;
|
|
223
|
+
let taskArtifacts = this.artifacts.get(taskId);
|
|
224
|
+
if (!taskArtifacts) {
|
|
225
|
+
taskArtifacts = new Map();
|
|
226
|
+
this.artifacts.set(taskId, taskArtifacts);
|
|
227
|
+
}
|
|
228
|
+
const prior = taskArtifacts.get(artifactId);
|
|
229
|
+
const merged = this.mergeArtifact(prior, event);
|
|
230
|
+
taskArtifacts.set(artifactId, merged);
|
|
231
|
+
return merged;
|
|
232
|
+
}
|
|
233
|
+
get(taskId, artifactId) {
|
|
234
|
+
return this.artifacts.get(taskId)?.get(artifactId);
|
|
235
|
+
}
|
|
236
|
+
list(taskId) {
|
|
237
|
+
return [...(this.artifacts.get(taskId)?.values() ?? [])];
|
|
238
|
+
}
|
|
239
|
+
mergeArtifact(prior, event) {
|
|
240
|
+
const next = event.artifact;
|
|
241
|
+
const append = Boolean(event.append);
|
|
242
|
+
const lastChunk = Boolean(event.lastChunk);
|
|
243
|
+
if (!append || !prior) {
|
|
244
|
+
return {
|
|
245
|
+
...next,
|
|
246
|
+
metadata: this.withLastChunk(next.metadata, lastChunk),
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
return {
|
|
250
|
+
...prior,
|
|
251
|
+
...next,
|
|
252
|
+
parts: [...(prior.parts ?? []), ...(next.parts ?? [])],
|
|
253
|
+
metadata: this.withLastChunk({
|
|
254
|
+
...(prior.metadata ?? {}),
|
|
255
|
+
...(next.metadata ?? {}),
|
|
256
|
+
}, lastChunk),
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
withLastChunk(metadata, lastChunk) {
|
|
260
|
+
return {
|
|
261
|
+
...(metadata ?? {}),
|
|
262
|
+
"a2a:last_chunk": lastChunk,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A2A Task Lifecycle Management
|
|
3
|
+
*
|
|
4
|
+
* State machine enforcement, task CRUD, and LAFS integration
|
|
5
|
+
* for A2A Protocol v1.0+ compliance.
|
|
6
|
+
*
|
|
7
|
+
* Reference: A2A spec Section 6 (Task Lifecycle)
|
|
8
|
+
*/
|
|
9
|
+
import type { Task, TaskState, Artifact, Message } from '@a2a-js/sdk';
|
|
10
|
+
import type { LAFSEnvelope } from '../types.js';
|
|
11
|
+
/** States from which no further transitions are possible */
|
|
12
|
+
export declare const TERMINAL_STATES: ReadonlySet<TaskState>;
|
|
13
|
+
/** States where the task is paused awaiting external input */
|
|
14
|
+
export declare const INTERRUPTED_STATES: ReadonlySet<TaskState>;
|
|
15
|
+
/** Valid state transitions (adjacency map). Terminal states have empty outgoing sets. */
|
|
16
|
+
export declare const VALID_TRANSITIONS: ReadonlyMap<TaskState, ReadonlySet<TaskState>>;
|
|
17
|
+
/** Check if a transition from one state to another is valid */
|
|
18
|
+
export declare function isValidTransition(from: TaskState, to: TaskState): boolean;
|
|
19
|
+
/** Check if a state is terminal (no further transitions allowed) */
|
|
20
|
+
export declare function isTerminalState(state: TaskState): boolean;
|
|
21
|
+
/** Check if a state is interrupted (paused awaiting input) */
|
|
22
|
+
export declare function isInterruptedState(state: TaskState): boolean;
|
|
23
|
+
/** Thrown when attempting an invalid state transition */
|
|
24
|
+
export declare class InvalidStateTransitionError extends Error {
|
|
25
|
+
readonly taskId: string;
|
|
26
|
+
readonly fromState: TaskState;
|
|
27
|
+
readonly toState: TaskState;
|
|
28
|
+
constructor(taskId: string, fromState: TaskState, toState: TaskState);
|
|
29
|
+
}
|
|
30
|
+
/** Thrown when attempting to modify a task in a terminal state */
|
|
31
|
+
export declare class TaskImmutabilityError extends Error {
|
|
32
|
+
readonly taskId: string;
|
|
33
|
+
readonly terminalState: TaskState;
|
|
34
|
+
constructor(taskId: string, terminalState: TaskState);
|
|
35
|
+
}
|
|
36
|
+
/** Thrown when a task is not found */
|
|
37
|
+
export declare class TaskNotFoundError extends Error {
|
|
38
|
+
readonly taskId: string;
|
|
39
|
+
constructor(taskId: string);
|
|
40
|
+
}
|
|
41
|
+
/** Thrown when a refinement/follow-up task references invalid parent tasks */
|
|
42
|
+
export declare class TaskRefinementError extends Error {
|
|
43
|
+
readonly referenceTaskIds: string[];
|
|
44
|
+
constructor(message: string, referenceTaskIds: string[]);
|
|
45
|
+
}
|
|
46
|
+
/** Options for creating a new task */
|
|
47
|
+
export interface CreateTaskOptions {
|
|
48
|
+
contextId?: string;
|
|
49
|
+
metadata?: Record<string, unknown>;
|
|
50
|
+
referenceTaskIds?: string[];
|
|
51
|
+
parallelFollowUp?: boolean;
|
|
52
|
+
}
|
|
53
|
+
/** Options for listing tasks */
|
|
54
|
+
export interface ListTasksOptions {
|
|
55
|
+
contextId?: string;
|
|
56
|
+
state?: TaskState;
|
|
57
|
+
limit?: number;
|
|
58
|
+
pageToken?: string;
|
|
59
|
+
}
|
|
60
|
+
/** Paginated result from listTasks */
|
|
61
|
+
export interface ListTasksResult {
|
|
62
|
+
tasks: Task[];
|
|
63
|
+
nextPageToken?: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* In-memory task manager implementing A2A task lifecycle.
|
|
67
|
+
* Enforces valid state transitions and terminal state immutability.
|
|
68
|
+
*/
|
|
69
|
+
export declare class TaskManager {
|
|
70
|
+
private tasks;
|
|
71
|
+
private contextIndex;
|
|
72
|
+
/** Create a new task in the submitted state */
|
|
73
|
+
createTask(options?: CreateTaskOptions): Task;
|
|
74
|
+
/** Create a refinement/follow-up task referencing existing task(s). */
|
|
75
|
+
createRefinedTask(referenceTaskIds: string[], options?: Omit<CreateTaskOptions, 'referenceTaskIds'>): Task;
|
|
76
|
+
/** Get a task by ID. Throws TaskNotFoundError if not found. */
|
|
77
|
+
getTask(taskId: string): Task;
|
|
78
|
+
/** List tasks with optional filtering and pagination */
|
|
79
|
+
listTasks(options?: ListTasksOptions): ListTasksResult;
|
|
80
|
+
/**
|
|
81
|
+
* Update task status. Enforces valid transitions and terminal state immutability.
|
|
82
|
+
* @throws InvalidStateTransitionError if the transition is not valid
|
|
83
|
+
* @throws TaskImmutabilityError if the task is in a terminal state
|
|
84
|
+
*/
|
|
85
|
+
updateTaskStatus(taskId: string, state: TaskState, message?: Message): Task;
|
|
86
|
+
/**
|
|
87
|
+
* Add an artifact to a task.
|
|
88
|
+
* @throws TaskImmutabilityError if the task is in a terminal state
|
|
89
|
+
*/
|
|
90
|
+
addArtifact(taskId: string, artifact: Artifact): Task;
|
|
91
|
+
/**
|
|
92
|
+
* Add a message to task history.
|
|
93
|
+
* @throws TaskImmutabilityError if the task is in a terminal state
|
|
94
|
+
*/
|
|
95
|
+
addHistory(taskId: string, message: Message): Task;
|
|
96
|
+
/** Cancel a task by transitioning to canceled state */
|
|
97
|
+
cancelTask(taskId: string): Task;
|
|
98
|
+
/** Get all tasks in a given context */
|
|
99
|
+
getTasksByContext(contextId: string): Task[];
|
|
100
|
+
/** Check if a task is in a terminal state */
|
|
101
|
+
isTerminal(taskId: string): boolean;
|
|
102
|
+
private resolveContextForReferenceTasks;
|
|
103
|
+
private validateReferenceTasks;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Attach a LAFS envelope as an artifact to an A2A task.
|
|
107
|
+
* Uses createLafsArtifact() from bridge.ts to wrap the envelope.
|
|
108
|
+
*/
|
|
109
|
+
export declare function attachLafsEnvelope(manager: TaskManager, taskId: string, envelope: LAFSEnvelope): Task;
|