@loopingai/core 0.1.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 +674 -0
- package/README.md +279 -0
- package/dist/a2a/agent-stub.d.ts +61 -0
- package/dist/a2a/agent-stub.d.ts.map +1 -0
- package/dist/a2a/agent-stub.js +2 -0
- package/dist/a2a/agent-stub.js.map +1 -0
- package/dist/a2a/card.d.ts +119 -0
- package/dist/a2a/card.d.ts.map +1 -0
- package/dist/a2a/card.js +115 -0
- package/dist/a2a/card.js.map +1 -0
- package/dist/a2a/context.d.ts +14 -0
- package/dist/a2a/context.d.ts.map +1 -0
- package/dist/a2a/context.js +55 -0
- package/dist/a2a/context.js.map +1 -0
- package/dist/a2a/executor.d.ts +85 -0
- package/dist/a2a/executor.d.ts.map +1 -0
- package/dist/a2a/executor.js +107 -0
- package/dist/a2a/executor.js.map +1 -0
- package/dist/a2a/index.d.ts +18 -0
- package/dist/a2a/index.d.ts.map +1 -0
- package/dist/a2a/index.js +17 -0
- package/dist/a2a/index.js.map +1 -0
- package/dist/a2a/notify.d.ts +75 -0
- package/dist/a2a/notify.d.ts.map +1 -0
- package/dist/a2a/notify.js +161 -0
- package/dist/a2a/notify.js.map +1 -0
- package/dist/a2a/parts.d.ts +46 -0
- package/dist/a2a/parts.d.ts.map +1 -0
- package/dist/a2a/parts.js +95 -0
- package/dist/a2a/parts.js.map +1 -0
- package/dist/a2a/task-store.d.ts +45 -0
- package/dist/a2a/task-store.d.ts.map +1 -0
- package/dist/a2a/task-store.js +100 -0
- package/dist/a2a/task-store.js.map +1 -0
- package/dist/a2a/task.d.ts +86 -0
- package/dist/a2a/task.d.ts.map +1 -0
- package/dist/a2a/task.js +16 -0
- package/dist/a2a/task.js.map +1 -0
- package/dist/a2a/verify.d.ts +64 -0
- package/dist/a2a/verify.d.ts.map +1 -0
- package/dist/a2a/verify.js +122 -0
- package/dist/a2a/verify.js.map +1 -0
- package/dist/agent/budget.d.ts +47 -0
- package/dist/agent/budget.d.ts.map +1 -0
- package/dist/agent/budget.js +43 -0
- package/dist/agent/budget.js.map +1 -0
- package/dist/agent/control.d.ts +110 -0
- package/dist/agent/control.d.ts.map +1 -0
- package/dist/agent/control.js +115 -0
- package/dist/agent/control.js.map +1 -0
- package/dist/agent/final-reply.d.ts +49 -0
- package/dist/agent/final-reply.d.ts.map +1 -0
- package/dist/agent/final-reply.js +52 -0
- package/dist/agent/final-reply.js.map +1 -0
- package/dist/agent/history.d.ts +83 -0
- package/dist/agent/history.d.ts.map +1 -0
- package/dist/agent/history.js +117 -0
- package/dist/agent/history.js.map +1 -0
- package/dist/agent/index.d.ts +17 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +17 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/inference.d.ts +52 -0
- package/dist/agent/inference.d.ts.map +1 -0
- package/dist/agent/inference.js +79 -0
- package/dist/agent/inference.js.map +1 -0
- package/dist/agent/model.d.ts +75 -0
- package/dist/agent/model.d.ts.map +1 -0
- package/dist/agent/model.js +62 -0
- package/dist/agent/model.js.map +1 -0
- package/dist/agent/session.d.ts +92 -0
- package/dist/agent/session.d.ts.map +1 -0
- package/dist/agent/session.js +80 -0
- package/dist/agent/session.js.map +1 -0
- package/dist/config.d.ts +159 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +92 -0
- package/dist/config.js.map +1 -0
- package/dist/contract/index.d.ts +10 -0
- package/dist/contract/index.d.ts.map +1 -0
- package/dist/contract/index.js +9 -0
- package/dist/contract/index.js.map +1 -0
- package/dist/contract/plugin.d.ts +163 -0
- package/dist/contract/plugin.d.ts.map +1 -0
- package/dist/contract/plugin.js +39 -0
- package/dist/contract/plugin.js.map +1 -0
- package/dist/contract/recipe.d.ts +170 -0
- package/dist/contract/recipe.d.ts.map +1 -0
- package/dist/contract/recipe.js +2 -0
- package/dist/contract/recipe.js.map +1 -0
- package/dist/contract/validation.d.ts +86 -0
- package/dist/contract/validation.d.ts.map +1 -0
- package/dist/contract/validation.js +76 -0
- package/dist/contract/validation.js.map +1 -0
- package/dist/db/db.d.ts +131 -0
- package/dist/db/db.d.ts.map +1 -0
- package/dist/db/db.js +91 -0
- package/dist/db/db.js.map +1 -0
- package/dist/db/index.d.ts +9 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +9 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/migrations/index.d.ts +21 -0
- package/dist/db/migrations/index.d.ts.map +1 -0
- package/dist/db/migrations/index.js +54 -0
- package/dist/db/migrations/index.js.map +1 -0
- package/dist/db/models/subtasks.d.ts +102 -0
- package/dist/db/models/subtasks.d.ts.map +1 -0
- package/dist/db/models/subtasks.js +285 -0
- package/dist/db/models/subtasks.js.map +1 -0
- package/dist/db/models/tasks.d.ts +92 -0
- package/dist/db/models/tasks.d.ts.map +1 -0
- package/dist/db/models/tasks.js +212 -0
- package/dist/db/models/tasks.js.map +1 -0
- package/dist/db/schema.d.ts +488 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +91 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/env.d.ts +46 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +40 -0
- package/dist/env.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/platform.d.ts +47 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +47 -0
- package/dist/platform.js.map +1 -0
- package/dist/runtime/index.d.ts +86 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +123 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/tool-families.d.ts +33 -0
- package/dist/runtime/tool-families.d.ts.map +1 -0
- package/dist/runtime/tool-families.js +62 -0
- package/dist/runtime/tool-families.js.map +1 -0
- package/dist/subagent/fingerprint.d.ts +38 -0
- package/dist/subagent/fingerprint.d.ts.map +1 -0
- package/dist/subagent/fingerprint.js +97 -0
- package/dist/subagent/fingerprint.js.map +1 -0
- package/dist/subagent/index.d.ts +147 -0
- package/dist/subagent/index.d.ts.map +1 -0
- package/dist/subagent/index.js +299 -0
- package/dist/subagent/index.js.map +1 -0
- package/dist/subagent/prompt.d.ts +34 -0
- package/dist/subagent/prompt.d.ts.map +1 -0
- package/dist/subagent/prompt.js +46 -0
- package/dist/subagent/prompt.js.map +1 -0
- package/dist/subagent/run.d.ts +151 -0
- package/dist/subagent/run.d.ts.map +1 -0
- package/dist/subagent/run.js +491 -0
- package/dist/subagent/run.js.map +1 -0
- package/dist/subagent/workspace.d.ts +65 -0
- package/dist/subagent/workspace.d.ts.map +1 -0
- package/dist/subagent/workspace.js +53 -0
- package/dist/subagent/workspace.js.map +1 -0
- package/dist/subtasks/catalog.d.ts +40 -0
- package/dist/subtasks/catalog.d.ts.map +1 -0
- package/dist/subtasks/catalog.js +36 -0
- package/dist/subtasks/catalog.js.map +1 -0
- package/dist/subtasks/decomposition.d.ts +90 -0
- package/dist/subtasks/decomposition.d.ts.map +1 -0
- package/dist/subtasks/decomposition.js +205 -0
- package/dist/subtasks/decomposition.js.map +1 -0
- package/dist/subtasks/delegate.d.ts +77 -0
- package/dist/subtasks/delegate.d.ts.map +1 -0
- package/dist/subtasks/delegate.js +102 -0
- package/dist/subtasks/delegate.js.map +1 -0
- package/dist/subtasks/index.d.ts +11 -0
- package/dist/subtasks/index.d.ts.map +1 -0
- package/dist/subtasks/index.js +11 -0
- package/dist/subtasks/index.js.map +1 -0
- package/dist/subtasks/scheduler.d.ts +49 -0
- package/dist/subtasks/scheduler.d.ts.map +1 -0
- package/dist/subtasks/scheduler.js +48 -0
- package/dist/subtasks/scheduler.js.map +1 -0
- package/dist/subtasks/subtask-types.d.ts +100 -0
- package/dist/subtasks/subtask-types.d.ts.map +1 -0
- package/dist/subtasks/subtask-types.js +111 -0
- package/dist/subtasks/subtask-types.js.map +1 -0
- package/dist/subtasks/types.d.ts +321 -0
- package/dist/subtasks/types.d.ts.map +1 -0
- package/dist/subtasks/types.js +16 -0
- package/dist/subtasks/types.js.map +1 -0
- package/dist/testing/auth.d.ts +12 -0
- package/dist/testing/auth.d.ts.map +1 -0
- package/dist/testing/auth.js +25 -0
- package/dist/testing/auth.js.map +1 -0
- package/dist/testing/do.d.ts +30 -0
- package/dist/testing/do.d.ts.map +1 -0
- package/dist/testing/do.js +26 -0
- package/dist/testing/do.js.map +1 -0
- package/dist/testing/fake-session.d.ts +27 -0
- package/dist/testing/fake-session.d.ts.map +1 -0
- package/dist/testing/fake-session.js +38 -0
- package/dist/testing/fake-session.js.map +1 -0
- package/dist/testing/fixtures.d.ts +27 -0
- package/dist/testing/fixtures.d.ts.map +1 -0
- package/dist/testing/fixtures.js +67 -0
- package/dist/testing/fixtures.js.map +1 -0
- package/dist/testing/index.d.ts +31 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +31 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/mock-model.d.ts +43 -0
- package/dist/testing/mock-model.d.ts.map +1 -0
- package/dist/testing/mock-model.js +62 -0
- package/dist/testing/mock-model.js.map +1 -0
- package/dist/testing/node.d.ts +24 -0
- package/dist/testing/node.d.ts.map +1 -0
- package/dist/testing/node.js +24 -0
- package/dist/testing/node.js.map +1 -0
- package/dist/testing/vcr-global-setup.d.ts +3 -0
- package/dist/testing/vcr-global-setup.d.ts.map +1 -0
- package/dist/testing/vcr-global-setup.js +9 -0
- package/dist/testing/vcr-global-setup.js.map +1 -0
- package/dist/testing/vcr-shared.d.ts +21 -0
- package/dist/testing/vcr-shared.d.ts.map +1 -0
- package/dist/testing/vcr-shared.js +21 -0
- package/dist/testing/vcr-shared.js.map +1 -0
- package/dist/testing/vcr-spec.d.ts +17 -0
- package/dist/testing/vcr-spec.d.ts.map +1 -0
- package/dist/testing/vcr-spec.js +72 -0
- package/dist/testing/vcr-spec.js.map +1 -0
- package/dist/testing/vcr.d.ts +81 -0
- package/dist/testing/vcr.d.ts.map +1 -0
- package/dist/testing/vcr.js +211 -0
- package/dist/testing/vcr.js.map +1 -0
- package/dist/worker/index.d.ts +62 -0
- package/dist/worker/index.d.ts.map +1 -0
- package/dist/worker/index.js +194 -0
- package/dist/worker/index.js.map +1 -0
- package/eslint-rules/index.js +31 -0
- package/eslint-rules/no-deprecated-object-properties.js +81 -0
- package/package.json +147 -0
- package/scripts/generate-keys.mjs +53 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parts.js","sourceRoot":"","sources":["../../src/a2a/parts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAA2B,MAAM,aAAa,CAAC;AAG5D;;;;;;;;;;;;GAYG;AAEH,6DAA6D;AAC7D,MAAM,eAAe,GAAG,YAAY,CAAC;AAErC;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,OAAO;QACL,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE;QACvC,QAAQ,EAAE,SAAS;QACnB,QAAQ,EAAE,EAAE;QACZ,SAAS,EAAE,eAAe;KAC3B,CAAC;AACJ,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,SAAS,CAAC,KAAyB;IACjD,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,MAAM,IAAI,IAAI,KAAK,IAAI,EAAE,EAAE,CAAC;QAC/B,IAAI,IAAI,CAAC,OAAO,EAAE,KAAK,KAAK,MAAM;YAAE,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;IAChE,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,MAAM,CAAC,OAAgB;IACrC,OAAO,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAClC,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAG,GAAG,IAAI,CAAC;AAEjD,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;AAElC,gFAAgF;AAChF,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACzC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,OAAgB;IAC1C,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7B,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,gBAAgB,CAAC,4BAA4B,CAAC,CAAC;IAC3D,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,GAAG,sBAAsB,EAAE,CAAC;QAC7D,MAAM,IAAI,gBAAgB,CAAC,qCAAqC,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAKhC;IACC,OAAO;QACL,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,IAAI,EAAE,IAAI,CAAC,UAAU;QACrB,KAAK,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,QAAQ,EAAE,SAAS;QACnB,UAAU,EAAE,EAAE;QACd,gBAAgB,EAAE,EAAE;KACrB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { TaskStore } from "@a2a-js/sdk/server";
|
|
2
|
+
import { type ListTasksRequest, type ListTasksResponse, type Task } from "@a2a-js/sdk";
|
|
3
|
+
import type { GatewayIdentity } from "./verify.js";
|
|
4
|
+
import type { AgentResolver } from "./agent-stub.js";
|
|
5
|
+
/**
|
|
6
|
+
* A durable {@link TaskStore} for the a2a-js `DefaultRequestHandler`, backed by
|
|
7
|
+
* the caller's agent Durable Object (via native RPC) instead of the SDK's
|
|
8
|
+
* per-request `InMemoryTaskStore`.
|
|
9
|
+
*
|
|
10
|
+
* Task state must survive the accept → async callback gap (and answer
|
|
11
|
+
* `GetTask`/`CancelTask`/`ListTasks` across requests), so it lives in the same
|
|
12
|
+
* per-caller DO that owns the Session — keyed by the verified `identity.key`,
|
|
13
|
+
* exactly like {@link file://./executor.ts A2AExecutor}. A workflow updates the
|
|
14
|
+
* same rows through its own DO RPC calls, so this store and the workflow share
|
|
15
|
+
* one source of truth.
|
|
16
|
+
*
|
|
17
|
+
* v1.0 passes a `ServerCallContext` to every method so a shared store can scope
|
|
18
|
+
* rows by tenant/owner. This store needs none of that: the DO instance it routes
|
|
19
|
+
* to *is* the scope, resolved from the caller identity the Worker verified, so a
|
|
20
|
+
* task is unreachable from any other caller by construction.
|
|
21
|
+
*/
|
|
22
|
+
export declare class DurableTaskStore implements TaskStore {
|
|
23
|
+
private readonly identity;
|
|
24
|
+
private readonly resolveAgent;
|
|
25
|
+
constructor(identity: GatewayIdentity, resolveAgent: AgentResolver);
|
|
26
|
+
load(taskId: string): Promise<Task | undefined>;
|
|
27
|
+
save(task: Task): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* `ListTasks` (new in v1.0): this caller's tasks, newest first, with the
|
|
30
|
+
* spec's optional `contextId` / `status` / `statusTimestampAfter` filters
|
|
31
|
+
* applied in SQL.
|
|
32
|
+
*
|
|
33
|
+
* The page token is the row offset as a decimal string. Offset paging can skip
|
|
34
|
+
* or repeat a row if tasks are written mid-scan, which is acceptable here: the
|
|
35
|
+
* listing is a diagnostic view over an append-mostly table, and the alternative
|
|
36
|
+
* (a keyset cursor) buys stability this caller has no use for.
|
|
37
|
+
*
|
|
38
|
+
* An agent whose DO does not implement `listTasks` answers the RPC with the
|
|
39
|
+
* protocol's own "unsupported" error rather than a binding-level failure — the
|
|
40
|
+
* capability is genuinely optional, and one of the two predecessor agents
|
|
41
|
+
* shipped without it for its whole life.
|
|
42
|
+
*/
|
|
43
|
+
list(params: ListTasksRequest): Promise<ListTasksResponse>;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=task-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-store.d.ts","sourceRoot":"","sources":["../../src/a2a/task-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,IAAI,EACV,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,gBAAiB,YAAW,SAAS;IAE9C,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,YAAY;gBADZ,QAAQ,EAAE,eAAe,EACzB,YAAY,EAAE,aAAa;IAGxC,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC;IAM/C,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAIrC;;;;;;;;;;;;;;OAcG;IACG,IAAI,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CAiCjE"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { UnsupportedOperationError } from "@a2a-js/sdk/errors";
|
|
2
|
+
import { TaskState } from "@a2a-js/sdk";
|
|
3
|
+
/**
|
|
4
|
+
* A durable {@link TaskStore} for the a2a-js `DefaultRequestHandler`, backed by
|
|
5
|
+
* the caller's agent Durable Object (via native RPC) instead of the SDK's
|
|
6
|
+
* per-request `InMemoryTaskStore`.
|
|
7
|
+
*
|
|
8
|
+
* Task state must survive the accept → async callback gap (and answer
|
|
9
|
+
* `GetTask`/`CancelTask`/`ListTasks` across requests), so it lives in the same
|
|
10
|
+
* per-caller DO that owns the Session — keyed by the verified `identity.key`,
|
|
11
|
+
* exactly like {@link file://./executor.ts A2AExecutor}. A workflow updates the
|
|
12
|
+
* same rows through its own DO RPC calls, so this store and the workflow share
|
|
13
|
+
* one source of truth.
|
|
14
|
+
*
|
|
15
|
+
* v1.0 passes a `ServerCallContext` to every method so a shared store can scope
|
|
16
|
+
* rows by tenant/owner. This store needs none of that: the DO instance it routes
|
|
17
|
+
* to *is* the scope, resolved from the caller identity the Worker verified, so a
|
|
18
|
+
* task is unreachable from any other caller by construction.
|
|
19
|
+
*/
|
|
20
|
+
export class DurableTaskStore {
|
|
21
|
+
identity;
|
|
22
|
+
resolveAgent;
|
|
23
|
+
constructor(identity, resolveAgent) {
|
|
24
|
+
this.identity = identity;
|
|
25
|
+
this.resolveAgent = resolveAgent;
|
|
26
|
+
}
|
|
27
|
+
async load(taskId) {
|
|
28
|
+
return ((await this.resolveAgent(this.identity).getTask(taskId)) ?? undefined);
|
|
29
|
+
}
|
|
30
|
+
async save(task) {
|
|
31
|
+
await this.resolveAgent(this.identity).saveTask(task);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* `ListTasks` (new in v1.0): this caller's tasks, newest first, with the
|
|
35
|
+
* spec's optional `contextId` / `status` / `statusTimestampAfter` filters
|
|
36
|
+
* applied in SQL.
|
|
37
|
+
*
|
|
38
|
+
* The page token is the row offset as a decimal string. Offset paging can skip
|
|
39
|
+
* or repeat a row if tasks are written mid-scan, which is acceptable here: the
|
|
40
|
+
* listing is a diagnostic view over an append-mostly table, and the alternative
|
|
41
|
+
* (a keyset cursor) buys stability this caller has no use for.
|
|
42
|
+
*
|
|
43
|
+
* An agent whose DO does not implement `listTasks` answers the RPC with the
|
|
44
|
+
* protocol's own "unsupported" error rather than a binding-level failure — the
|
|
45
|
+
* capability is genuinely optional, and one of the two predecessor agents
|
|
46
|
+
* shipped without it for its whole life.
|
|
47
|
+
*/
|
|
48
|
+
async list(params) {
|
|
49
|
+
const agent = this.resolveAgent(this.identity);
|
|
50
|
+
if (!agent.listTasks) {
|
|
51
|
+
throw new UnsupportedOperationError({
|
|
52
|
+
message: "this agent does not support ListTasks"
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
const pageSize = clampPageSize(params.pageSize);
|
|
56
|
+
const offset = parseOffset(params.pageToken);
|
|
57
|
+
const { tasks, totalSize } = await agent.listTasks({
|
|
58
|
+
contextId: params.contextId,
|
|
59
|
+
// The proto default (`UNSPECIFIED`) is the spec's "no status filter".
|
|
60
|
+
state: params.status === TaskState.TASK_STATE_UNSPECIFIED
|
|
61
|
+
? undefined
|
|
62
|
+
: params.status,
|
|
63
|
+
updatedAfter: parseTimestamp(params.statusTimestampAfter),
|
|
64
|
+
includeArtifacts: params.includeArtifacts === true,
|
|
65
|
+
historyLength: params.historyLength,
|
|
66
|
+
limit: pageSize,
|
|
67
|
+
offset
|
|
68
|
+
});
|
|
69
|
+
const nextOffset = offset + tasks.length;
|
|
70
|
+
return {
|
|
71
|
+
tasks,
|
|
72
|
+
nextPageToken: nextOffset < totalSize ? String(nextOffset) : "",
|
|
73
|
+
pageSize,
|
|
74
|
+
totalSize
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Page size bounds from the `ListTasks` spec: default 50, min 1, max 100. */
|
|
79
|
+
function clampPageSize(pageSize) {
|
|
80
|
+
if (pageSize === undefined)
|
|
81
|
+
return 50;
|
|
82
|
+
return Math.min(100, Math.max(1, Math.trunc(pageSize)));
|
|
83
|
+
}
|
|
84
|
+
/** Row offset from a page token; an unparseable token restarts at the first page. */
|
|
85
|
+
function parseOffset(pageToken) {
|
|
86
|
+
const offset = Number.parseInt(pageToken, 10);
|
|
87
|
+
return Number.isSafeInteger(offset) && offset > 0 ? offset : 0;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* `statusTimestampAfter` (ISO 8601) as epoch milliseconds, matching the
|
|
91
|
+
* `updated_at` column the store filters on — that column is stamped on every
|
|
92
|
+
* status write, so it tracks `status.timestamp`.
|
|
93
|
+
*/
|
|
94
|
+
function parseTimestamp(iso) {
|
|
95
|
+
if (!iso)
|
|
96
|
+
return undefined;
|
|
97
|
+
const ms = Date.parse(iso);
|
|
98
|
+
return Number.isNaN(ms) ? undefined : ms;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=task-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-store.js","sourceRoot":"","sources":["../../src/a2a/task-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { type Role, type TaskState } from "@a2a-js/sdk";
|
|
2
|
+
/**
|
|
3
|
+
* The A2A `Task` (and its nested types) as they cross the DO RPC boundary.
|
|
4
|
+
*
|
|
5
|
+
* Two constraints shape these, and the SDK types satisfy neither directly:
|
|
6
|
+
*
|
|
7
|
+
* 1. **Serializability.** A v1.0 `Part` carries a `content` oneof whose `raw`
|
|
8
|
+
* arm is a `Buffer`; its methods fail Cloudflare's `Rpc.Serializable`
|
|
9
|
+
* constraint and collapse the generated DO-stub return types to `never`.
|
|
10
|
+
* Pinning `content` to its `text` arm removes that arm — and this agent only
|
|
11
|
+
* ever produces, and only ever receives, text (see {@link file://./parts.ts}).
|
|
12
|
+
* 2. **Instantiation depth.** These are written out as plain interfaces rather
|
|
13
|
+
* than derived with `Omit<Task, …> & { … }`. The v1.0 model is deep enough
|
|
14
|
+
* that layering mapped types under Cloudflare's own RPC type mapping exceeds
|
|
15
|
+
* TypeScript's instantiation limit at the stub call sites.
|
|
16
|
+
*
|
|
17
|
+
* Each of these widens to its SDK counterpart for free, so no cast is needed at
|
|
18
|
+
* the a2a-js edges (`AgentEvent.task`, `TaskStore.load`/`list`); the
|
|
19
|
+
* {@link _Widens} guard below fails the build if that ever stops holding.
|
|
20
|
+
* Runtime is unaffected — tasks are JSON-round-tripped in storage, so any field
|
|
21
|
+
* the SDK adds survives even though these types don't name it.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* The A2A extension `metadata` bag as this agent handles it: never set. Its SDK
|
|
25
|
+
* type is an open `{ [key: string]: any }` map, which is both what collapses the
|
|
26
|
+
* DO-stub types (`unknown` members fail `Rpc.Serializable`) and what pushes the
|
|
27
|
+
* stub-mapped `Task` past TypeScript's instantiation limit. Pinning it to
|
|
28
|
+
* `undefined` keeps both in check and still widens to the SDK type. Metadata a
|
|
29
|
+
* caller sent is not erased — stored tasks are JSON blobs, so it round-trips
|
|
30
|
+
* untouched; it is simply invisible to this agent, which never reads it.
|
|
31
|
+
*/
|
|
32
|
+
type A2AMetadata = undefined;
|
|
33
|
+
/** A `Part` restricted to the text arm of its `content` oneof. */
|
|
34
|
+
export interface PlainPart {
|
|
35
|
+
content: {
|
|
36
|
+
$case: "text";
|
|
37
|
+
value: string;
|
|
38
|
+
};
|
|
39
|
+
metadata: A2AMetadata;
|
|
40
|
+
filename: string;
|
|
41
|
+
mediaType: string;
|
|
42
|
+
}
|
|
43
|
+
/** A `Message` whose parts are all {@link PlainPart}. */
|
|
44
|
+
export interface PlainMessage {
|
|
45
|
+
messageId: string;
|
|
46
|
+
contextId: string;
|
|
47
|
+
taskId: string;
|
|
48
|
+
role: Role;
|
|
49
|
+
parts: PlainPart[];
|
|
50
|
+
metadata: A2AMetadata;
|
|
51
|
+
extensions: string[];
|
|
52
|
+
referenceTaskIds: string[];
|
|
53
|
+
}
|
|
54
|
+
/** An `Artifact` whose parts are all {@link PlainPart}. */
|
|
55
|
+
export interface PlainArtifact {
|
|
56
|
+
artifactId: string;
|
|
57
|
+
name: string;
|
|
58
|
+
description: string;
|
|
59
|
+
parts: PlainPart[];
|
|
60
|
+
metadata: A2AMetadata;
|
|
61
|
+
extensions: string[];
|
|
62
|
+
}
|
|
63
|
+
/** A `TaskStatus` whose message is a {@link PlainMessage}. */
|
|
64
|
+
export interface PlainStatus {
|
|
65
|
+
state: TaskState;
|
|
66
|
+
message: PlainMessage | undefined;
|
|
67
|
+
timestamp: string | undefined;
|
|
68
|
+
}
|
|
69
|
+
/** A `Task` built entirely from the plain types above. */
|
|
70
|
+
export interface PlainTask {
|
|
71
|
+
id: string;
|
|
72
|
+
contextId: string;
|
|
73
|
+
status: PlainStatus;
|
|
74
|
+
artifacts: PlainArtifact[];
|
|
75
|
+
history: PlainMessage[];
|
|
76
|
+
metadata: A2AMetadata;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* A task state rendered for humans and for the `notify_tasks.state` column
|
|
80
|
+
* (`TASK_STATE_INPUT_REQUIRED` → `input-required`). v1.0 states are numeric enum
|
|
81
|
+
* members, so anything stored or displayed goes through the protobuf JSON name
|
|
82
|
+
* first. Matches looping-gateway's `taskStateLabel`.
|
|
83
|
+
*/
|
|
84
|
+
export declare function taskStateLabel(state: TaskState): string;
|
|
85
|
+
export {};
|
|
86
|
+
//# sourceMappingURL=task.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task.d.ts","sourceRoot":"","sources":["../../src/a2a/task.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,IAAI,EAET,KAAK,SAAS,EACf,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;;;;;;;;GAQG;AACH,KAAK,WAAW,GAAG,SAAS,CAAC;AAE7B,kEAAkE;AAClE,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,QAAQ,EAAE,WAAW,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,yDAAyD;AACzD,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,QAAQ,EAAE,WAAW,CAAC;IACtB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,2DAA2D;AAC3D,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,QAAQ,EAAE,WAAW,CAAC;IACtB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,8DAA8D;AAC9D,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,YAAY,GAAG,SAAS,CAAC;IAClC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B;AAED,0DAA0D;AAC1D,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,QAAQ,EAAE,WAAW,CAAC;CACvB;AAQD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAKvD"}
|
package/dist/a2a/task.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { taskStateToJSON } from "@a2a-js/sdk";
|
|
2
|
+
const _assertWidens = true;
|
|
3
|
+
void _assertWidens;
|
|
4
|
+
/**
|
|
5
|
+
* A task state rendered for humans and for the `notify_tasks.state` column
|
|
6
|
+
* (`TASK_STATE_INPUT_REQUIRED` → `input-required`). v1.0 states are numeric enum
|
|
7
|
+
* members, so anything stored or displayed goes through the protobuf JSON name
|
|
8
|
+
* first. Matches looping-gateway's `taskStateLabel`.
|
|
9
|
+
*/
|
|
10
|
+
export function taskStateLabel(state) {
|
|
11
|
+
return taskStateToJSON(state)
|
|
12
|
+
.replace(/^TASK_STATE_/, "")
|
|
13
|
+
.toLowerCase()
|
|
14
|
+
.replace(/_/g, "-");
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=task.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task.js","sourceRoot":"","sources":["../../src/a2a/task.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EAIhB,MAAM,aAAa,CAAC;AAqFrB,MAAM,aAAa,GAAY,IAAI,CAAC;AACpC,KAAK,aAAa,CAAC;AAEnB;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,KAAgB;IAC7C,OAAO,eAAe,CAAC,KAAK,CAAC;SAC1B,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC;SAC3B,WAAW,EAAE;SACb,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type JWTPayload } from "jose";
|
|
2
|
+
/**
|
|
3
|
+
* Default namespaced claim carrying the minimal caller identity.
|
|
4
|
+
*
|
|
5
|
+
* Overridable via {@link VerifyOptions.identityClaim} for a deployment that
|
|
6
|
+
* isn't behind looping-gateway; the default is the Looping namespace so an
|
|
7
|
+
* agent built on this package needs no configuration to interoperate.
|
|
8
|
+
*/
|
|
9
|
+
export declare const IDENTITY_CLAIM = "https://looping.ai/identity";
|
|
10
|
+
/**
|
|
11
|
+
* The gateway-agent instance identity forwarded by the gateway — i.e. which
|
|
12
|
+
* registered agent instance dispatched this call, not the human end user.
|
|
13
|
+
* Mirrors `RemoteIdentity` in looping-gateway's `src/auth/agent-jwt.ts`.
|
|
14
|
+
*
|
|
15
|
+
* Any end user travels unverified, inline in the message text; the gateway
|
|
16
|
+
* deliberately excludes it from this signed claim so a remote agent cannot read
|
|
17
|
+
* the full caller auth context. Every field is optional because the claim is
|
|
18
|
+
* whatever the issuer put there — {@link GatewayIdentity.key} is the only one
|
|
19
|
+
* anything downstream depends on, and a caller without it is rejected before a
|
|
20
|
+
* DO is ever addressed.
|
|
21
|
+
*/
|
|
22
|
+
export interface GatewayIdentity {
|
|
23
|
+
/** Canonical instance key, e.g. `custom:7:analytics`. */
|
|
24
|
+
key?: string;
|
|
25
|
+
/** Registry name of the logical agent instance. */
|
|
26
|
+
name?: string;
|
|
27
|
+
/** Dispatch kind of the caller (`"custom"` for remote agents). */
|
|
28
|
+
kind?: string;
|
|
29
|
+
/** Workspace the calling agent instance belongs to. */
|
|
30
|
+
workspaceId?: number | null;
|
|
31
|
+
}
|
|
32
|
+
/** Thrown when a gateway token is missing or fails verification. */
|
|
33
|
+
export declare class GatewayAuthError extends Error {
|
|
34
|
+
constructor(message: string);
|
|
35
|
+
}
|
|
36
|
+
/** Extract the Bearer token from an `Authorization` header, or null. */
|
|
37
|
+
export declare function bearerToken(request: Request): string | null;
|
|
38
|
+
export interface VerifyOptions {
|
|
39
|
+
/** Allowed gateway origins — validates both the `jku` domain and `iss` claim. */
|
|
40
|
+
allowedOrigins: string[];
|
|
41
|
+
audience: string;
|
|
42
|
+
/** Claim carrying the caller identity. Defaults to {@link IDENTITY_CLAIM}. */
|
|
43
|
+
identityClaim?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Normalize configured gateway origins to the HTTPS origins emitted in gateway
|
|
47
|
+
* JWT `jku` and `iss` claims. This accepts a hostname, `http://` URL, or URL
|
|
48
|
+
* with a trailing slash while keeping the verification allowlist exact.
|
|
49
|
+
*/
|
|
50
|
+
export declare function normalizeGatewayOrigins(origins: string[]): string[];
|
|
51
|
+
/**
|
|
52
|
+
* Verify a gateway JWT and return its payload + parsed identity.
|
|
53
|
+
*
|
|
54
|
+
* The `jku` JWK Set URL is read directly from the token's protected header
|
|
55
|
+
* (RFC 7515 §4.1.2) and validated against the allowlist before fetching, so no
|
|
56
|
+
* separate JWKS URL configuration is needed on the remote side.
|
|
57
|
+
*
|
|
58
|
+
* Throws {@link GatewayAuthError} on any failure.
|
|
59
|
+
*/
|
|
60
|
+
export declare function verifyGatewayToken(token: string, opts: VerifyOptions): Promise<{
|
|
61
|
+
payload: JWTPayload;
|
|
62
|
+
identity: GatewayIdentity;
|
|
63
|
+
}>;
|
|
64
|
+
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/a2a/verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,UAAU,EAChB,MAAM,MAAM,CAAC;AAyBd;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,gCAAgC,CAAC;AAE5D;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAe;IAC9B,yDAAyD;IACzD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,oEAAoE;AACpE,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAcD,wEAAwE;AACxE,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAI3D;AAED,MAAM,WAAW,aAAa;IAC5B,iFAAiF;IACjF,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAenE;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC;IAAE,OAAO,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,eAAe,CAAA;CAAE,CAAC,CAyC7D"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { createRemoteJWKSet, decodeJwt, decodeProtectedHeader, jwtVerify } from "jose";
|
|
2
|
+
/**
|
|
3
|
+
* Verify the gateway identity JWT (the "B authenticates A" half of zero-trust).
|
|
4
|
+
*
|
|
5
|
+
* The gateway signs a short-lived EdDSA JWT and sends it as a Bearer token on
|
|
6
|
+
* every A2A call. Per RFC 7515 §4.1.2 it embeds a `jku` header pointing at its
|
|
7
|
+
* public JWKS, so remote agents don't need a separately configured JWKS URL —
|
|
8
|
+
* they read `jku` straight from the token and verify the key from there.
|
|
9
|
+
*
|
|
10
|
+
* Security: the `jku` origin is validated against the allowed origins before
|
|
11
|
+
* fetching, preventing key-injection attacks (an attacker cannot point `jku` at
|
|
12
|
+
* their own JWKS and have it accepted).
|
|
13
|
+
*
|
|
14
|
+
* **This file is a guardian.** The four checks below — `jku` present → origin
|
|
15
|
+
* allowlist → `iss` origin equals `jku` origin → `jwtVerify` pinned to EdDSA —
|
|
16
|
+
* are the whole zero-trust contract, and they arrived here byte-identical from
|
|
17
|
+
* two independently-evolved agents. Do not weaken any of them, do not make any
|
|
18
|
+
* of them optional, and do not add a bypass for local development: run a local
|
|
19
|
+
* gateway instead.
|
|
20
|
+
*/
|
|
21
|
+
/** Algorithm the gateway signs with — reject anything else. */
|
|
22
|
+
const ALG = "EdDSA";
|
|
23
|
+
/**
|
|
24
|
+
* Default namespaced claim carrying the minimal caller identity.
|
|
25
|
+
*
|
|
26
|
+
* Overridable via {@link VerifyOptions.identityClaim} for a deployment that
|
|
27
|
+
* isn't behind looping-gateway; the default is the Looping namespace so an
|
|
28
|
+
* agent built on this package needs no configuration to interoperate.
|
|
29
|
+
*/
|
|
30
|
+
export const IDENTITY_CLAIM = "https://looping.ai/identity";
|
|
31
|
+
/** Thrown when a gateway token is missing or fails verification. */
|
|
32
|
+
export class GatewayAuthError extends Error {
|
|
33
|
+
constructor(message) {
|
|
34
|
+
super(message);
|
|
35
|
+
this.name = "GatewayAuthError";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// jose's remote JWKS helper caches keys + handles rotation; build one per URL
|
|
39
|
+
// and reuse it across requests in the same isolate.
|
|
40
|
+
const jwksByUrl = new Map();
|
|
41
|
+
function jwksFor(url) {
|
|
42
|
+
let set = jwksByUrl.get(url);
|
|
43
|
+
if (!set) {
|
|
44
|
+
set = createRemoteJWKSet(new URL(url));
|
|
45
|
+
jwksByUrl.set(url, set);
|
|
46
|
+
}
|
|
47
|
+
return set;
|
|
48
|
+
}
|
|
49
|
+
/** Extract the Bearer token from an `Authorization` header, or null. */
|
|
50
|
+
export function bearerToken(request) {
|
|
51
|
+
const header = request.headers.get("authorization") ?? "";
|
|
52
|
+
const match = /^Bearer\s+(.+)$/i.exec(header.trim());
|
|
53
|
+
return match ? match[1] : null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Normalize configured gateway origins to the HTTPS origins emitted in gateway
|
|
57
|
+
* JWT `jku` and `iss` claims. This accepts a hostname, `http://` URL, or URL
|
|
58
|
+
* with a trailing slash while keeping the verification allowlist exact.
|
|
59
|
+
*/
|
|
60
|
+
export function normalizeGatewayOrigins(origins) {
|
|
61
|
+
return origins.map((origin) => {
|
|
62
|
+
const trimmed = origin.trim();
|
|
63
|
+
if (!trimmed) {
|
|
64
|
+
throw new GatewayAuthError("allowed gateway origin cannot be empty");
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
return new URL(`https://${trimmed.replace(/^https?:\/\//i, "")}`).origin;
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
71
|
+
throw new GatewayAuthError(`invalid allowed gateway origin '${origin}': ${message}`);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Verify a gateway JWT and return its payload + parsed identity.
|
|
77
|
+
*
|
|
78
|
+
* The `jku` JWK Set URL is read directly from the token's protected header
|
|
79
|
+
* (RFC 7515 §4.1.2) and validated against the allowlist before fetching, so no
|
|
80
|
+
* separate JWKS URL configuration is needed on the remote side.
|
|
81
|
+
*
|
|
82
|
+
* Throws {@link GatewayAuthError} on any failure.
|
|
83
|
+
*/
|
|
84
|
+
export async function verifyGatewayToken(token, opts) {
|
|
85
|
+
try {
|
|
86
|
+
const allowedOrigins = normalizeGatewayOrigins(opts.allowedOrigins);
|
|
87
|
+
// Extract jku from the protected header — this is the standard way the
|
|
88
|
+
// gateway advertises where to fetch its public key (RFC 7515 §4.1.2).
|
|
89
|
+
const header = decodeProtectedHeader(token);
|
|
90
|
+
const jku = header.jku;
|
|
91
|
+
if (!jku) {
|
|
92
|
+
throw new GatewayAuthError("gateway JWT missing jku header (RFC 7515 §4.1.2)");
|
|
93
|
+
}
|
|
94
|
+
// Security: validate the jku origin before fetching. Without this an
|
|
95
|
+
// attacker could forge a token with jku pointing at their own JWKS.
|
|
96
|
+
const jkuOrigin = new URL(jku).origin;
|
|
97
|
+
if (!allowedOrigins.includes(jkuOrigin)) {
|
|
98
|
+
throw new GatewayAuthError(`jku origin '${jkuOrigin}' is not in the allowed gateway origins`);
|
|
99
|
+
}
|
|
100
|
+
// Prevent one listed gateway from impersonating another: the origin where
|
|
101
|
+
// keys are fetched must match the origin that issued the token.
|
|
102
|
+
const rawIss = decodeJwt(token).iss ?? "";
|
|
103
|
+
const issOrigin = new URL(rawIss).origin;
|
|
104
|
+
if (issOrigin !== jkuOrigin) {
|
|
105
|
+
throw new GatewayAuthError(`jku origin '${jkuOrigin}' does not match iss origin '${issOrigin}'`);
|
|
106
|
+
}
|
|
107
|
+
const { payload } = await jwtVerify(token, jwksFor(jku), {
|
|
108
|
+
issuer: allowedOrigins,
|
|
109
|
+
audience: opts.audience,
|
|
110
|
+
algorithms: [ALG]
|
|
111
|
+
});
|
|
112
|
+
const claim = opts.identityClaim ?? IDENTITY_CLAIM;
|
|
113
|
+
const identity = payload[claim] ?? {};
|
|
114
|
+
return { payload, identity };
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
if (err instanceof GatewayAuthError)
|
|
118
|
+
throw err;
|
|
119
|
+
throw new GatewayAuthError(err.message);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/a2a/verify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One round's turn allowance, carried as a single mutable object rather than as a
|
|
3
|
+
* number threaded down and a count threaded back up.
|
|
4
|
+
*
|
|
5
|
+
* The distinction matters because a round is not one model call. Each slot may
|
|
6
|
+
* repair a rejected decomposition several times before the round moves on to the
|
|
7
|
+
* fallback, and every one of those attempts *restarts* the round from the same
|
|
8
|
+
* messages — so a failed attempt is work redone, with whatever tool side effects
|
|
9
|
+
* it already had. They all spend the same allowance. When that allowance was a
|
|
10
|
+
* plain number, keeping later attempts honest meant remembering to decrement it
|
|
11
|
+
* between each pair, and forgetting let one round cost a multiple of its budget.
|
|
12
|
+
* Sharing one object removes the thing there was to forget.
|
|
13
|
+
*
|
|
14
|
+
* `spent` is mutated where the turn is actually spent (the model loop's
|
|
15
|
+
* `onStepEnd`) and read where it actually matters (the RPC return that the
|
|
16
|
+
* Workflow meters against the Task). Nothing in between carries it.
|
|
17
|
+
*/
|
|
18
|
+
/** One round's allowance and what it has spent, across every attempt in the round. */
|
|
19
|
+
export interface TurnBudget {
|
|
20
|
+
/** What this round was allowed when it started: the Task's unspent turns. */
|
|
21
|
+
readonly allowance: number;
|
|
22
|
+
/** Turns spent so far. Mutated by the model loop, one per completed step. */
|
|
23
|
+
spent: number;
|
|
24
|
+
}
|
|
25
|
+
/** A fresh budget for a round that may spend `allowance` turns. */
|
|
26
|
+
export declare const newTurnBudget: (allowance: number) => TurnBudget;
|
|
27
|
+
/**
|
|
28
|
+
* Steps one `generateText` attempt may spend, given what the run has already used.
|
|
29
|
+
*
|
|
30
|
+
* Must be evaluated **per attempt**: `isStepCount` counts steps within a single
|
|
31
|
+
* call, so an allowance computed once and shared with later attempts would hand
|
|
32
|
+
* each of them the turns its predecessors already burned.
|
|
33
|
+
*
|
|
34
|
+
* The `Math.max(1, …)` floor is load-bearing, not defensive. A primary that
|
|
35
|
+
* consumed the entire remainder still leaves the fallback one step, because an
|
|
36
|
+
* attempt with nothing to spend cannot reach an ending — which fails the round and
|
|
37
|
+
* costs the caller its answer, a far worse trade than one turn. So a round may
|
|
38
|
+
* exceed its allowance by exactly one, and never by more.
|
|
39
|
+
*
|
|
40
|
+
* The bound survives repair attempts because only an attempt that *starts* already
|
|
41
|
+
* at the allowance can overrun, and only by the one step of this floor: an attempt
|
|
42
|
+
* that starts under it is capped at the exact remainder, and `runTurn` refuses to
|
|
43
|
+
* open a repair once the allowance is gone. That leaves the fallback's first
|
|
44
|
+
* attempt as the only one that can ever begin overdrawn.
|
|
45
|
+
*/
|
|
46
|
+
export declare const stepAllowance: (maxTurns: number, spent: number) => number;
|
|
47
|
+
//# sourceMappingURL=budget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.d.ts","sourceRoot":"","sources":["../../src/agent/budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,sFAAsF;AACtF,MAAM,WAAW,UAAU;IACzB,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;CACf;AAED,mEAAmE;AACnE,eAAO,MAAM,aAAa,GAAI,WAAW,MAAM,KAAG,UAGhD,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,aAAa,GAAI,UAAU,MAAM,EAAE,OAAO,MAAM,KAAG,MACjC,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One round's turn allowance, carried as a single mutable object rather than as a
|
|
3
|
+
* number threaded down and a count threaded back up.
|
|
4
|
+
*
|
|
5
|
+
* The distinction matters because a round is not one model call. Each slot may
|
|
6
|
+
* repair a rejected decomposition several times before the round moves on to the
|
|
7
|
+
* fallback, and every one of those attempts *restarts* the round from the same
|
|
8
|
+
* messages — so a failed attempt is work redone, with whatever tool side effects
|
|
9
|
+
* it already had. They all spend the same allowance. When that allowance was a
|
|
10
|
+
* plain number, keeping later attempts honest meant remembering to decrement it
|
|
11
|
+
* between each pair, and forgetting let one round cost a multiple of its budget.
|
|
12
|
+
* Sharing one object removes the thing there was to forget.
|
|
13
|
+
*
|
|
14
|
+
* `spent` is mutated where the turn is actually spent (the model loop's
|
|
15
|
+
* `onStepEnd`) and read where it actually matters (the RPC return that the
|
|
16
|
+
* Workflow meters against the Task). Nothing in between carries it.
|
|
17
|
+
*/
|
|
18
|
+
/** A fresh budget for a round that may spend `allowance` turns. */
|
|
19
|
+
export const newTurnBudget = (allowance) => ({
|
|
20
|
+
allowance,
|
|
21
|
+
spent: 0
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* Steps one `generateText` attempt may spend, given what the run has already used.
|
|
25
|
+
*
|
|
26
|
+
* Must be evaluated **per attempt**: `isStepCount` counts steps within a single
|
|
27
|
+
* call, so an allowance computed once and shared with later attempts would hand
|
|
28
|
+
* each of them the turns its predecessors already burned.
|
|
29
|
+
*
|
|
30
|
+
* The `Math.max(1, …)` floor is load-bearing, not defensive. A primary that
|
|
31
|
+
* consumed the entire remainder still leaves the fallback one step, because an
|
|
32
|
+
* attempt with nothing to spend cannot reach an ending — which fails the round and
|
|
33
|
+
* costs the caller its answer, a far worse trade than one turn. So a round may
|
|
34
|
+
* exceed its allowance by exactly one, and never by more.
|
|
35
|
+
*
|
|
36
|
+
* The bound survives repair attempts because only an attempt that *starts* already
|
|
37
|
+
* at the allowance can overrun, and only by the one step of this floor: an attempt
|
|
38
|
+
* that starts under it is capped at the exact remainder, and `runTurn` refuses to
|
|
39
|
+
* open a repair once the allowance is gone. That leaves the fallback's first
|
|
40
|
+
* attempt as the only one that can ever begin overdrawn.
|
|
41
|
+
*/
|
|
42
|
+
export const stepAllowance = (maxTurns, spent) => Math.max(1, maxTurns - spent);
|
|
43
|
+
//# sourceMappingURL=budget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.js","sourceRoot":"","sources":["../../src/agent/budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAUH,mEAAmE;AACnE,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,SAAiB,EAAc,EAAE,CAAC,CAAC;IAC/D,SAAS;IACT,KAAK,EAAE,CAAC;CACT,CAAC,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,QAAgB,EAAE,KAAa,EAAU,EAAE,CACvE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC,CAAC"}
|