@ai-ecoverse/slicc-agent 1.2.0 → 2.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/README.md +33 -6
- package/dist/client.js +64 -0
- package/dist/client.js.map +7 -0
- package/dist/control.js +69 -0
- package/dist/control.js.map +7 -0
- package/dist/host.js +82 -0
- package/dist/host.js.map +7 -0
- package/dist/index.js +26 -4
- package/dist/index.js.map +2 -2
- package/dist/owner.js +34 -0
- package/dist/owner.js.map +7 -0
- package/dist/services.js +12 -0
- package/dist/services.js.map +7 -0
- package/dist/spectrum/agent-port.js +156 -0
- package/dist/spectrum/agent-port.js.map +7 -0
- package/dist/spectrum/emitter.js +20 -0
- package/dist/spectrum/emitter.js.map +7 -0
- package/dist/spectrum/index.js +17 -0
- package/dist/spectrum/index.js.map +7 -0
- package/dist/spectrum/messages.js +161 -0
- package/dist/spectrum/messages.js.map +7 -0
- package/dist/types/client.d.ts +14 -0
- package/dist/types/control.d.ts +4 -0
- package/dist/types/host.d.ts +13 -0
- package/dist/types/index.d.ts +7 -1
- package/dist/types/owner.d.ts +21 -0
- package/dist/types/services.d.ts +59 -0
- package/dist/types/spectrum/agent-port.d.ts +32 -0
- package/dist/types/spectrum/emitter.d.ts +7 -0
- package/dist/types/spectrum/index.d.ts +7 -0
- package/dist/types/spectrum/messages.d.ts +37 -0
- package/dist/types/wire.d.ts +27 -0
- package/dist/types/worker.d.ts +5 -0
- package/dist/wire.js +164 -0
- package/dist/wire.js.map +7 -0
- package/dist/worker.js +14 -0
- package/dist/worker.js.map +7 -0
- package/package.json +12 -1
- package/dist/port.js +0 -57
- package/dist/port.js.map +0 -7
- package/dist/types/port.d.ts +0 -23
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
class Emitter {
|
|
2
|
+
#listeners = /* @__PURE__ */ new Map();
|
|
3
|
+
on(type, listener) {
|
|
4
|
+
let set = this.#listeners.get(type);
|
|
5
|
+
if (!set) {
|
|
6
|
+
set = /* @__PURE__ */ new Set();
|
|
7
|
+
this.#listeners.set(type, set);
|
|
8
|
+
}
|
|
9
|
+
set.add(listener);
|
|
10
|
+
return () => set.delete(listener);
|
|
11
|
+
}
|
|
12
|
+
emit(type, detail) {
|
|
13
|
+
for (const listener of [...this.#listeners.get(type) ?? []])
|
|
14
|
+
listener(detail);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
Emitter
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=emitter.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/spectrum/emitter.ts"],
|
|
4
|
+
"sourcesContent": ["type Listener<T> = (detail: T) => void;\n\nexport class Emitter<Events extends object> {\n readonly #listeners = new Map<keyof Events, Set<Listener<never>>>();\n\n on<K extends keyof Events>(type: K, listener: Listener<Events[K]>): () => void {\n let set = this.#listeners.get(type);\n if (!set) {\n set = new Set();\n this.#listeners.set(type, set);\n }\n set.add(listener as Listener<never>);\n return () => set.delete(listener as Listener<never>);\n }\n\n emit<K extends keyof Events>(type: K, detail: Events[K]): void {\n for (const listener of [...(this.#listeners.get(type) ?? [])])\n (listener as Listener<Events[K]>)(detail);\n }\n}\n"],
|
|
5
|
+
"mappings": "AAEO,MAAM,QAA+B;AAAA,EACjC,aAAa,oBAAI,IAAwC;AAAA,EAElE,GAA2B,MAAS,UAA2C;AAC7E,QAAI,MAAM,KAAK,WAAW,IAAI,IAAI;AAClC,QAAI,CAAC,KAAK;AACR,YAAM,oBAAI,IAAI;AACd,WAAK,WAAW,IAAI,MAAM,GAAG;AAAA,IAC/B;AACA,QAAI,IAAI,QAA2B;AACnC,WAAO,MAAM,IAAI,OAAO,QAA2B;AAAA,EACrD;AAAA,EAEA,KAA6B,MAAS,QAAyB;AAC7D,eAAW,YAAY,CAAC,GAAI,KAAK,WAAW,IAAI,IAAI,KAAK,CAAC,CAAE;AAC1D,MAAC,SAAiC,MAAM;AAAA,EAC5C;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { AgentAdapter } from "./agent-port.js";
|
|
2
|
+
import { AgentAdapter as AgentAdapter2, CONE } from "./agent-port.js";
|
|
3
|
+
import { assistant, isBusy, queued, textOf, toMessages } from "./messages.js";
|
|
4
|
+
function createAgentModel(connection) {
|
|
5
|
+
return { agent: new AgentAdapter(connection) };
|
|
6
|
+
}
|
|
7
|
+
export {
|
|
8
|
+
AgentAdapter2 as AgentAdapter,
|
|
9
|
+
CONE,
|
|
10
|
+
assistant,
|
|
11
|
+
createAgentModel,
|
|
12
|
+
isBusy,
|
|
13
|
+
queued,
|
|
14
|
+
textOf,
|
|
15
|
+
toMessages
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/spectrum/index.ts"],
|
|
4
|
+
"sourcesContent": ["import type { AgentConnection } from '../client.ts';\nimport { AgentAdapter } from './agent-port.ts';\n\nexport { AgentAdapter, CONE } from './agent-port.ts';\nexport { assistant, isBusy, queued, textOf, toMessages } from './messages.ts';\n\nexport function createAgentModel(connection: AgentConnection): { agent: AgentAdapter } {\n return { agent: new AgentAdapter(connection) };\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,oBAAoB;AAE7B,SAAS,gBAAAA,eAAc,YAAY;AACnC,SAAS,WAAW,QAAQ,QAAQ,QAAQ,kBAAkB;AAEvD,SAAS,iBAAiB,YAAsD;AACrF,SAAO,EAAE,OAAO,IAAI,aAAa,UAAU,EAAE;AAC/C;",
|
|
6
|
+
"names": ["AgentAdapter"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
function textOf(content) {
|
|
2
|
+
if (typeof content === "string") return content;
|
|
3
|
+
return (content ?? []).map((block) => block.type === "text" ? block.text ?? "" : "").join("");
|
|
4
|
+
}
|
|
5
|
+
const paths = (args) => typeof args?.path === "string" ? [args.path] : [];
|
|
6
|
+
function title(name, args) {
|
|
7
|
+
const subject = args?.command ?? args?.path;
|
|
8
|
+
return typeof subject === "string" ? `${name} ${subject}` : name;
|
|
9
|
+
}
|
|
10
|
+
function toolCall(block) {
|
|
11
|
+
return {
|
|
12
|
+
id: block.id ?? "",
|
|
13
|
+
name: block.name ?? "tool",
|
|
14
|
+
title: title(block.name ?? "tool", block.arguments),
|
|
15
|
+
input: JSON.stringify(block.arguments ?? {}),
|
|
16
|
+
output: "",
|
|
17
|
+
status: "running",
|
|
18
|
+
paths: paths(block.arguments)
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function part(block) {
|
|
22
|
+
if (block.type === "text") return { type: "text", text: block.text ?? "" };
|
|
23
|
+
if (block.type === "thinking") return { type: "thinking", text: block.thinking ?? "" };
|
|
24
|
+
if (block.type === "toolCall") return { type: "tool", tool: toolCall(block) };
|
|
25
|
+
return void 0;
|
|
26
|
+
}
|
|
27
|
+
function usage(message) {
|
|
28
|
+
const used = message.usage;
|
|
29
|
+
if (!used) return void 0;
|
|
30
|
+
return {
|
|
31
|
+
input: used.input ?? 0,
|
|
32
|
+
output: used.output ?? 0,
|
|
33
|
+
cost: used.cost?.total ?? 0,
|
|
34
|
+
cacheRead: used.cacheRead ?? 0,
|
|
35
|
+
cacheWrite: used.cacheWrite ?? 0
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const statuses = { error: "error", aborted: "stopped" };
|
|
39
|
+
function assistant(id, message, status) {
|
|
40
|
+
const blocks = Array.isArray(message.content) ? message.content : [];
|
|
41
|
+
const parts = blocks.map(part).filter((item) => item !== void 0);
|
|
42
|
+
if (message.errorMessage)
|
|
43
|
+
parts.push({ type: "error", message: message.errorMessage, action: "retry" });
|
|
44
|
+
const used = usage(message);
|
|
45
|
+
return {
|
|
46
|
+
id,
|
|
47
|
+
role: "assistant",
|
|
48
|
+
parts,
|
|
49
|
+
status: status ?? statuses[message.stopReason ?? ""] ?? "done",
|
|
50
|
+
createdAt: message.timestamp ?? 0,
|
|
51
|
+
...message.provider && message.model ? { model: `${message.provider}/${message.model}` } : {},
|
|
52
|
+
...used ? { usage: used } : {}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function user(id, message) {
|
|
56
|
+
return { id, role: "user", text: textOf(message.content), createdAt: message.timestamp ?? 0 };
|
|
57
|
+
}
|
|
58
|
+
function system(id, entry, text) {
|
|
59
|
+
const reason = entry.data?.reason;
|
|
60
|
+
return {
|
|
61
|
+
id,
|
|
62
|
+
role: "system",
|
|
63
|
+
kind: "compaction",
|
|
64
|
+
text,
|
|
65
|
+
createdAt: 0,
|
|
66
|
+
state: "summarized",
|
|
67
|
+
...reason ? { trigger: reason } : {}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function tools(messages) {
|
|
71
|
+
const found = /* @__PURE__ */ new Map();
|
|
72
|
+
for (const message of messages) {
|
|
73
|
+
if (message.role !== "assistant") continue;
|
|
74
|
+
for (const item of message.parts) if (item.type === "tool") found.set(item.tool.id, item.tool);
|
|
75
|
+
}
|
|
76
|
+
return found;
|
|
77
|
+
}
|
|
78
|
+
function settle(calls, result) {
|
|
79
|
+
const call = calls.get(result.toolCallId ?? "");
|
|
80
|
+
if (!call) return;
|
|
81
|
+
call.output = textOf(result.content);
|
|
82
|
+
call.status = result.isError ? "error" : "done";
|
|
83
|
+
}
|
|
84
|
+
function entryMessage(entry, out, calls) {
|
|
85
|
+
const id = `e${entry.id}`;
|
|
86
|
+
const model = entry.model?.[0];
|
|
87
|
+
if (entry.kind === "pi.user" && model) out.push(user(id, model));
|
|
88
|
+
else if (entry.kind === "pi.assistant" && model) {
|
|
89
|
+
const message = assistant(id, model);
|
|
90
|
+
out.push(message);
|
|
91
|
+
for (const [key, value] of tools([message])) calls.set(key, value);
|
|
92
|
+
} else if (entry.kind === "pi.tool-result" && model) settle(calls, model);
|
|
93
|
+
else if (entry.kind === "pi.compaction") out.push(system(id, entry, textOf(model?.content)));
|
|
94
|
+
}
|
|
95
|
+
const slotStatus = {
|
|
96
|
+
pending: "running",
|
|
97
|
+
running: "running",
|
|
98
|
+
done: "done"
|
|
99
|
+
};
|
|
100
|
+
function live(view, out, calls) {
|
|
101
|
+
const state = view.docs["pi.live"];
|
|
102
|
+
for (const slot of state?.tools ?? []) {
|
|
103
|
+
const call = calls.get(slot.callId);
|
|
104
|
+
if (call && call.status === "running") {
|
|
105
|
+
call.status = slotStatus[slot.status] ?? "running";
|
|
106
|
+
call.output = slot.output ?? call.output;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const partial = state?.generation?.message;
|
|
110
|
+
if (partial) out.push(assistant("live", partial, "streaming"));
|
|
111
|
+
for (const compaction of state?.compactions ?? []) {
|
|
112
|
+
out.push({
|
|
113
|
+
id: `compaction-${compaction.taskId}`,
|
|
114
|
+
role: "system",
|
|
115
|
+
kind: "compaction",
|
|
116
|
+
text: "",
|
|
117
|
+
createdAt: 0,
|
|
118
|
+
state: "summarizing",
|
|
119
|
+
trigger: compaction.reason
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
const retry = state?.generation?.retry;
|
|
123
|
+
if (retry)
|
|
124
|
+
out.push({
|
|
125
|
+
id: "retry",
|
|
126
|
+
role: "system",
|
|
127
|
+
kind: "notice",
|
|
128
|
+
text: retry.error,
|
|
129
|
+
createdAt: retry.at
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function toMessages(view) {
|
|
133
|
+
const out = [];
|
|
134
|
+
if (!view) return out;
|
|
135
|
+
const calls = /* @__PURE__ */ new Map();
|
|
136
|
+
for (const entry of view.entries) entryMessage(entry, out, calls);
|
|
137
|
+
live(view, out, calls);
|
|
138
|
+
return out;
|
|
139
|
+
}
|
|
140
|
+
function isBusy(view) {
|
|
141
|
+
return Boolean(view?.docs["pi.live"]?.run);
|
|
142
|
+
}
|
|
143
|
+
function queued(view) {
|
|
144
|
+
const items = view?.docs["pi.inbox"]?.items ?? [];
|
|
145
|
+
return items.filter((item) => item.mode !== "write").map((item) => ({
|
|
146
|
+
id: `q${item.id}`,
|
|
147
|
+
role: "user",
|
|
148
|
+
text: textOf(item.content),
|
|
149
|
+
createdAt: 0,
|
|
150
|
+
queued: true,
|
|
151
|
+
mode: item.mode === "steer" ? "steer" : "queue"
|
|
152
|
+
}));
|
|
153
|
+
}
|
|
154
|
+
export {
|
|
155
|
+
assistant,
|
|
156
|
+
isBusy,
|
|
157
|
+
queued,
|
|
158
|
+
textOf,
|
|
159
|
+
toMessages
|
|
160
|
+
};
|
|
161
|
+
//# sourceMappingURL=messages.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/spectrum/messages.ts"],
|
|
4
|
+
"sourcesContent": ["import type {\n AssistantMessage,\n AssistantStatus,\n Message,\n MessagePart,\n SystemMessage,\n ToolCall,\n ToolStatus,\n Usage,\n UserMessage,\n} from '@ai-ecoverse/slicc-spectrum/ui';\nimport type { ConversationView, EntryRecord } from '@earendil-works/pi-durable';\n\ntype Json = Record<string, unknown>;\n\ninterface Block {\n type: string;\n text?: string;\n thinking?: string;\n id?: string;\n name?: string;\n arguments?: Json;\n}\n\ninterface ModelMessage {\n role: string;\n content?: string | Block[];\n timestamp?: number;\n stopReason?: string;\n errorMessage?: string;\n provider?: string;\n model?: string;\n usage?: {\n input?: number;\n output?: number;\n cacheRead?: number;\n cacheWrite?: number;\n cost?: { total?: number };\n };\n toolCallId?: string;\n isError?: boolean;\n}\n\ninterface Slot {\n callId: string;\n status: string;\n output?: string;\n}\n\ninterface Live {\n run?: unknown;\n generation?: { message?: ModelMessage; retry?: { at: number; error: string } };\n tools?: Slot[];\n compactions?: { taskId: number; reason: string }[];\n}\n\nexport function textOf(content: ModelMessage['content']): string {\n if (typeof content === 'string') return content;\n return (content ?? []).map((block) => (block.type === 'text' ? (block.text ?? '') : '')).join('');\n}\n\nconst paths = (args: Json | undefined): string[] =>\n typeof args?.path === 'string' ? [args.path] : [];\n\nfunction title(name: string, args: Json | undefined): string {\n const subject = args?.command ?? args?.path;\n return typeof subject === 'string' ? `${name} ${subject}` : name;\n}\n\nfunction toolCall(block: Block): ToolCall {\n return {\n id: block.id ?? '',\n name: block.name ?? 'tool',\n title: title(block.name ?? 'tool', block.arguments),\n input: JSON.stringify(block.arguments ?? {}),\n output: '',\n status: 'running',\n paths: paths(block.arguments),\n };\n}\n\nfunction part(block: Block): MessagePart | undefined {\n if (block.type === 'text') return { type: 'text', text: block.text ?? '' };\n if (block.type === 'thinking') return { type: 'thinking', text: block.thinking ?? '' };\n if (block.type === 'toolCall') return { type: 'tool', tool: toolCall(block) };\n return undefined;\n}\n\nfunction usage(message: ModelMessage): Usage | undefined {\n const used = message.usage;\n if (!used) return undefined;\n return {\n input: used.input ?? 0,\n output: used.output ?? 0,\n cost: used.cost?.total ?? 0,\n cacheRead: used.cacheRead ?? 0,\n cacheWrite: used.cacheWrite ?? 0,\n };\n}\n\nconst statuses: Record<string, AssistantStatus> = { error: 'error', aborted: 'stopped' };\n\nexport function assistant(\n id: string,\n message: ModelMessage,\n status?: AssistantStatus\n): AssistantMessage {\n const blocks = Array.isArray(message.content) ? message.content : [];\n const parts = blocks.map(part).filter((item): item is MessagePart => item !== undefined);\n if (message.errorMessage)\n parts.push({ type: 'error', message: message.errorMessage, action: 'retry' });\n const used = usage(message);\n return {\n id,\n role: 'assistant',\n parts,\n status: status ?? statuses[message.stopReason ?? ''] ?? 'done',\n createdAt: message.timestamp ?? 0,\n ...(message.provider && message.model ? { model: `${message.provider}/${message.model}` } : {}),\n ...(used ? { usage: used } : {}),\n };\n}\n\nfunction user(id: string, message: ModelMessage): UserMessage {\n return { id, role: 'user', text: textOf(message.content), createdAt: message.timestamp ?? 0 };\n}\n\nfunction system(id: string, entry: EntryRecord, text: string): SystemMessage {\n const reason = (entry.data as Json | undefined)?.reason as SystemMessage['trigger'] | undefined;\n return {\n id,\n role: 'system',\n kind: 'compaction',\n text,\n createdAt: 0,\n state: 'summarized',\n ...(reason ? { trigger: reason } : {}),\n };\n}\n\nfunction tools(messages: Message[]): Map<string, ToolCall> {\n const found = new Map<string, ToolCall>();\n for (const message of messages) {\n if (message.role !== 'assistant') continue;\n for (const item of message.parts) if (item.type === 'tool') found.set(item.tool.id, item.tool);\n }\n return found;\n}\n\nfunction settle(calls: Map<string, ToolCall>, result: ModelMessage): void {\n const call = calls.get(result.toolCallId ?? '');\n if (!call) return;\n call.output = textOf(result.content);\n call.status = result.isError ? 'error' : 'done';\n}\n\nfunction entryMessage(entry: EntryRecord, out: Message[], calls: Map<string, ToolCall>): void {\n const id = `e${entry.id}`;\n const model = entry.model?.[0] as ModelMessage | undefined;\n if (entry.kind === 'pi.user' && model) out.push(user(id, model));\n else if (entry.kind === 'pi.assistant' && model) {\n const message = assistant(id, model);\n out.push(message);\n for (const [key, value] of tools([message])) calls.set(key, value);\n } else if (entry.kind === 'pi.tool-result' && model) settle(calls, model);\n else if (entry.kind === 'pi.compaction') out.push(system(id, entry, textOf(model?.content)));\n}\n\nconst slotStatus: Record<string, ToolStatus> = {\n pending: 'running',\n running: 'running',\n done: 'done',\n};\n\nfunction live(view: ConversationView, out: Message[], calls: Map<string, ToolCall>): void {\n const state = view.docs['pi.live'] as Live | undefined;\n for (const slot of state?.tools ?? []) {\n const call = calls.get(slot.callId);\n if (call && call.status === 'running') {\n call.status = slotStatus[slot.status] ?? 'running';\n call.output = slot.output ?? call.output;\n }\n }\n const partial = state?.generation?.message;\n if (partial) out.push(assistant('live', partial, 'streaming'));\n for (const compaction of state?.compactions ?? []) {\n out.push({\n id: `compaction-${compaction.taskId}`,\n role: 'system',\n kind: 'compaction',\n text: '',\n createdAt: 0,\n state: 'summarizing',\n trigger: compaction.reason as SystemMessage['trigger'],\n });\n }\n const retry = state?.generation?.retry;\n if (retry)\n out.push({\n id: 'retry',\n role: 'system',\n kind: 'notice',\n text: retry.error,\n createdAt: retry.at,\n });\n}\n\nexport function toMessages(view: ConversationView | undefined): Message[] {\n const out: Message[] = [];\n if (!view) return out;\n const calls = new Map<string, ToolCall>();\n for (const entry of view.entries) entryMessage(entry, out, calls);\n live(view, out, calls);\n return out;\n}\n\nexport function isBusy(view: ConversationView | undefined): boolean {\n return Boolean((view?.docs['pi.live'] as Live | undefined)?.run);\n}\n\nexport function queued(view: ConversationView | undefined): UserMessage[] {\n const items = ((view?.docs['pi.inbox'] as { items?: Json[] } | undefined)?.items ?? []) as {\n id: number;\n mode: string;\n content?: ModelMessage['content'];\n }[];\n return items\n .filter((item) => item.mode !== 'write')\n .map((item) => ({\n id: `q${item.id}`,\n role: 'user',\n text: textOf(item.content),\n createdAt: 0,\n queued: true,\n mode: item.mode === 'steer' ? 'steer' : 'queue',\n }));\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Context, type ReplicatedState } from '@earendil-works/chord';
|
|
2
|
+
import type { ConversationView } from '@earendil-works/pi-durable';
|
|
3
|
+
import { AgentControl, type SendMode } from './services.ts';
|
|
4
|
+
import { type PortEndpoint } from './wire.ts';
|
|
5
|
+
export interface AgentConnection {
|
|
6
|
+
readonly serverId: string;
|
|
7
|
+
readonly control: AgentControl;
|
|
8
|
+
readonly transcript: ReplicatedState<ConversationView>;
|
|
9
|
+
prompt(text: string, whenBusy?: SendMode): Promise<string>;
|
|
10
|
+
close(): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
export declare function connectAgent(endpoint: PortEndpoint, options?: {
|
|
13
|
+
context?: Context;
|
|
14
|
+
}): Promise<AgentConnection>;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type Conversation, type Harness, type SubmissionId } from '@earendil-works/pi-durable';
|
|
2
|
+
import type { AgentControl } from './services.ts';
|
|
3
|
+
export declare function submissionId(value: string): SubmissionId | undefined;
|
|
4
|
+
export declare function createAgentControl(harness: Harness, conversation: Conversation): AgentControl;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type Context } from '@earendil-works/chord';
|
|
2
|
+
import type { Agent } from './agent.ts';
|
|
3
|
+
import { type PortEndpoint } from './wire.ts';
|
|
4
|
+
export interface AgentHost {
|
|
5
|
+
readonly serverId: string;
|
|
6
|
+
readonly agent: Agent;
|
|
7
|
+
connect(endpoint: PortEndpoint): void;
|
|
8
|
+
close(): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
export declare function hostAgent(agent: Agent, options?: {
|
|
11
|
+
serverId?: string;
|
|
12
|
+
context?: Context;
|
|
13
|
+
}): Promise<AgentHost>;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
export { type Agent, type AgentOptions, answerText, openAgent } from './agent.ts';
|
|
2
|
-
export { type
|
|
2
|
+
export { type AgentConnection, connectAgent } from './client.ts';
|
|
3
|
+
export { createAgentControl } from './control.ts';
|
|
4
|
+
export { type AgentHost, hostAgent } from './host.ts';
|
|
5
|
+
export { AGENT_LOCK, type AgentOwner, type AgentWorker, type OwnerOptions, startAgent, } from './owner.ts';
|
|
6
|
+
export { AGENT_SESSION, AgentControl, AgentSessions, type AgentSettingsChange, AgentTranscript, type OperationError, type PromptResult, type SendMode, type SendRequest, type SendResponse, } from './services.ts';
|
|
3
7
|
export { AGENT_DIRECTORY, type OpfsSqliteOptions, openMemorySqliteStorage, openOpfsSqliteStorage, WasmSqliteDatabase, } from './sqlite.ts';
|
|
8
|
+
export { PortChannel, type PortEndpoint, PortListener, withUnrefTimers } from './wire.ts';
|
|
9
|
+
export { serveConnections, type WorkerScope } from './worker.ts';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type AgentConnection } from './client.ts';
|
|
2
|
+
import type { PortEndpoint } from './wire.ts';
|
|
3
|
+
export declare const AGENT_LOCK = "slicc-agent";
|
|
4
|
+
export interface AgentWorker {
|
|
5
|
+
postMessage(message: unknown, transfer: Transferable[]): void;
|
|
6
|
+
terminate(): void;
|
|
7
|
+
}
|
|
8
|
+
export interface AgentOwner {
|
|
9
|
+
connect(): Promise<AgentConnection>;
|
|
10
|
+
restart(): void;
|
|
11
|
+
release(): void;
|
|
12
|
+
}
|
|
13
|
+
export interface OwnerOptions {
|
|
14
|
+
worker: () => AgentWorker;
|
|
15
|
+
locks?: LockManager;
|
|
16
|
+
channel?: () => {
|
|
17
|
+
port1: PortEndpoint;
|
|
18
|
+
port2: Transferable;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export declare function startAgent(options: OwnerOptions): Promise<AgentOwner>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type Context, type ReplicatedState } from '@earendil-works/chord';
|
|
2
|
+
import type { ConversationView } from '@earendil-works/pi-durable';
|
|
3
|
+
export type SendMode = 'steer' | 'followUp' | 'reject';
|
|
4
|
+
export interface SendRequest {
|
|
5
|
+
text: string;
|
|
6
|
+
whenBusy: SendMode;
|
|
7
|
+
requestId: string | null;
|
|
8
|
+
}
|
|
9
|
+
export interface OperationError {
|
|
10
|
+
code: string;
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
export type SendResponse = {
|
|
14
|
+
accepted: true;
|
|
15
|
+
submissionId: string;
|
|
16
|
+
error: null;
|
|
17
|
+
} | {
|
|
18
|
+
accepted: false;
|
|
19
|
+
submissionId: null;
|
|
20
|
+
error: OperationError;
|
|
21
|
+
};
|
|
22
|
+
export type PromptResult = {
|
|
23
|
+
status: 'done';
|
|
24
|
+
text: string;
|
|
25
|
+
reason: null;
|
|
26
|
+
} | {
|
|
27
|
+
status: 'unanswered';
|
|
28
|
+
text: null;
|
|
29
|
+
reason: string;
|
|
30
|
+
};
|
|
31
|
+
export interface AgentControl {
|
|
32
|
+
send(request: SendRequest, context: Context): Promise<SendResponse>;
|
|
33
|
+
wait(submissionId: string, context: Context): Promise<PromptResult>;
|
|
34
|
+
withdraw(submissionId: string, context: Context): Promise<{
|
|
35
|
+
outcome: 'withdrawn' | 'already_placed' | 'not_found';
|
|
36
|
+
}>;
|
|
37
|
+
abort(context: Context): Promise<void>;
|
|
38
|
+
compact(instructions: string | null, context: Context): Promise<SendResponse>;
|
|
39
|
+
reset(handoff: string | null, context: Context): Promise<void>;
|
|
40
|
+
configure(change: AgentSettingsChange, context: Context): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
export interface AgentSettingsChange {
|
|
43
|
+
model: {
|
|
44
|
+
provider: string;
|
|
45
|
+
modelId: string;
|
|
46
|
+
} | null;
|
|
47
|
+
thinkingLevel: 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | null;
|
|
48
|
+
}
|
|
49
|
+
export declare const AgentControl: import("@earendil-works/chord").Service<AgentControl>;
|
|
50
|
+
export interface AgentTranscript {
|
|
51
|
+
readonly state: ReplicatedState<ConversationView>;
|
|
52
|
+
}
|
|
53
|
+
export declare const AgentTranscript: import("@earendil-works/chord").Service<AgentTranscript>;
|
|
54
|
+
export interface AgentSessions {
|
|
55
|
+
attach(sessionId: string, context: Context): Promise<void>;
|
|
56
|
+
detach(context: Context): Promise<void>;
|
|
57
|
+
}
|
|
58
|
+
export declare const AgentSessions: import("@earendil-works/chord").Service<AgentSessions>;
|
|
59
|
+
export declare const AGENT_SESSION = "agent";
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Agent, AgentEvents, AgentPort, FrozenCone, Message, Outgoing, SlashCommand, Thinking, UserMessage } from '@ai-ecoverse/slicc-spectrum/ui';
|
|
2
|
+
import type { AgentConnection } from '../client.ts';
|
|
3
|
+
import { Emitter } from './emitter.ts';
|
|
4
|
+
export declare const CONE = "cone";
|
|
5
|
+
export declare class AgentAdapter extends Emitter<AgentEvents> implements AgentPort {
|
|
6
|
+
#private;
|
|
7
|
+
constructor(connection: AgentConnection);
|
|
8
|
+
ready(): Promise<void>;
|
|
9
|
+
list(): readonly Agent[];
|
|
10
|
+
active(): string;
|
|
11
|
+
select(): void;
|
|
12
|
+
messages(): readonly Message[];
|
|
13
|
+
send(_agentId: string, input: string | Outgoing): Promise<void>;
|
|
14
|
+
stop(): void;
|
|
15
|
+
busy(): boolean;
|
|
16
|
+
queue(): readonly UserMessage[];
|
|
17
|
+
unqueue(_agentId: string, messageId: string): void;
|
|
18
|
+
suggestion(): string | null;
|
|
19
|
+
answer(): void;
|
|
20
|
+
resolveLick(): void;
|
|
21
|
+
compact(): void;
|
|
22
|
+
clear(): void;
|
|
23
|
+
setModel(_agentId: string, model: string): void;
|
|
24
|
+
setThinking(_agentId: string, level: Thinking): void;
|
|
25
|
+
older(): Promise<readonly Message[]>;
|
|
26
|
+
commands(): readonly SlashCommand[];
|
|
27
|
+
createScoop(): Agent;
|
|
28
|
+
frozen(): readonly FrozenCone[];
|
|
29
|
+
freeze(): void;
|
|
30
|
+
thaw(): Agent | null;
|
|
31
|
+
discard(): void;
|
|
32
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
type Listener<T> = (detail: T) => void;
|
|
2
|
+
export declare class Emitter<Events extends object> {
|
|
3
|
+
#private;
|
|
4
|
+
on<K extends keyof Events>(type: K, listener: Listener<Events[K]>): () => void;
|
|
5
|
+
emit<K extends keyof Events>(type: K, detail: Events[K]): void;
|
|
6
|
+
}
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AgentConnection } from '../client.ts';
|
|
2
|
+
import { AgentAdapter } from './agent-port.ts';
|
|
3
|
+
export { AgentAdapter, CONE } from './agent-port.ts';
|
|
4
|
+
export { assistant, isBusy, queued, textOf, toMessages } from './messages.ts';
|
|
5
|
+
export declare function createAgentModel(connection: AgentConnection): {
|
|
6
|
+
agent: AgentAdapter;
|
|
7
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { AssistantMessage, AssistantStatus, Message, UserMessage } from '@ai-ecoverse/slicc-spectrum/ui';
|
|
2
|
+
import type { ConversationView } from '@earendil-works/pi-durable';
|
|
3
|
+
type Json = Record<string, unknown>;
|
|
4
|
+
interface Block {
|
|
5
|
+
type: string;
|
|
6
|
+
text?: string;
|
|
7
|
+
thinking?: string;
|
|
8
|
+
id?: string;
|
|
9
|
+
name?: string;
|
|
10
|
+
arguments?: Json;
|
|
11
|
+
}
|
|
12
|
+
interface ModelMessage {
|
|
13
|
+
role: string;
|
|
14
|
+
content?: string | Block[];
|
|
15
|
+
timestamp?: number;
|
|
16
|
+
stopReason?: string;
|
|
17
|
+
errorMessage?: string;
|
|
18
|
+
provider?: string;
|
|
19
|
+
model?: string;
|
|
20
|
+
usage?: {
|
|
21
|
+
input?: number;
|
|
22
|
+
output?: number;
|
|
23
|
+
cacheRead?: number;
|
|
24
|
+
cacheWrite?: number;
|
|
25
|
+
cost?: {
|
|
26
|
+
total?: number;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
toolCallId?: string;
|
|
30
|
+
isError?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export declare function textOf(content: ModelMessage['content']): string;
|
|
33
|
+
export declare function assistant(id: string, message: ModelMessage, status?: AssistantStatus): AssistantMessage;
|
|
34
|
+
export declare function toMessages(view: ConversationView | undefined): Message[];
|
|
35
|
+
export declare function isBusy(view: ConversationView | undefined): boolean;
|
|
36
|
+
export declare function queued(view: ConversationView | undefined): UserMessage[];
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ByteTransportFactory } from '@earendil-works/pi-client';
|
|
2
|
+
import type { ServerListener } from '@earendil-works/pi-server';
|
|
3
|
+
type ByteConnectionAcceptor = Parameters<ServerListener['start']>[0];
|
|
4
|
+
export interface PortEndpoint {
|
|
5
|
+
postMessage(message: unknown, transfer?: Transferable[]): void;
|
|
6
|
+
addEventListener(type: 'message', listener: (event: MessageEvent) => void): void;
|
|
7
|
+
addEventListener(type: 'close' | 'messageerror' | 'error', listener: (event: Event) => void): void;
|
|
8
|
+
start?(): void;
|
|
9
|
+
close?(): void;
|
|
10
|
+
}
|
|
11
|
+
export declare function withUnrefTimers<T>(run: () => T, scope?: {
|
|
12
|
+
setTimeout: typeof setTimeout;
|
|
13
|
+
}): T;
|
|
14
|
+
export declare class PortListener implements ServerListener {
|
|
15
|
+
#private;
|
|
16
|
+
constructor(serverId: string);
|
|
17
|
+
start(accept: ByteConnectionAcceptor): Promise<void>;
|
|
18
|
+
close(): Promise<void>;
|
|
19
|
+
connect(endpoint: PortEndpoint): void;
|
|
20
|
+
}
|
|
21
|
+
export declare class PortChannel {
|
|
22
|
+
#private;
|
|
23
|
+
readonly hello: Promise<string>;
|
|
24
|
+
constructor(endpoint: PortEndpoint);
|
|
25
|
+
readonly transport: ByteTransportFactory;
|
|
26
|
+
}
|
|
27
|
+
export {};
|
package/dist/wire.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
function post(endpoint, chunk) {
|
|
2
|
+
const copy = chunk.slice();
|
|
3
|
+
endpoint.postMessage(copy, [copy.buffer]);
|
|
4
|
+
}
|
|
5
|
+
function unrefable(id) {
|
|
6
|
+
return {
|
|
7
|
+
unref() {
|
|
8
|
+
return this;
|
|
9
|
+
},
|
|
10
|
+
ref() {
|
|
11
|
+
return this;
|
|
12
|
+
},
|
|
13
|
+
[Symbol.toPrimitive]: () => id
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function withUnrefTimers(run, scope = globalThis) {
|
|
17
|
+
const original = scope.setTimeout;
|
|
18
|
+
scope.setTimeout = ((...args) => {
|
|
19
|
+
const id = original(...args);
|
|
20
|
+
return typeof id === "number" ? unrefable(id) : id;
|
|
21
|
+
});
|
|
22
|
+
try {
|
|
23
|
+
return run();
|
|
24
|
+
} finally {
|
|
25
|
+
scope.setTimeout = original;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
class PortConnection {
|
|
29
|
+
closed = false;
|
|
30
|
+
#endpoint;
|
|
31
|
+
#ended;
|
|
32
|
+
constructor(endpoint, ended) {
|
|
33
|
+
this.#endpoint = endpoint;
|
|
34
|
+
this.#ended = ended;
|
|
35
|
+
}
|
|
36
|
+
async send(chunk) {
|
|
37
|
+
if (this.closed) throw new Error("connection is closed");
|
|
38
|
+
post(this.#endpoint, chunk);
|
|
39
|
+
}
|
|
40
|
+
close(finalChunk) {
|
|
41
|
+
if (this.closed) return;
|
|
42
|
+
if (finalChunk) post(this.#endpoint, finalChunk);
|
|
43
|
+
this.closed = true;
|
|
44
|
+
this.#endpoint.postMessage({ close: true });
|
|
45
|
+
this.#endpoint.close?.();
|
|
46
|
+
this.#ended();
|
|
47
|
+
}
|
|
48
|
+
end() {
|
|
49
|
+
this.closed = true;
|
|
50
|
+
this.#ended();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
class PortListener {
|
|
54
|
+
#serverId;
|
|
55
|
+
#connections = /* @__PURE__ */ new Set();
|
|
56
|
+
#accept;
|
|
57
|
+
constructor(serverId) {
|
|
58
|
+
this.#serverId = serverId;
|
|
59
|
+
}
|
|
60
|
+
async start(accept) {
|
|
61
|
+
this.#accept = accept;
|
|
62
|
+
}
|
|
63
|
+
async close() {
|
|
64
|
+
for (const connection of [...this.#connections]) connection.close();
|
|
65
|
+
this.#accept = void 0;
|
|
66
|
+
}
|
|
67
|
+
connect(endpoint) {
|
|
68
|
+
const accept = this.#accept;
|
|
69
|
+
if (!accept) throw new Error("agent host is not listening");
|
|
70
|
+
const connection = new PortConnection(endpoint, () => this.#connections.delete(connection));
|
|
71
|
+
this.#connections.add(connection);
|
|
72
|
+
const handler = withUnrefTimers(() => accept(connection));
|
|
73
|
+
endpoint.addEventListener("message", ({ data }) => {
|
|
74
|
+
if (data instanceof Uint8Array) handler.onData(data);
|
|
75
|
+
else if (data?.close) {
|
|
76
|
+
connection.end();
|
|
77
|
+
handler.onClose();
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
const failed = (event) => {
|
|
81
|
+
connection.end();
|
|
82
|
+
handler.onError(new Error(`agent port ${event.type}`));
|
|
83
|
+
};
|
|
84
|
+
endpoint.addEventListener("messageerror", failed);
|
|
85
|
+
endpoint.addEventListener("close", () => {
|
|
86
|
+
if (connection.closed) return;
|
|
87
|
+
connection.end();
|
|
88
|
+
handler.onClose();
|
|
89
|
+
});
|
|
90
|
+
endpoint.start?.();
|
|
91
|
+
endpoint.postMessage({ hello: this.#serverId });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
class PortChannel {
|
|
95
|
+
hello;
|
|
96
|
+
#endpoint;
|
|
97
|
+
#queue = [];
|
|
98
|
+
#handlers;
|
|
99
|
+
#ended;
|
|
100
|
+
#greet;
|
|
101
|
+
#refuse;
|
|
102
|
+
constructor(endpoint) {
|
|
103
|
+
this.#endpoint = endpoint;
|
|
104
|
+
this.hello = new Promise((greet, refuse) => {
|
|
105
|
+
this.#greet = greet;
|
|
106
|
+
this.#refuse = refuse;
|
|
107
|
+
});
|
|
108
|
+
endpoint.addEventListener("message", ({ data }) => this.#receive(data));
|
|
109
|
+
endpoint.addEventListener(
|
|
110
|
+
"messageerror",
|
|
111
|
+
(event) => this.#end(new Error(`agent port ${event.type}`))
|
|
112
|
+
);
|
|
113
|
+
endpoint.addEventListener("error", (event) => this.#end(new Error(`agent port ${event.type}`)));
|
|
114
|
+
endpoint.addEventListener("close", () => this.#end("close"));
|
|
115
|
+
endpoint.start?.();
|
|
116
|
+
}
|
|
117
|
+
transport = (handlers) => {
|
|
118
|
+
this.#handlers = handlers;
|
|
119
|
+
for (const chunk of this.#queue.splice(0)) handlers.onData(chunk);
|
|
120
|
+
if (this.#ended) this.#deliver(this.#ended);
|
|
121
|
+
return {
|
|
122
|
+
send: async (chunk) => {
|
|
123
|
+
if (this.#ended) throw new Error("agent port is closed");
|
|
124
|
+
post(this.#endpoint, chunk);
|
|
125
|
+
},
|
|
126
|
+
close: () => {
|
|
127
|
+
if (this.#ended) return;
|
|
128
|
+
this.#ended = "close";
|
|
129
|
+
this.#refuse(new Error("agent port closed before hello"));
|
|
130
|
+
this.#endpoint.postMessage({ close: true });
|
|
131
|
+
this.#endpoint.close?.();
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
#receive(frame) {
|
|
136
|
+
if (frame instanceof Uint8Array) {
|
|
137
|
+
if (this.#handlers) this.#handlers.onData(frame);
|
|
138
|
+
else this.#queue.push(frame);
|
|
139
|
+
} else if ("hello" in frame) {
|
|
140
|
+
this.#greet(frame.hello);
|
|
141
|
+
} else {
|
|
142
|
+
this.#end("close");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
#end(reason) {
|
|
146
|
+
if (this.#ended) return;
|
|
147
|
+
this.#ended = reason;
|
|
148
|
+
this.#refuse(reason === "close" ? new Error("agent port closed before hello") : reason);
|
|
149
|
+
this.#deliver(reason);
|
|
150
|
+
}
|
|
151
|
+
#deliver(reason) {
|
|
152
|
+
const handlers = this.#handlers;
|
|
153
|
+
if (!handlers) return;
|
|
154
|
+
this.#handlers = void 0;
|
|
155
|
+
if (reason === "close") handlers.onClose();
|
|
156
|
+
else handlers.onError(reason);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
export {
|
|
160
|
+
PortChannel,
|
|
161
|
+
PortListener,
|
|
162
|
+
withUnrefTimers
|
|
163
|
+
};
|
|
164
|
+
//# sourceMappingURL=wire.js.map
|