ankka 0.7.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_proto/ankka/protocol/v1/agent_pb.d.ts +95 -0
- package/dist/_proto/ankka/protocol/v1/agent_pb.js +16 -1
- package/dist/_proto/ankka/protocol/v1/discovery_pb.d.ts +235 -1
- package/dist/_proto/ankka/protocol/v1/discovery_pb.js +45 -5
- package/dist/_proto/ankka/protocol/v1/endpoint_pb.d.ts +63 -0
- package/dist/_proto/ankka/protocol/v1/endpoint_pb.js +15 -5
- package/dist/_proto/ankka/protocol/v1/wasm_pb.d.ts +307 -0
- package/dist/_proto/ankka/protocol/v1/wasm_pb.js +72 -0
- package/dist/autonomous.d.ts +154 -0
- package/dist/autonomous.js +199 -0
- package/dist/client.d.ts +13 -0
- package/dist/client.js +49 -0
- package/dist/context.d.ts +15 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/kinds.d.ts +1 -1
- package/dist/kinds.js +1 -0
- package/dist/routes.d.ts +41 -5
- package/dist/routes.js +24 -1
- package/dist/server/agent.d.ts +8 -2
- package/dist/server/agent.js +59 -2
- package/dist/server/http.js +13 -0
- package/dist/service.d.ts +11 -1
- package/dist/service.js +46 -5
- package/dist/spec.d.ts +6 -3
- package/dist/spec.js +62 -6
- package/dist/testkit/integration.js +5 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/service.js
CHANGED
|
@@ -11,8 +11,9 @@ import { View } from "./view.js";
|
|
|
11
11
|
import { Consumer } from "./consumer.js";
|
|
12
12
|
import { TimedAction } from "./timedAction.js";
|
|
13
13
|
import { Agent } from "./agent.js";
|
|
14
|
+
import { AutonomousAgent, COMPLETE_TASK, FAIL_TASK } from "./autonomous.js";
|
|
14
15
|
import { Endpoint } from "./endpoint.js";
|
|
15
|
-
import {
|
|
16
|
+
import { isAcl } from "./routes.js";
|
|
16
17
|
import { ComponentClient } from "./client.js";
|
|
17
18
|
import { Server } from "./server/server.js";
|
|
18
19
|
import { renderSpec } from "./spec.js";
|
|
@@ -100,6 +101,8 @@ export class ServiceBuilder {
|
|
|
100
101
|
add(registerTimedAction(cls, problems), cls);
|
|
101
102
|
else if (extendsBase(cls, Agent))
|
|
102
103
|
add(registerAgent(cls, problems), cls);
|
|
104
|
+
else if (extendsBase(cls, AutonomousAgent))
|
|
105
|
+
add(registerAutonomousAgent(cls, problems), cls);
|
|
103
106
|
else if (extendsBase(cls, Endpoint)) {
|
|
104
107
|
const ep = cls;
|
|
105
108
|
if (typeof ep.prefix === "string") {
|
|
@@ -117,7 +120,7 @@ export class ServiceBuilder {
|
|
|
117
120
|
}
|
|
118
121
|
}
|
|
119
122
|
else {
|
|
120
|
-
problems.push(`${nameOf(cls)} is not a component class: it must extend EventSourcedEntity, KeyValueEntity, Workflow, View, Consumer, TimedAction, Agent or Endpoint`);
|
|
123
|
+
problems.push(`${nameOf(cls)} is not a component class: it must extend EventSourcedEntity, KeyValueEntity, Workflow, View, Consumer, TimedAction, Agent, AutonomousAgent or Endpoint`);
|
|
121
124
|
}
|
|
122
125
|
}
|
|
123
126
|
if (problems.length > 0)
|
|
@@ -314,6 +317,44 @@ function registerAgent(cls, problems) {
|
|
|
314
317
|
return undefined;
|
|
315
318
|
return Object.freeze({ kind: "agent", id: cls.componentId, cls, handlers, tools, guardrails, role: cls.role ?? "", maxToolCallSteps });
|
|
316
319
|
}
|
|
320
|
+
function registerAutonomousAgent(cls, problems) {
|
|
321
|
+
const { fail, ok } = checker(cls, problems);
|
|
322
|
+
requireId(cls, fail);
|
|
323
|
+
if (typeof cls.description !== "string" || cls.description.trim() === "")
|
|
324
|
+
fail("a description is required");
|
|
325
|
+
const tools = new Map();
|
|
326
|
+
for (const [property, t] of Object.entries(cls.tools ?? {})) {
|
|
327
|
+
if (typeof t !== "object" || t === null || typeof t.name !== "string" || typeof t.run !== "function") {
|
|
328
|
+
fail(`tools.${property} is not a tool: declare it with tool(name, description, input, run)`);
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
if (t.name === COMPLETE_TASK || t.name === FAIL_TASK)
|
|
332
|
+
fail(`tool name ${JSON.stringify(t.name)} is reserved`);
|
|
333
|
+
if (tools.has(t.name))
|
|
334
|
+
fail(`two tools declare the name ${JSON.stringify(t.name)}`);
|
|
335
|
+
tools.set(t.name, t);
|
|
336
|
+
}
|
|
337
|
+
const guardrails = new Map();
|
|
338
|
+
for (const g of Object.values(cls.guardrails ?? {})) {
|
|
339
|
+
if (guardrails.has(g.name))
|
|
340
|
+
fail(`two guardrails declare the name ${JSON.stringify(g.name)}`);
|
|
341
|
+
guardrails.set(g.name, g);
|
|
342
|
+
}
|
|
343
|
+
const taskTypes = new Map();
|
|
344
|
+
const accepts = cls.accepts ?? [];
|
|
345
|
+
if (accepts.length === 0)
|
|
346
|
+
fail("it accepts no task type: declare accepts = [taskAcceptance(...)]");
|
|
347
|
+
for (const a of accepts) {
|
|
348
|
+
if (taskTypes.has(a.taskType.name))
|
|
349
|
+
fail(`task type ${JSON.stringify(a.taskType.name)} is accepted twice`);
|
|
350
|
+
if (!Number.isInteger(a.maxIterations) || a.maxIterations < 1)
|
|
351
|
+
fail(`task type ${JSON.stringify(a.taskType.name)} needs a budget of at least one iteration`);
|
|
352
|
+
taskTypes.set(a.taskType.name, a.taskType);
|
|
353
|
+
}
|
|
354
|
+
if (!ok())
|
|
355
|
+
return undefined;
|
|
356
|
+
return Object.freeze({ kind: "autonomous-agent", id: cls.componentId, cls, tools, guardrails, taskTypes });
|
|
357
|
+
}
|
|
317
358
|
/** Collects a handler table by wire name, reporting a duplicate wire name or a kind the component cannot host. */
|
|
318
359
|
export function collectHandlers(table, what, allowed, fail) {
|
|
319
360
|
const out = new Map();
|
|
@@ -347,8 +388,8 @@ function registerEndpoint(cls, problems) {
|
|
|
347
388
|
if (typeof cls.prefix !== "string" || !cls.prefix.startsWith("/") || (cls.prefix.length > 1 && cls.prefix.endsWith("/"))) {
|
|
348
389
|
fail(`needs a static prefix starting with "/" and not ending with one, not ${JSON.stringify(cls.prefix)}`);
|
|
349
390
|
}
|
|
350
|
-
if (!
|
|
351
|
-
fail(`needs a static acl — Acl.allowAll, Acl.denyAll or Acl.
|
|
391
|
+
if (!isAcl(cls.acl)) {
|
|
392
|
+
fail(`needs a static acl — Acl.allowAll, Acl.denyAll, Acl.authenticated or Acl.allowCallers(...). An unstated access rule is a decision nobody made`);
|
|
352
393
|
}
|
|
353
394
|
const routes = new Map();
|
|
354
395
|
const seen = new Map();
|
|
@@ -367,7 +408,7 @@ function registerEndpoint(cls, problems) {
|
|
|
367
408
|
if (other)
|
|
368
409
|
fail(`routes ${other} and ${id} both declare ${key}`);
|
|
369
410
|
seen.set(key, id);
|
|
370
|
-
if (r.acl !== undefined && !
|
|
411
|
+
if (r.acl !== undefined && !isAcl(r.acl))
|
|
371
412
|
fail(`routes.${id}: unknown acl ${JSON.stringify(r.acl)}`);
|
|
372
413
|
routes.set(id, r);
|
|
373
414
|
}
|
package/dist/spec.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type MessageInitShape } from "@bufbuild/protobuf";
|
|
2
|
+
import { Endpoint_Acl, type CallerMatcherSchema, type Spec } from "./_proto/ankka/protocol/v1/discovery_pb.ts";
|
|
2
3
|
import type { Registry } from "./service.ts";
|
|
3
|
-
import { Acl } from "./routes.ts";
|
|
4
|
-
export declare const PROTOCOL_VERSION = "1.
|
|
4
|
+
import type { Acl } from "./routes.ts";
|
|
5
|
+
export declare const PROTOCOL_VERSION = "1.2";
|
|
5
6
|
export declare const SDK_NAME = "ankka-typescript";
|
|
6
7
|
export declare function aclToProto(acl: Acl): Endpoint_Acl;
|
|
8
|
+
/** The callers a CALLERS acl names, as discovery carries them; empty for every other rule. */
|
|
9
|
+
export declare function callersToProto(acl: Acl): MessageInitShape<typeof CallerMatcherSchema>[];
|
|
7
10
|
export declare function renderSpec(registry: Registry): Spec;
|
package/dist/spec.js
CHANGED
|
@@ -1,23 +1,42 @@
|
|
|
1
1
|
// Renders the discovery Spec from the registry: what the sidecar hosts is exactly what is registered.
|
|
2
2
|
import { create } from "@bufbuild/protobuf";
|
|
3
3
|
import { Endpoint_Acl, SpecSchema } from "./_proto/ankka/protocol/v1/discovery_pb.js";
|
|
4
|
+
import { resultSchemaJson } from "./autonomous.js";
|
|
4
5
|
import { kindToProto } from "./kinds.js";
|
|
5
|
-
import { Acl } from "./routes.js";
|
|
6
6
|
import { toJsonSchema } from "./schema.js";
|
|
7
7
|
import { isCodec } from "./codec.js";
|
|
8
8
|
import { VERSION } from "./version.js";
|
|
9
|
-
export const PROTOCOL_VERSION = "1.
|
|
9
|
+
export const PROTOCOL_VERSION = "1.2";
|
|
10
10
|
export const SDK_NAME = "ankka-typescript";
|
|
11
11
|
export function aclToProto(acl) {
|
|
12
|
+
if (typeof acl === "object")
|
|
13
|
+
return Endpoint_Acl.CALLERS;
|
|
12
14
|
switch (acl) {
|
|
13
|
-
case
|
|
15
|
+
case "allow-all":
|
|
14
16
|
return Endpoint_Acl.ALLOW_ALL;
|
|
15
|
-
case
|
|
17
|
+
case "deny-all":
|
|
16
18
|
return Endpoint_Acl.DENY_ALL;
|
|
17
|
-
case
|
|
19
|
+
case "authenticated":
|
|
18
20
|
return Endpoint_Acl.AUTHENTICATED;
|
|
19
21
|
}
|
|
20
22
|
}
|
|
23
|
+
/** The callers a CALLERS acl names, as discovery carries them; empty for every other rule. */
|
|
24
|
+
export function callersToProto(acl) {
|
|
25
|
+
if (typeof acl !== "object")
|
|
26
|
+
return [];
|
|
27
|
+
return acl.callers.map((m) => {
|
|
28
|
+
switch (m.kind) {
|
|
29
|
+
case "internet":
|
|
30
|
+
return { kind: { case: "internet", value: {} } };
|
|
31
|
+
case "anyInProject":
|
|
32
|
+
return { kind: { case: "anyInProject", value: {} } };
|
|
33
|
+
case "self":
|
|
34
|
+
return { kind: { case: "self", value: {} } };
|
|
35
|
+
case "service":
|
|
36
|
+
return { kind: { case: "service", value: m.project !== undefined ? { name: m.name, project: m.project } : { name: m.name } } };
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
21
40
|
function handlerInits(handlers) {
|
|
22
41
|
return [...handlers.values()]
|
|
23
42
|
.map((h) => ({ name: h.name, readOnly: h.readOnly, streaming: h.streaming }))
|
|
@@ -48,6 +67,8 @@ function settingsInit(s) {
|
|
|
48
67
|
function componentInit(c) {
|
|
49
68
|
const base = { kind: kindToProto(c.kind), id: c.id };
|
|
50
69
|
switch (c.kind) {
|
|
70
|
+
case "autonomous-agent":
|
|
71
|
+
return autonomousInit(c);
|
|
51
72
|
case "event-sourced":
|
|
52
73
|
return { ...base, handlers: handlerInits(c.handlers), detail: { case: "eventSourced", value: { snapshotEvery: c.snapshotEvery } } };
|
|
53
74
|
case "key-value":
|
|
@@ -82,19 +103,54 @@ function componentInit(c) {
|
|
|
82
103
|
};
|
|
83
104
|
}
|
|
84
105
|
}
|
|
106
|
+
function settingsOf(s) {
|
|
107
|
+
return {
|
|
108
|
+
...(s.approachingBudgetAt !== undefined ? { approachingBudgetAt: s.approachingBudgetAt } : {}),
|
|
109
|
+
...(s.repeatedFailureAt !== undefined ? { repeatedFailureAt: s.repeatedFailureAt } : {}),
|
|
110
|
+
...(s.maxConsecutiveFailures !== undefined ? { maxConsecutiveFailures: s.maxConsecutiveFailures } : {}),
|
|
111
|
+
...(s.dependencyStuckAfterMillis !== undefined ? { dependencyStuckAfterMillis: BigInt(s.dependencyStuckAfterMillis) } : {}),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function autonomousInit(c) {
|
|
115
|
+
const cls = c.cls;
|
|
116
|
+
return {
|
|
117
|
+
kind: kindToProto(c.kind),
|
|
118
|
+
id: c.id,
|
|
119
|
+
handlers: [],
|
|
120
|
+
detail: {
|
|
121
|
+
case: "autonomousAgent",
|
|
122
|
+
value: {
|
|
123
|
+
description: cls.description,
|
|
124
|
+
...(cls.instructions !== undefined ? { instructions: cls.instructions } : {}),
|
|
125
|
+
...(cls.model !== undefined ? { model: cls.model } : {}),
|
|
126
|
+
tools: [...c.tools.values()]
|
|
127
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
128
|
+
.map((t) => ({ name: t.name, description: t.description, inputSchemaJson: JSON.stringify(isCodec(t.input) ? { type: "object" } : toJsonSchema(t.input)) })),
|
|
129
|
+
guardrails: [...c.guardrails.keys()].sort(),
|
|
130
|
+
taskTypes: [...c.taskTypes.values()].map((t) => {
|
|
131
|
+
const schema = resultSchemaJson(t);
|
|
132
|
+
return { name: t.name, description: t.description, rules: t.rules.map((r) => r.name), ...(schema !== undefined ? { resultSchemaJson: schema } : {}) };
|
|
133
|
+
}),
|
|
134
|
+
accepts: cls.accepts.map((a) => ({ taskType: a.taskType.name, maxIterations: a.maxIterations })),
|
|
135
|
+
...(cls.settings ? { settings: settingsOf(cls.settings) } : {}),
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
85
140
|
export function renderSpec(registry) {
|
|
86
141
|
const components = [...registry.components.values()].sort((a, b) => a.id.localeCompare(b.id)).map(componentInit);
|
|
87
142
|
const endpoints = [...registry.endpoints.values()].map((e) => ({
|
|
88
143
|
id: e.id,
|
|
89
144
|
prefix: e.prefix,
|
|
90
145
|
acl: aclToProto(e.acl),
|
|
146
|
+
allowCallers: callersToProto(e.acl),
|
|
91
147
|
routes: [...e.routes.entries()].map(([id, r]) => ({
|
|
92
148
|
id,
|
|
93
149
|
method: r.method,
|
|
94
150
|
template: r.template,
|
|
95
151
|
hasBody: r.body !== undefined,
|
|
96
152
|
streaming: r.streaming,
|
|
97
|
-
...(r.acl !== undefined ? { acl: aclToProto(r.acl) } : {}),
|
|
153
|
+
...(r.acl !== undefined ? { acl: aclToProto(r.acl), allowCallers: callersToProto(r.acl) } : {}),
|
|
98
154
|
})),
|
|
99
155
|
}));
|
|
100
156
|
return create(SpecSchema, {
|
|
@@ -223,7 +223,11 @@ export class AnkkaTestKit {
|
|
|
223
223
|
...this.#env,
|
|
224
224
|
})
|
|
225
225
|
.withExposedPorts(HTTP_PORT, CALLBACK_PORT)
|
|
226
|
-
|
|
226
|
+
// Both ports, not only the health route: it answers as soon as HTTP is bound, which can be
|
|
227
|
+
// before the callback server listens, and Docker's port proxy accepts then resets a
|
|
228
|
+
// connection to a port nothing in the container listens on yet — so a handler's first call
|
|
229
|
+
// after a restart failed UNAVAILABLE. forListeningPorts checks from inside the container.
|
|
230
|
+
.withWaitStrategy(tc.Wait.forAll([tc.Wait.forHttp("/_ankka/health", HTTP_PORT).forStatusCode(200), tc.Wait.forListeningPorts()]))
|
|
227
231
|
.withStartupTimeout(this.#readyTimeoutMs)
|
|
228
232
|
.start();
|
|
229
233
|
this.#sidecar = container;
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.9.0";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Written by scripts/proto.ts from package.json; do not edit. 0.0.0 in the tree, the tag's version in a release.
|
|
2
|
-
export const VERSION = "0.
|
|
2
|
+
export const VERSION = "0.9.0";
|
package/package.json
CHANGED