@mcpmesh/sdk 1.3.4 → 2.0.0-beta.1
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/dist/__tests__/a2a/a2a-bearer.spec.d.ts +2 -0
- package/dist/__tests__/a2a/a2a-bearer.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/a2a-bearer.spec.js +58 -0
- package/dist/__tests__/a2a/a2a-bearer.spec.js.map +1 -0
- package/dist/__tests__/a2a/a2a-client.spec.d.ts +2 -0
- package/dist/__tests__/a2a/a2a-client.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/a2a-client.spec.js +334 -0
- package/dist/__tests__/a2a/a2a-client.spec.js.map +1 -0
- package/dist/__tests__/a2a/a2a-job.spec.d.ts +2 -0
- package/dist/__tests__/a2a/a2a-job.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/a2a-job.spec.js +255 -0
- package/dist/__tests__/a2a/a2a-job.spec.js.map +1 -0
- package/dist/__tests__/a2a/a2a-stream.spec.d.ts +2 -0
- package/dist/__tests__/a2a/a2a-stream.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/a2a-stream.spec.js +278 -0
- package/dist/__tests__/a2a/a2a-stream.spec.js.map +1 -0
- package/dist/__tests__/a2a/agent-a2a-config.spec.d.ts +2 -0
- package/dist/__tests__/a2a/agent-a2a-config.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/agent-a2a-config.spec.js +262 -0
- package/dist/__tests__/a2a/agent-a2a-config.spec.js.map +1 -0
- package/dist/__tests__/a2a/producer/auth-filter.spec.d.ts +2 -0
- package/dist/__tests__/a2a/producer/auth-filter.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/producer/auth-filter.spec.js +127 -0
- package/dist/__tests__/a2a/producer/auth-filter.spec.js.map +1 -0
- package/dist/__tests__/a2a/producer/card-builder.spec.d.ts +2 -0
- package/dist/__tests__/a2a/producer/card-builder.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/producer/card-builder.spec.js +113 -0
- package/dist/__tests__/a2a/producer/card-builder.spec.js.map +1 -0
- package/dist/__tests__/a2a/producer/dispatcher.spec.d.ts +2 -0
- package/dist/__tests__/a2a/producer/dispatcher.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/producer/dispatcher.spec.js +850 -0
- package/dist/__tests__/a2a/producer/dispatcher.spec.js.map +1 -0
- package/dist/__tests__/a2a/producer/mount-surface-push.spec.d.ts +2 -0
- package/dist/__tests__/a2a/producer/mount-surface-push.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/producer/mount-surface-push.spec.js +164 -0
- package/dist/__tests__/a2a/producer/mount-surface-push.spec.js.map +1 -0
- package/dist/__tests__/a2a/producer/mount.spec.d.ts +2 -0
- package/dist/__tests__/a2a/producer/mount.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/producer/mount.spec.js +433 -0
- package/dist/__tests__/a2a/producer/mount.spec.js.map +1 -0
- package/dist/__tests__/a2a/producer/public-url-cache.spec.d.ts +2 -0
- package/dist/__tests__/a2a/producer/public-url-cache.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/producer/public-url-cache.spec.js +116 -0
- package/dist/__tests__/a2a/producer/public-url-cache.spec.js.map +1 -0
- package/dist/__tests__/a2a/producer/sse-emitter.spec.d.ts +2 -0
- package/dist/__tests__/a2a/producer/sse-emitter.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/producer/sse-emitter.spec.js +754 -0
- package/dist/__tests__/a2a/producer/sse-emitter.spec.js.map +1 -0
- package/dist/__tests__/a2a/producer/state-translator.spec.d.ts +2 -0
- package/dist/__tests__/a2a/producer/state-translator.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/producer/state-translator.spec.js +124 -0
- package/dist/__tests__/a2a/producer/state-translator.spec.js.map +1 -0
- package/dist/__tests__/a2a/producer/task-store.spec.d.ts +2 -0
- package/dist/__tests__/a2a/producer/task-store.spec.d.ts.map +1 -0
- package/dist/__tests__/a2a/producer/task-store.spec.js +180 -0
- package/dist/__tests__/a2a/producer/task-store.spec.js.map +1 -0
- package/dist/__tests__/agent-add-tool.spec.d.ts +2 -0
- package/dist/__tests__/agent-add-tool.spec.d.ts.map +1 -0
- package/dist/__tests__/agent-add-tool.spec.js +483 -0
- package/dist/__tests__/agent-add-tool.spec.js.map +1 -0
- package/dist/__tests__/api-runtime-race.spec.d.ts +2 -0
- package/dist/__tests__/api-runtime-race.spec.d.ts.map +1 -0
- package/dist/__tests__/api-runtime-race.spec.js +193 -0
- package/dist/__tests__/api-runtime-race.spec.js.map +1 -0
- package/dist/__tests__/claim-dispatcher.spec.d.ts +2 -0
- package/dist/__tests__/claim-dispatcher.spec.d.ts.map +1 -0
- package/dist/__tests__/claim-dispatcher.spec.js +408 -0
- package/dist/__tests__/claim-dispatcher.spec.js.map +1 -0
- package/dist/__tests__/inbound-job-dispatch.spec.d.ts +2 -0
- package/dist/__tests__/inbound-job-dispatch.spec.d.ts.map +1 -0
- package/dist/__tests__/inbound-job-dispatch.spec.js +185 -0
- package/dist/__tests__/inbound-job-dispatch.spec.js.map +1 -0
- package/dist/__tests__/job-controller-progress.spec.d.ts +2 -0
- package/dist/__tests__/job-controller-progress.spec.d.ts.map +1 -0
- package/dist/__tests__/job-controller-progress.spec.js +85 -0
- package/dist/__tests__/job-controller-progress.spec.js.map +1 -0
- package/dist/__tests__/jobs-cancel-route.spec.d.ts +2 -0
- package/dist/__tests__/jobs-cancel-route.spec.d.ts.map +1 -0
- package/dist/__tests__/jobs-cancel-route.spec.js +88 -0
- package/dist/__tests__/jobs-cancel-route.spec.js.map +1 -0
- package/dist/__tests__/llm-agent-stream.test.d.ts +14 -0
- package/dist/__tests__/llm-agent-stream.test.d.ts.map +1 -0
- package/dist/__tests__/llm-agent-stream.test.js +341 -0
- package/dist/__tests__/llm-agent-stream.test.js.map +1 -0
- package/dist/__tests__/llm-provider.test.js +22 -1
- package/dist/__tests__/llm-provider.test.js.map +1 -1
- package/dist/__tests__/media-resolver.test.js +40 -0
- package/dist/__tests__/media-resolver.test.js.map +1 -1
- package/dist/__tests__/mesh-job-submitter.spec.d.ts +2 -0
- package/dist/__tests__/mesh-job-submitter.spec.d.ts.map +1 -0
- package/dist/__tests__/mesh-job-submitter.spec.js +110 -0
- package/dist/__tests__/mesh-job-submitter.spec.js.map +1 -0
- package/dist/__tests__/proxy-stream.test.d.ts +9 -0
- package/dist/__tests__/proxy-stream.test.d.ts.map +1 -0
- package/dist/__tests__/proxy-stream.test.js +347 -0
- package/dist/__tests__/proxy-stream.test.js.map +1 -0
- package/dist/__tests__/resolver-meshjob.spec.d.ts +26 -0
- package/dist/__tests__/resolver-meshjob.spec.d.ts.map +1 -0
- package/dist/__tests__/resolver-meshjob.spec.js +201 -0
- package/dist/__tests__/resolver-meshjob.spec.js.map +1 -0
- package/dist/__tests__/schema-verdict-policy.test.d.ts +6 -0
- package/dist/__tests__/schema-verdict-policy.test.d.ts.map +1 -0
- package/dist/__tests__/schema-verdict-policy.test.js +126 -0
- package/dist/__tests__/schema-verdict-policy.test.js.map +1 -0
- package/dist/__tests__/sse-stream.test.d.ts +12 -0
- package/dist/__tests__/sse-stream.test.d.ts.map +1 -0
- package/dist/__tests__/sse-stream.test.js +170 -0
- package/dist/__tests__/sse-stream.test.js.map +1 -0
- package/dist/a2a/a2a-bearer.d.ts +27 -0
- package/dist/a2a/a2a-bearer.d.ts.map +1 -0
- package/dist/a2a/a2a-bearer.js +63 -0
- package/dist/a2a/a2a-bearer.js.map +1 -0
- package/dist/a2a/a2a-client.d.ts +114 -0
- package/dist/a2a/a2a-client.d.ts.map +1 -0
- package/dist/a2a/a2a-client.js +405 -0
- package/dist/a2a/a2a-client.js.map +1 -0
- package/dist/a2a/a2a-event.d.ts +25 -0
- package/dist/a2a/a2a-event.d.ts.map +1 -0
- package/dist/a2a/a2a-event.js +9 -0
- package/dist/a2a/a2a-event.js.map +1 -0
- package/dist/a2a/a2a-job.d.ts +58 -0
- package/dist/a2a/a2a-job.d.ts.map +1 -0
- package/dist/a2a/a2a-job.js +264 -0
- package/dist/a2a/a2a-job.js.map +1 -0
- package/dist/a2a/a2a-stream.d.ts +39 -0
- package/dist/a2a/a2a-stream.d.ts.map +1 -0
- package/dist/a2a/a2a-stream.js +290 -0
- package/dist/a2a/a2a-stream.js.map +1 -0
- package/dist/a2a/errors.d.ts +29 -0
- package/dist/a2a/errors.d.ts.map +1 -0
- package/dist/a2a/errors.js +48 -0
- package/dist/a2a/errors.js.map +1 -0
- package/dist/a2a/index.d.ts +12 -0
- package/dist/a2a/index.d.ts.map +1 -0
- package/dist/a2a/index.js +11 -0
- package/dist/a2a/index.js.map +1 -0
- package/dist/a2a/producer/auth-filter.d.ts +34 -0
- package/dist/a2a/producer/auth-filter.d.ts.map +1 -0
- package/dist/a2a/producer/auth-filter.js +39 -0
- package/dist/a2a/producer/auth-filter.js.map +1 -0
- package/dist/a2a/producer/card-builder.d.ts +59 -0
- package/dist/a2a/producer/card-builder.d.ts.map +1 -0
- package/dist/a2a/producer/card-builder.js +59 -0
- package/dist/a2a/producer/card-builder.js.map +1 -0
- package/dist/a2a/producer/dispatcher.d.ts +276 -0
- package/dist/a2a/producer/dispatcher.d.ts.map +1 -0
- package/dist/a2a/producer/dispatcher.js +896 -0
- package/dist/a2a/producer/dispatcher.js.map +1 -0
- package/dist/a2a/producer/index.d.ts +26 -0
- package/dist/a2a/producer/index.d.ts.map +1 -0
- package/dist/a2a/producer/index.js +23 -0
- package/dist/a2a/producer/index.js.map +1 -0
- package/dist/a2a/producer/mount.d.ts +75 -0
- package/dist/a2a/producer/mount.d.ts.map +1 -0
- package/dist/a2a/producer/mount.js +422 -0
- package/dist/a2a/producer/mount.js.map +1 -0
- package/dist/a2a/producer/public-url-cache.d.ts +73 -0
- package/dist/a2a/producer/public-url-cache.d.ts.map +1 -0
- package/dist/a2a/producer/public-url-cache.js +0 -0
- package/dist/a2a/producer/public-url-cache.js.map +1 -0
- package/dist/a2a/producer/registry.d.ts +138 -0
- package/dist/a2a/producer/registry.d.ts.map +1 -0
- package/dist/a2a/producer/registry.js +117 -0
- package/dist/a2a/producer/registry.js.map +1 -0
- package/dist/a2a/producer/sse-emitter.d.ts +85 -0
- package/dist/a2a/producer/sse-emitter.d.ts.map +1 -0
- package/dist/a2a/producer/sse-emitter.js +405 -0
- package/dist/a2a/producer/sse-emitter.js.map +1 -0
- package/dist/a2a/producer/state-translator.d.ts +63 -0
- package/dist/a2a/producer/state-translator.d.ts.map +1 -0
- package/dist/a2a/producer/state-translator.js +108 -0
- package/dist/a2a/producer/state-translator.js.map +1 -0
- package/dist/a2a/producer/task-store.d.ts +128 -0
- package/dist/a2a/producer/task-store.d.ts.map +1 -0
- package/dist/a2a/producer/task-store.js +128 -0
- package/dist/a2a/producer/task-store.js.map +1 -0
- package/dist/agent.d.ts +99 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +754 -19
- package/dist/agent.js.map +1 -1
- package/dist/api-runtime.d.ts +25 -0
- package/dist/api-runtime.d.ts.map +1 -1
- package/dist/api-runtime.js +75 -2
- package/dist/api-runtime.js.map +1 -1
- package/dist/claim-dispatcher.d.ts +126 -0
- package/dist/claim-dispatcher.d.ts.map +1 -0
- package/dist/claim-dispatcher.js +478 -0
- package/dist/claim-dispatcher.js.map +1 -0
- package/dist/express.d.ts.map +1 -1
- package/dist/express.js +33 -6
- package/dist/express.js.map +1 -1
- package/dist/inbound-job-dispatch.d.ts +105 -0
- package/dist/inbound-job-dispatch.d.ts.map +1 -0
- package/dist/inbound-job-dispatch.js +335 -0
- package/dist/inbound-job-dispatch.js.map +1 -0
- package/dist/index.d.ts +40 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -3
- package/dist/index.js.map +1 -1
- package/dist/job-context.d.ts +107 -0
- package/dist/job-context.d.ts.map +1 -0
- package/dist/job-context.js +95 -0
- package/dist/job-context.js.map +1 -0
- package/dist/jobs-cancel-route.d.ts +36 -0
- package/dist/jobs-cancel-route.d.ts.map +1 -0
- package/dist/jobs-cancel-route.js +60 -0
- package/dist/jobs-cancel-route.js.map +1 -0
- package/dist/jobs-helper-tools.d.ts +48 -0
- package/dist/jobs-helper-tools.d.ts.map +1 -0
- package/dist/jobs-helper-tools.js +133 -0
- package/dist/jobs-helper-tools.js.map +1 -0
- package/dist/llm-agent.d.ts +62 -53
- package/dist/llm-agent.d.ts.map +1 -1
- package/dist/llm-agent.js +211 -292
- package/dist/llm-agent.js.map +1 -1
- package/dist/llm-provider.d.ts +11 -4
- package/dist/llm-provider.d.ts.map +1 -1
- package/dist/llm-provider.js +57 -4
- package/dist/llm-provider.js.map +1 -1
- package/dist/llm.d.ts +4 -1
- package/dist/llm.d.ts.map +1 -1
- package/dist/llm.js +7 -17
- package/dist/llm.js.map +1 -1
- package/dist/media/resolver.d.ts.map +1 -1
- package/dist/media/resolver.js +3 -2
- package/dist/media/resolver.js.map +1 -1
- package/dist/mesh-job-submitter.d.ts +83 -0
- package/dist/mesh-job-submitter.d.ts.map +1 -0
- package/dist/mesh-job-submitter.js +143 -0
- package/dist/mesh-job-submitter.js.map +1 -0
- package/dist/provider-handlers/gemini-handler.js +5 -0
- package/dist/provider-handlers/gemini-handler.js.map +1 -1
- package/dist/proxy.d.ts +40 -0
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +375 -2
- package/dist/proxy.js.map +1 -1
- package/dist/resolver-meshjob.d.ts +170 -0
- package/dist/resolver-meshjob.d.ts.map +1 -0
- package/dist/resolver-meshjob.js +159 -0
- package/dist/resolver-meshjob.js.map +1 -0
- package/dist/route.d.ts +4 -0
- package/dist/route.d.ts.map +1 -1
- package/dist/route.js.map +1 -1
- package/dist/schema-normalize.d.ts +62 -0
- package/dist/schema-normalize.d.ts.map +1 -0
- package/dist/schema-normalize.js +128 -0
- package/dist/schema-normalize.js.map +1 -0
- package/dist/sse-stream.d.ts +44 -0
- package/dist/sse-stream.d.ts.map +1 -0
- package/dist/sse-stream.js +173 -0
- package/dist/sse-stream.js.map +1 -0
- package/dist/tool-worker-entry.d.ts +21 -0
- package/dist/tool-worker-entry.d.ts.map +1 -0
- package/dist/tool-worker-entry.js +162 -0
- package/dist/tool-worker-entry.js.map +1 -0
- package/dist/tool-worker-pool.d.ts +49 -0
- package/dist/tool-worker-pool.d.ts.map +1 -0
- package/dist/tool-worker-pool.js +272 -0
- package/dist/tool-worker-pool.js.map +1 -0
- package/dist/types.d.ts +351 -9
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -3
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handle to a long-running A2A task — returned by `A2AClient.submit`
|
|
3
|
+
* (issue #917).
|
|
4
|
+
*
|
|
5
|
+
* Provides direct task lifecycle methods (`status`, `wait`, `cancel`)
|
|
6
|
+
* AND a convenience `bridge(jobController)` that mirrors A2A polling
|
|
7
|
+
* state into a mesh `JobController` for the typical
|
|
8
|
+
* `addTool({ task: true, a2aConfig: { ... } })` consumer pattern.
|
|
9
|
+
*
|
|
10
|
+
* Mirrors `mesh._a2a_consumer.A2AJob` (Python) and
|
|
11
|
+
* `io.mcpmesh.a2a.A2AJob` (Java).
|
|
12
|
+
*/
|
|
13
|
+
import { JobController } from "@mcpmesh/core";
|
|
14
|
+
import { A2AClient, type A2AResponse, type A2ATaskEnvelope } from "./a2a-client.js";
|
|
15
|
+
export declare class A2AJob {
|
|
16
|
+
private readonly client;
|
|
17
|
+
readonly taskId: string;
|
|
18
|
+
readonly initialState: string;
|
|
19
|
+
private readonly initialResult;
|
|
20
|
+
constructor(client: A2AClient, taskId: string, initialState: string, initialResult: A2ATaskEnvelope);
|
|
21
|
+
/** POST `tasks/get` once and return the raw `result` envelope. */
|
|
22
|
+
status(): Promise<A2ATaskEnvelope>;
|
|
23
|
+
/**
|
|
24
|
+
* POST `tasks/cancel`. Idempotent — already-terminal tasks should
|
|
25
|
+
* return cleanly per A2A v1.0; transport-level errors are logged
|
|
26
|
+
* and swallowed so callers can still raise `A2AJobCanceledError`
|
|
27
|
+
* or similar.
|
|
28
|
+
*/
|
|
29
|
+
cancel(reason?: string): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Poll `tasks/get` until terminal; return `A2AResponse` on completed.
|
|
32
|
+
*
|
|
33
|
+
* Throws `A2AJobFailedError` on `state=failed`,
|
|
34
|
+
* `A2AJobCanceledError` on `state=canceled`, `A2ATimeoutError` if
|
|
35
|
+
* the deadline elapses.
|
|
36
|
+
*/
|
|
37
|
+
wait(timeoutMs?: number): Promise<A2AResponse>;
|
|
38
|
+
/**
|
|
39
|
+
* Mirror A2A polling into the supplied `JobController` until terminal.
|
|
40
|
+
* Returns the final artifact value: parsed JSON when the artifact
|
|
41
|
+
* text is valid JSON, otherwise the raw text. Empty artifacts return
|
|
42
|
+
* an empty string.
|
|
43
|
+
*
|
|
44
|
+
* Cancel handling: races each iteration's sleep against
|
|
45
|
+
* `awaitJobCancel(jobId)` so a mesh-side cancel arriving DURING a
|
|
46
|
+
* sleep wakes us immediately instead of waiting for the next poll
|
|
47
|
+
* boundary. On detection POSTs `tasks/cancel` upstream so the
|
|
48
|
+
* producer stops billing for the work, then throws
|
|
49
|
+
* `A2AJobCanceledError` so the framework's `task: true` wrapper
|
|
50
|
+
* records a canceled outcome.
|
|
51
|
+
*/
|
|
52
|
+
bridge(controller: JobController): Promise<unknown>;
|
|
53
|
+
private _mirrorProgress;
|
|
54
|
+
private _propagateCancelUpstream;
|
|
55
|
+
private _terminalToResponseOrThrow;
|
|
56
|
+
private _terminalToArtifactOrThrow;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=a2a-job.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"a2a-job.d.ts","sourceRoot":"","sources":["../../src/a2a/a2a-job.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,aAAa,EAAkB,MAAM,eAAe,CAAC;AAC9D,OAAO,EACL,SAAS,EACT,KAAK,WAAW,EAChB,KAAK,eAAe,EASrB,MAAM,iBAAiB,CAAC;AAOzB,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkB;gBAG9C,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,eAAe;IAQhC,kEAAkE;IAC5D,MAAM,IAAI,OAAO,CAAC,eAAe,CAAC;IAIxC;;;;;OAKG;IACG,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAe5C;;;;;;OAMG;IACG,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAgCpD;;;;;;;;;;;;;OAaG;IACG,MAAM,CAAC,UAAU,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;YAwF3C,eAAe;YAyCf,wBAAwB;IAWtC,OAAO,CAAC,0BAA0B;IAalC,OAAO,CAAC,0BAA0B;CAcnC"}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handle to a long-running A2A task — returned by `A2AClient.submit`
|
|
3
|
+
* (issue #917).
|
|
4
|
+
*
|
|
5
|
+
* Provides direct task lifecycle methods (`status`, `wait`, `cancel`)
|
|
6
|
+
* AND a convenience `bridge(jobController)` that mirrors A2A polling
|
|
7
|
+
* state into a mesh `JobController` for the typical
|
|
8
|
+
* `addTool({ task: true, a2aConfig: { ... } })` consumer pattern.
|
|
9
|
+
*
|
|
10
|
+
* Mirrors `mesh._a2a_consumer.A2AJob` (Python) and
|
|
11
|
+
* `io.mcpmesh.a2a.A2AJob` (Java).
|
|
12
|
+
*/
|
|
13
|
+
import { awaitJobCancel } from "@mcpmesh/core";
|
|
14
|
+
import { POLL_BACKOFF_FACTOR, extractArtifactText, isCanceledState, isTerminalState, maybeJsonParse, readProgress, readState, readStatusMessage, } from "./a2a-client.js";
|
|
15
|
+
import { A2AJobCanceledError, A2AJobFailedError, A2ATimeoutError, } from "./errors.js";
|
|
16
|
+
export class A2AJob {
|
|
17
|
+
client;
|
|
18
|
+
taskId;
|
|
19
|
+
initialState;
|
|
20
|
+
initialResult;
|
|
21
|
+
constructor(client, taskId, initialState, initialResult) {
|
|
22
|
+
this.client = client;
|
|
23
|
+
this.taskId = taskId;
|
|
24
|
+
this.initialState = initialState;
|
|
25
|
+
this.initialResult = initialResult;
|
|
26
|
+
}
|
|
27
|
+
/** POST `tasks/get` once and return the raw `result` envelope. */
|
|
28
|
+
async status() {
|
|
29
|
+
return this.client.tasksGet(this.taskId);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* POST `tasks/cancel`. Idempotent — already-terminal tasks should
|
|
33
|
+
* return cleanly per A2A v1.0; transport-level errors are logged
|
|
34
|
+
* and swallowed so callers can still raise `A2AJobCanceledError`
|
|
35
|
+
* or similar.
|
|
36
|
+
*/
|
|
37
|
+
async cancel(reason) {
|
|
38
|
+
try {
|
|
39
|
+
await this.client.tasksCancel(this.taskId, reason);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
// Mirror the Python+Java posture (best-effort): the remote may
|
|
43
|
+
// have already terminated the task. Log and move on so callers
|
|
44
|
+
// can still raise A2AJobCanceledError or similar.
|
|
45
|
+
console.info(`A2A tasks/cancel: remote raised for task ${this.taskId} on ` +
|
|
46
|
+
`${this.client.url} (may already be terminal): ` +
|
|
47
|
+
`${err?.message ?? String(err)}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Poll `tasks/get` until terminal; return `A2AResponse` on completed.
|
|
52
|
+
*
|
|
53
|
+
* Throws `A2AJobFailedError` on `state=failed`,
|
|
54
|
+
* `A2AJobCanceledError` on `state=canceled`, `A2ATimeoutError` if
|
|
55
|
+
* the deadline elapses.
|
|
56
|
+
*/
|
|
57
|
+
async wait(timeoutMs) {
|
|
58
|
+
const timeout = timeoutMs ?? this.client.timeoutMs;
|
|
59
|
+
if (timeout <= 0) {
|
|
60
|
+
throw new Error("A2AJob.wait: timeoutMs must be > 0");
|
|
61
|
+
}
|
|
62
|
+
if (isTerminalState(this.initialState)) {
|
|
63
|
+
return this._terminalToResponseOrThrow(this.initialResult);
|
|
64
|
+
}
|
|
65
|
+
const deadline = Date.now() + timeout;
|
|
66
|
+
let intervalMs = this.client.pollIntervalMs;
|
|
67
|
+
while (Date.now() < deadline) {
|
|
68
|
+
await sleep(Math.min(intervalMs, Math.max(1, deadline - Date.now())));
|
|
69
|
+
if (Date.now() >= deadline)
|
|
70
|
+
break;
|
|
71
|
+
const result = await this.status();
|
|
72
|
+
const state = readState(result);
|
|
73
|
+
if (isTerminalState(state)) {
|
|
74
|
+
return this._terminalToResponseOrThrow(result);
|
|
75
|
+
}
|
|
76
|
+
intervalMs = Math.min(this.client.pollIntervalMaxMs, Math.floor(intervalMs * POLL_BACKOFF_FACTOR));
|
|
77
|
+
}
|
|
78
|
+
throw new A2ATimeoutError(`A2A task '${this.taskId}' on ${this.client.url} did not reach ` +
|
|
79
|
+
`terminal state within ${timeout}ms`);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Mirror A2A polling into the supplied `JobController` until terminal.
|
|
83
|
+
* Returns the final artifact value: parsed JSON when the artifact
|
|
84
|
+
* text is valid JSON, otherwise the raw text. Empty artifacts return
|
|
85
|
+
* an empty string.
|
|
86
|
+
*
|
|
87
|
+
* Cancel handling: races each iteration's sleep against
|
|
88
|
+
* `awaitJobCancel(jobId)` so a mesh-side cancel arriving DURING a
|
|
89
|
+
* sleep wakes us immediately instead of waiting for the next poll
|
|
90
|
+
* boundary. On detection POSTs `tasks/cancel` upstream so the
|
|
91
|
+
* producer stops billing for the work, then throws
|
|
92
|
+
* `A2AJobCanceledError` so the framework's `task: true` wrapper
|
|
93
|
+
* records a canceled outcome.
|
|
94
|
+
*/
|
|
95
|
+
async bridge(controller) {
|
|
96
|
+
if (controller == null) {
|
|
97
|
+
throw new Error("A2AJob.bridge: controller must be non-null");
|
|
98
|
+
}
|
|
99
|
+
const jobId = controller.jobId;
|
|
100
|
+
const cancelPromise = awaitJobCancel(jobId);
|
|
101
|
+
const mirrorState = { lastProgress: undefined, lastMessage: undefined };
|
|
102
|
+
if (this.initialResult) {
|
|
103
|
+
await this._mirrorProgress(controller, this.initialResult, mirrorState);
|
|
104
|
+
}
|
|
105
|
+
if (isTerminalState(this.initialState)) {
|
|
106
|
+
return this._terminalToArtifactOrThrow(this.initialResult);
|
|
107
|
+
}
|
|
108
|
+
let cancelObserved = false;
|
|
109
|
+
// ONE shared AbortController fans out the cancelPromise resolution
|
|
110
|
+
// to every per-iteration `raceSleep` listener. Without this the
|
|
111
|
+
// polling loop would attach a fresh `.then` to cancelPromise on
|
|
112
|
+
// every iteration — for a 30-min job at sub-second polling that's
|
|
113
|
+
// hundreds of accumulated handlers on a never-resolving promise,
|
|
114
|
+
// i.e. linear memory creep. AbortController makes the fan-out O(1).
|
|
115
|
+
const cancelAbort = new AbortController();
|
|
116
|
+
cancelPromise
|
|
117
|
+
.then(() => {
|
|
118
|
+
cancelObserved = true;
|
|
119
|
+
cancelAbort.abort();
|
|
120
|
+
})
|
|
121
|
+
.catch((err) => {
|
|
122
|
+
// awaitJobCancel rejects only when the napi binding itself
|
|
123
|
+
// fails (binding loss mid-job). If we ignored the rejection
|
|
124
|
+
// the polling loop would never observe a cancel signal and
|
|
125
|
+
// could poll the A2A backend until the user-supplied deadline.
|
|
126
|
+
// Treat as a degraded-but-recoverable cancel signal so the
|
|
127
|
+
// bridge fails fast and propagates `tasks/cancel` upstream.
|
|
128
|
+
console.warn(`[a2a-job] bridge: awaitJobCancel observer failed for task ` +
|
|
129
|
+
`${this.taskId} (treating as degraded cancel): ` +
|
|
130
|
+
`${err?.message ?? String(err)}`);
|
|
131
|
+
cancelObserved = true;
|
|
132
|
+
cancelAbort.abort();
|
|
133
|
+
});
|
|
134
|
+
let intervalMs = this.client.pollIntervalMs;
|
|
135
|
+
while (true) {
|
|
136
|
+
if (cancelObserved) {
|
|
137
|
+
await this._propagateCancelUpstream("mesh-side cancel");
|
|
138
|
+
throw new A2AJobCanceledError(`A2A task ${this.taskId} canceled by mesh-side request`);
|
|
139
|
+
}
|
|
140
|
+
let result;
|
|
141
|
+
try {
|
|
142
|
+
result = await this.status();
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
// tasks/get itself failed (network error, HTTP 5xx, malformed
|
|
146
|
+
// envelope, ...). The upstream producer is almost certainly
|
|
147
|
+
// still running — best-effort POST tasks/cancel so it stops
|
|
148
|
+
// billing for work whose result we'll never observe.
|
|
149
|
+
await this._propagateCancelUpstream("consumer poll failed");
|
|
150
|
+
throw new A2AJobFailedError(`A2A status poll failed for task ${this.taskId}: ` +
|
|
151
|
+
`${err?.message ?? String(err)}`, err);
|
|
152
|
+
}
|
|
153
|
+
const state = readState(result);
|
|
154
|
+
await this._mirrorProgress(controller, result, mirrorState);
|
|
155
|
+
if (isTerminalState(state)) {
|
|
156
|
+
return this._terminalToArtifactOrThrow(result);
|
|
157
|
+
}
|
|
158
|
+
// Race the sleep against the cancel signal — if cancel fires
|
|
159
|
+
// mid-sleep we wake immediately and propagate on the next loop
|
|
160
|
+
// iteration without wasting one more `tasks/get` round trip.
|
|
161
|
+
await raceSleep(intervalMs, cancelAbort.signal);
|
|
162
|
+
intervalMs = Math.min(this.client.pollIntervalMaxMs, Math.floor(intervalMs * POLL_BACKOFF_FACTOR));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// --- helpers -------------------------------------------------------------
|
|
166
|
+
async _mirrorProgress(controller, result, state) {
|
|
167
|
+
const progress = readProgress(result);
|
|
168
|
+
const message = readStatusMessage(result);
|
|
169
|
+
if (progress === undefined && message === undefined) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (progress === state.lastProgress && message === state.lastMessage) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
// Coerce missing progress to last-known or 0.0 — the controller
|
|
176
|
+
// requires a number, but the consumer surface allows message-only
|
|
177
|
+
// events. Clamp to [0, 1] — updateProgress expects a normalized
|
|
178
|
+
// fraction; raw A2A producer progress values are advisory.
|
|
179
|
+
const rawP = progress !== undefined
|
|
180
|
+
? progress
|
|
181
|
+
: state.lastProgress !== undefined
|
|
182
|
+
? state.lastProgress
|
|
183
|
+
: 0.0;
|
|
184
|
+
const clamped = Math.min(1.0, Math.max(0.0, rawP));
|
|
185
|
+
try {
|
|
186
|
+
await controller.updateProgress(clamped, message ?? null);
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
// Do NOT advance lastProgress / lastMessage on delivery failure
|
|
190
|
+
// — leaving them stale ensures the next poll's equality check
|
|
191
|
+
// sees a delta and retries the update.
|
|
192
|
+
console.warn(`[a2a-job] bridge: controller.updateProgress failed ` +
|
|
193
|
+
`(task=${this.taskId}, progress=${progress}, msg=${message}) — ` +
|
|
194
|
+
`will retry on next poll: ${err?.message ?? String(err)}`);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (progress !== undefined)
|
|
198
|
+
state.lastProgress = progress;
|
|
199
|
+
state.lastMessage = message;
|
|
200
|
+
}
|
|
201
|
+
async _propagateCancelUpstream(reason) {
|
|
202
|
+
try {
|
|
203
|
+
await this.client.tasksCancel(this.taskId, reason);
|
|
204
|
+
}
|
|
205
|
+
catch (err) {
|
|
206
|
+
console.debug(`[a2a-job] bridge: upstream cancel after ${reason} also failed ` +
|
|
207
|
+
`(task=${this.taskId}): ${err?.message ?? String(err)}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
_terminalToResponseOrThrow(result) {
|
|
211
|
+
const state = readState(result);
|
|
212
|
+
if (state.toLowerCase() === "completed") {
|
|
213
|
+
return this.client.buildResponse(this.taskId, result);
|
|
214
|
+
}
|
|
215
|
+
const msg = readStatusMessage(result) ?? `A2A task ${this.taskId} state=${state}`;
|
|
216
|
+
if (isCanceledState(state)) {
|
|
217
|
+
throw new A2AJobCanceledError(msg);
|
|
218
|
+
}
|
|
219
|
+
throw new A2AJobFailedError(msg);
|
|
220
|
+
}
|
|
221
|
+
_terminalToArtifactOrThrow(result) {
|
|
222
|
+
const state = readState(result);
|
|
223
|
+
if (state.toLowerCase() === "completed") {
|
|
224
|
+
const text = extractArtifactText(result);
|
|
225
|
+
if (!text)
|
|
226
|
+
return text;
|
|
227
|
+
return maybeJsonParse(text);
|
|
228
|
+
}
|
|
229
|
+
const msg = readStatusMessage(result) ?? `A2A task ${this.taskId} state=${state}`;
|
|
230
|
+
if (isCanceledState(state)) {
|
|
231
|
+
throw new A2AJobCanceledError(msg);
|
|
232
|
+
}
|
|
233
|
+
throw new A2AJobFailedError(msg);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function sleep(ms) {
|
|
237
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Sleep for `ms` OR until `signal` aborts — whichever is first. The
|
|
241
|
+
* abort wakes us early; the timer is cleared in either path so we
|
|
242
|
+
* don't leak handles. Uses an `AbortSignal` instead of a raw promise
|
|
243
|
+
* so the polling loop can attach ONE upstream listener (the
|
|
244
|
+
* AbortController) and fan out to per-iteration `raceSleep` calls
|
|
245
|
+
* without accumulating handlers on a long-running cancelPromise.
|
|
246
|
+
*/
|
|
247
|
+
function raceSleep(ms, signal) {
|
|
248
|
+
return new Promise((resolve) => {
|
|
249
|
+
if (signal.aborted) {
|
|
250
|
+
resolve();
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const onAbort = () => {
|
|
254
|
+
clearTimeout(timer);
|
|
255
|
+
resolve();
|
|
256
|
+
};
|
|
257
|
+
const timer = setTimeout(() => {
|
|
258
|
+
signal.removeEventListener("abort", onAbort);
|
|
259
|
+
resolve();
|
|
260
|
+
}, ms);
|
|
261
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
//# sourceMappingURL=a2a-job.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"a2a-job.js","sourceRoot":"","sources":["../../src/a2a/a2a-job.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Async iterator over parsed A2A SSE events — returned by
|
|
3
|
+
* `A2AClient.subscribe` (issue #917).
|
|
4
|
+
*
|
|
5
|
+
* Implements `AsyncIterable<A2AEvent>` so callers can write
|
|
6
|
+
* `for await (const event of stream) { ... }` idiomatically.
|
|
7
|
+
* Implements TC39 `Symbol.asyncDispose` (Node 20+) for `await using`,
|
|
8
|
+
* AND exposes `aclose()` as a manual fallback for older runtimes.
|
|
9
|
+
*
|
|
10
|
+
* Per A2A v1.0, client disconnect is a transient signal — the producer
|
|
11
|
+
* continues running unless explicitly canceled. `bridge` therefore does
|
|
12
|
+
* NOT POST `tasks/cancel` upstream when the mesh-side job is cancelled
|
|
13
|
+
* (it just closes the SSE connection). Users who need cancel
|
|
14
|
+
* propagation should use `submit` + `A2AJob.bridge` instead, which
|
|
15
|
+
* polls for cancel between iterations and propagates upstream.
|
|
16
|
+
*
|
|
17
|
+
* Mirrors `mesh._a2a_consumer.A2AStream` (Python) and
|
|
18
|
+
* `io.mcpmesh.a2a.A2AStream` (Java).
|
|
19
|
+
*/
|
|
20
|
+
import { JobController } from "@mcpmesh/core";
|
|
21
|
+
import type { A2AEvent } from "./a2a-event.js";
|
|
22
|
+
export declare class A2AStream implements AsyncIterable<A2AEvent> {
|
|
23
|
+
private readonly response;
|
|
24
|
+
readonly taskId: string;
|
|
25
|
+
private closed;
|
|
26
|
+
private reader;
|
|
27
|
+
constructor(response: Response, taskId: string);
|
|
28
|
+
[Symbol.asyncIterator](): AsyncIterator<A2AEvent>;
|
|
29
|
+
/** Mirror events into a JobController; return the final artifact value. */
|
|
30
|
+
bridge(controller: JobController): Promise<unknown>;
|
|
31
|
+
/**
|
|
32
|
+
* Explicit close — releases the reader lock + cancels the
|
|
33
|
+
* underlying ReadableStream so the undici Agent reclaims the
|
|
34
|
+
* connection promptly. Idempotent.
|
|
35
|
+
*/
|
|
36
|
+
aclose(): Promise<void>;
|
|
37
|
+
private _iterate;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=a2a-stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"a2a-stream.d.ts","sourceRoot":"","sources":["../../src/a2a/a2a-stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAW/C,qBAAa,SAAU,YAAW,aAAa,CAAC,QAAQ,CAAC;IACvD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAW;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAwD;gBAE1D,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM;IAK9C,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,QAAQ,CAAC;IAcjD,2EAA2E;IACrE,MAAM,CAAC,UAAU,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IAsFzD;;;;OAIG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;YA4Bd,QAAQ;CAkFxB"}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { A2AJobCanceledError, A2AJobFailedError, } from "./errors.js";
|
|
2
|
+
import { maybeJsonParse } from "./a2a-client.js";
|
|
3
|
+
const ASYNC_DISPOSE = Symbol.asyncDispose ??
|
|
4
|
+
Symbol.for("Symbol.asyncDispose");
|
|
5
|
+
export class A2AStream {
|
|
6
|
+
response;
|
|
7
|
+
taskId;
|
|
8
|
+
closed = false;
|
|
9
|
+
reader = null;
|
|
10
|
+
constructor(response, taskId) {
|
|
11
|
+
this.response = response;
|
|
12
|
+
this.taskId = taskId;
|
|
13
|
+
}
|
|
14
|
+
[Symbol.asyncIterator]() {
|
|
15
|
+
return this._iterate();
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* TC39 explicit-resource-management hook — Node 20+ supports
|
|
19
|
+
* `await using stream = await client.subscribe(...)`. The fallback
|
|
20
|
+
* below uses Symbol.for so older runtimes still surface a usable
|
|
21
|
+
* symbol on the prototype (idempotent close).
|
|
22
|
+
*/
|
|
23
|
+
async [ASYNC_DISPOSE]() {
|
|
24
|
+
await this.aclose();
|
|
25
|
+
}
|
|
26
|
+
/** Mirror events into a JobController; return the final artifact value. */
|
|
27
|
+
async bridge(controller) {
|
|
28
|
+
if (controller == null) {
|
|
29
|
+
throw new Error("A2AStream.bridge: controller must be non-null");
|
|
30
|
+
}
|
|
31
|
+
let lastProgress;
|
|
32
|
+
let lastMessage;
|
|
33
|
+
let artifactValue = "";
|
|
34
|
+
let sawArtifact = false;
|
|
35
|
+
let terminalState;
|
|
36
|
+
let terminalMessage;
|
|
37
|
+
try {
|
|
38
|
+
for await (const event of this) {
|
|
39
|
+
if (event.kind === "artifact") {
|
|
40
|
+
const text = event.artifactText ?? "";
|
|
41
|
+
artifactValue = !text ? text : maybeJsonParse(text);
|
|
42
|
+
sawArtifact = true;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (event.progress !== undefined || event.message !== undefined) {
|
|
46
|
+
const changed = event.progress !== lastProgress || event.message !== lastMessage;
|
|
47
|
+
if (changed) {
|
|
48
|
+
const rawP = event.progress !== undefined
|
|
49
|
+
? event.progress
|
|
50
|
+
: lastProgress !== undefined
|
|
51
|
+
? lastProgress
|
|
52
|
+
: 0.0;
|
|
53
|
+
const clamped = Math.min(1.0, Math.max(0.0, rawP));
|
|
54
|
+
try {
|
|
55
|
+
await controller.updateProgress(clamped, event.message ?? null);
|
|
56
|
+
if (event.progress !== undefined)
|
|
57
|
+
lastProgress = event.progress;
|
|
58
|
+
lastMessage = event.message;
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
// Do NOT advance lastProgress / lastMessage on delivery
|
|
62
|
+
// failure — leaving them stale ensures the next event
|
|
63
|
+
// still passes the equality check and retries.
|
|
64
|
+
console.warn(`[a2a-stream] bridge: controller.updateProgress failed ` +
|
|
65
|
+
`(task=${this.taskId}, progress=${event.progress}, ` +
|
|
66
|
+
`msg=${event.message}) — will retry on next event: ` +
|
|
67
|
+
`${err?.message ?? String(err)}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (event.final) {
|
|
72
|
+
terminalState = event.state;
|
|
73
|
+
terminalMessage = event.message;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
// Ensure the SSE stream is closed on any exit path (normal
|
|
80
|
+
// completion, throw, etc.). aclose() is idempotent.
|
|
81
|
+
await this.aclose();
|
|
82
|
+
}
|
|
83
|
+
if (terminalState) {
|
|
84
|
+
const lower = terminalState.toLowerCase();
|
|
85
|
+
if (lower === "canceled" || lower === "cancelled") {
|
|
86
|
+
throw new A2AJobCanceledError(terminalMessage ?? `A2A task ${this.taskId} canceled`);
|
|
87
|
+
}
|
|
88
|
+
if (lower === "failed") {
|
|
89
|
+
throw new A2AJobFailedError(terminalMessage ?? `A2A task ${this.taskId} failed`);
|
|
90
|
+
}
|
|
91
|
+
// Parity with A2AJob._terminalToArtifactOrThrow — completed
|
|
92
|
+
// with no artifact event seen returns "" rather than throwing,
|
|
93
|
+
// matching the polling-bridge semantics.
|
|
94
|
+
if (lower === "completed" && !sawArtifact) {
|
|
95
|
+
return "";
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (!sawArtifact) {
|
|
99
|
+
throw new A2AJobFailedError(`A2A subscribe stream ${this.taskId} ended without artifact`);
|
|
100
|
+
}
|
|
101
|
+
return artifactValue;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Explicit close — releases the reader lock + cancels the
|
|
105
|
+
* underlying ReadableStream so the undici Agent reclaims the
|
|
106
|
+
* connection promptly. Idempotent.
|
|
107
|
+
*/
|
|
108
|
+
async aclose() {
|
|
109
|
+
if (this.closed)
|
|
110
|
+
return;
|
|
111
|
+
this.closed = true;
|
|
112
|
+
try {
|
|
113
|
+
if (this.reader) {
|
|
114
|
+
try {
|
|
115
|
+
await this.reader.cancel();
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// best-effort
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
this.reader.releaseLock();
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
// best-effort
|
|
125
|
+
}
|
|
126
|
+
this.reader = null;
|
|
127
|
+
}
|
|
128
|
+
else if (this.response.body) {
|
|
129
|
+
try {
|
|
130
|
+
await this.response.body.cancel();
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// best-effort
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// swallow — close is best-effort
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async *_iterate() {
|
|
142
|
+
if (this.closed)
|
|
143
|
+
return;
|
|
144
|
+
if (!this.response.body) {
|
|
145
|
+
throw new A2AJobFailedError(`A2AStream(${this.taskId}): response body is null`);
|
|
146
|
+
}
|
|
147
|
+
if (!this.reader) {
|
|
148
|
+
this.reader = this.response.body.getReader();
|
|
149
|
+
}
|
|
150
|
+
const reader = this.reader;
|
|
151
|
+
const decoder = new TextDecoder("utf-8");
|
|
152
|
+
let pending = "";
|
|
153
|
+
const dataBuf = [];
|
|
154
|
+
const flushEvent = () => {
|
|
155
|
+
if (dataBuf.length === 0)
|
|
156
|
+
return null;
|
|
157
|
+
const payload = dataBuf.join("\n");
|
|
158
|
+
dataBuf.length = 0;
|
|
159
|
+
let envelope;
|
|
160
|
+
try {
|
|
161
|
+
envelope = JSON.parse(payload);
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
// Skip non-JSON SSE frames silently — matches Python behavior.
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
return parseSseEnvelope(envelope);
|
|
168
|
+
};
|
|
169
|
+
try {
|
|
170
|
+
while (true) {
|
|
171
|
+
const { value, done } = await reader.read();
|
|
172
|
+
if (done)
|
|
173
|
+
break;
|
|
174
|
+
pending += decoder.decode(value, { stream: true });
|
|
175
|
+
let nlIdx;
|
|
176
|
+
while ((nlIdx = pending.search(/\r\n|\n/)) !== -1) {
|
|
177
|
+
// Accept both LF and CRLF line endings.
|
|
178
|
+
const isCrLf = pending.charCodeAt(nlIdx) === 13 &&
|
|
179
|
+
pending.charCodeAt(nlIdx + 1) === 10;
|
|
180
|
+
const line = pending.slice(0, nlIdx);
|
|
181
|
+
pending = pending.slice(nlIdx + (isCrLf ? 2 : 1));
|
|
182
|
+
if (line === "") {
|
|
183
|
+
const event = flushEvent();
|
|
184
|
+
if (event !== null) {
|
|
185
|
+
yield event;
|
|
186
|
+
if (event.final) {
|
|
187
|
+
await this.aclose();
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
if (line.startsWith(":")) {
|
|
194
|
+
// SSE comment / keepalive — ignore.
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (line.startsWith("data:")) {
|
|
198
|
+
// Strip "data:" prefix and one optional space.
|
|
199
|
+
let payload = line.slice(5);
|
|
200
|
+
if (payload.startsWith(" "))
|
|
201
|
+
payload = payload.slice(1);
|
|
202
|
+
dataBuf.push(payload);
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
// event:/id:/retry:/unknown — ignore for v1.0.
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// Stream ended without a trailing blank — flush any pending frame.
|
|
209
|
+
pending += decoder.decode();
|
|
210
|
+
if (pending.length > 0 && pending.startsWith("data:")) {
|
|
211
|
+
let payload = pending.slice(5);
|
|
212
|
+
if (payload.startsWith(" "))
|
|
213
|
+
payload = payload.slice(1);
|
|
214
|
+
dataBuf.push(payload);
|
|
215
|
+
}
|
|
216
|
+
const tail = flushEvent();
|
|
217
|
+
if (tail !== null) {
|
|
218
|
+
yield tail;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
finally {
|
|
222
|
+
await this.aclose();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
function parseSseEnvelope(envelope) {
|
|
227
|
+
if (!envelope || typeof envelope !== "object")
|
|
228
|
+
return null;
|
|
229
|
+
const result = envelope.result;
|
|
230
|
+
if (!result || typeof result !== "object")
|
|
231
|
+
return null;
|
|
232
|
+
const r = result;
|
|
233
|
+
// ARTIFACT events have the "artifact" key.
|
|
234
|
+
const artifact = r.artifact;
|
|
235
|
+
if (artifact && typeof artifact === "object") {
|
|
236
|
+
const text = extractFirstTextPart(artifact) ?? "";
|
|
237
|
+
return {
|
|
238
|
+
kind: "artifact",
|
|
239
|
+
artifactText: text,
|
|
240
|
+
final: false,
|
|
241
|
+
raw: envelope,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
// STATUS events have the "status" key.
|
|
245
|
+
const status = r.status;
|
|
246
|
+
if (status && typeof status === "object") {
|
|
247
|
+
const statusObj = status;
|
|
248
|
+
const stateRaw = statusObj.state;
|
|
249
|
+
const state = typeof stateRaw === "string" ? stateRaw : undefined;
|
|
250
|
+
const msgObj = statusObj.message;
|
|
251
|
+
let message;
|
|
252
|
+
if (msgObj && typeof msgObj === "object") {
|
|
253
|
+
message = extractFirstTextPart(msgObj);
|
|
254
|
+
}
|
|
255
|
+
let progress;
|
|
256
|
+
const metadata = r.metadata;
|
|
257
|
+
if (metadata && typeof metadata === "object") {
|
|
258
|
+
const p = metadata.progress;
|
|
259
|
+
if (typeof p === "number" && Number.isFinite(p))
|
|
260
|
+
progress = p;
|
|
261
|
+
else if (typeof p === "string") {
|
|
262
|
+
const n = Number.parseFloat(p);
|
|
263
|
+
if (Number.isFinite(n))
|
|
264
|
+
progress = n;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
const finalRaw = r.final;
|
|
268
|
+
const final = finalRaw === true;
|
|
269
|
+
return {
|
|
270
|
+
kind: "status",
|
|
271
|
+
state,
|
|
272
|
+
progress,
|
|
273
|
+
message,
|
|
274
|
+
final,
|
|
275
|
+
raw: envelope,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
function extractFirstTextPart(container) {
|
|
281
|
+
const parts = container.parts;
|
|
282
|
+
if (!Array.isArray(parts) || parts.length === 0)
|
|
283
|
+
return undefined;
|
|
284
|
+
const first = parts[0];
|
|
285
|
+
if (!first || typeof first !== "object")
|
|
286
|
+
return undefined;
|
|
287
|
+
const text = first.text;
|
|
288
|
+
return typeof text === "string" ? text : undefined;
|
|
289
|
+
}
|
|
290
|
+
//# sourceMappingURL=a2a-stream.js.map
|