@ai-ecoverse/slicc-agent 3.1.1 → 3.3.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 +11 -1
- package/dist/agent.js +14 -1
- package/dist/agent.js.map +2 -2
- package/dist/cone.js +25 -0
- package/dist/cone.js.map +7 -0
- package/dist/control.js +83 -18
- package/dist/control.js.map +2 -2
- package/dist/host.js +26 -3
- package/dist/host.js.map +2 -2
- package/dist/services.js.map +2 -2
- package/dist/spectrum/agent-port.js +4 -0
- package/dist/spectrum/agent-port.js.map +2 -2
- package/dist/spectrum/messages.js +85 -2
- package/dist/spectrum/messages.js.map +2 -2
- package/dist/types/agent.d.ts +3 -1
- package/dist/types/cone.d.ts +11 -0
- package/dist/types/control.d.ts +2 -1
- package/dist/types/services.d.ts +10 -0
- package/dist/types/spectrum/agent-port.d.ts +1 -0
- package/dist/types/spectrum/messages.d.ts +5 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -104,7 +104,17 @@ app.model = { ...createKernelModel({ kernel, root }), ...createAgentModel(agent,
|
|
|
104
104
|
|
|
105
105
|
`createAgentModel()` returns the `agent` and `settings` ports. Settings keeps the UI preferences in `storage`; model and thinking belong to the conversation, and accounts and models come from the worker.
|
|
106
106
|
|
|
107
|
-
The adapter keeps the replicated transcript and maps it on every update. User, assistant and compaction entries become messages; tool results fill in the tool calls they answer; the generation in flight shows as a streaming message; retries and running compactions show as system messages. A failed generation ends in an error card whose action fits the error: credential errors (an invalid or missing key, 401/403, not authorized) open settings, a model the account can't use offers the model picker, and anything else offers Retry.
|
|
107
|
+
The adapter keeps the replicated transcript and maps it on every update. User, assistant and compaction entries become messages; tool results fill in the tool calls they answer; the generation in flight shows as a streaming message; retries and running compactions show as system messages. A failed generation ends in an error card whose action fits the error: credential errors (an invalid or missing key, 401/403, not authorized) open settings, a model the account can't use offers the model picker, and anything else offers Retry. A content-filter stop is different: every later request resends the content that tripped the filter, so Retry, and any new message, would be stopped again. Content-filter stops are Bedrock's `content_filtered` and `guardrail_intervened`, Anthropic's `sensitive` and refusals, and OpenAI's `content_filter`. Their card leads with "The model's content filter stopped this reply." and offers **Drop the last turn** (spectrum ≥ 1.12.0). `AgentControl.rewind(messageId)` finds the user turn before that message and forks the cone's conversation at the entry before it (`Conversation.fork`, so `pi.agent` comes back as it was there). It points the cone at the fork and returns the turn's prompt, which spectrum puts back into the composer. Forking the very first turn creates a fresh conversation with the same model and thinking level. The filtered branch stays in storage, off the active path. The fork starts with a `slicc.rewound` entry that shows as a "Rewound 1 turn" notice, and clicking the card of an earlier filtered turn rewinds that turn.
|
|
108
|
+
|
|
109
|
+
The cone's active conversation is the session document `slicc.cone` (the root until the first rewind). The host's transcript follows it across forks. Rewinding is refused while the cone is running or has queued input, because a fork starts with an empty inbox. A background compaction still running on the old branch finishes there. A fork also starts with fresh `pi.usage`. Scoops don't exist yet. When they do, a fork won't carry tasks or their documents along, so rewinding past a scoop's creation will need its own rule. Credential and model errors lead with a plain sentence ("Bedrock rejected the API key.", "This model isn't available with the current account.") and keep the provider's raw error as `detail`, which spectrum (≥ 1.11.0) shows underneath. `busy()` and `queue()` read durable's live and inbox documents. Steering is the default (#4): a send or a steer goes out with `whenBusy: 'steer'`, so while a run is going it joins after the current tool round, and a `queue` send is a follow-up. User messages carry `delivered` from the host's deliveries; entries the host didn't record (from before a reload) count as steered when they follow a tool result. There is one cone so far; scoops, the freezer, licks and questions come later. slicc-spectrum is an optional peer dependency (≥ 1.13.0), used for types only, so package managers don't install it next to the agent.
|
|
110
|
+
|
|
111
|
+
Each tool call shows its input in the way that suits it:
|
|
112
|
+
- `bash`: its command, with a timeout as the card's `meta`;
|
|
113
|
+
- `read`: the path, with its line range as `meta`;
|
|
114
|
+
- `write`: the path;
|
|
115
|
+
- `edit`: the path, and its change as a `diff` (spectrum ≥ 1.13.0). While the edit runs, the diff comes from its `oldText`/`newText` pairs. Once done, it comes from the hunks of the unified `patch` that pi's edit tool returns in its result details, with context lines;
|
|
116
|
+
- other tools: their arguments as pretty-printed JSON.
|
|
117
|
+
|
|
108
118
|
|
|
109
119
|
## Patched dependencies
|
|
110
120
|
|
package/dist/agent.js
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
Harness,
|
|
6
6
|
MemoryStorage
|
|
7
7
|
} from "@earendil-works/pi-durable";
|
|
8
|
+
import { activeCone, pointCone } from "./cone.js";
|
|
8
9
|
function answerText(content) {
|
|
9
10
|
if (typeof content === "string") return content;
|
|
10
11
|
if (!Array.isArray(content)) return "";
|
|
@@ -31,11 +32,23 @@ async function openAgent(options) {
|
|
|
31
32
|
context
|
|
32
33
|
);
|
|
33
34
|
const root = await harness.root(context, { agent: { model: options.model } });
|
|
35
|
+
let cone = await activeCone(harness, root, context);
|
|
36
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
34
37
|
harness.resume();
|
|
35
38
|
return {
|
|
36
39
|
harness,
|
|
37
40
|
root,
|
|
38
|
-
|
|
41
|
+
cone: () => cone,
|
|
42
|
+
async switchCone(conversation, switching) {
|
|
43
|
+
await pointCone(harness, conversation, switching);
|
|
44
|
+
cone = conversation;
|
|
45
|
+
await Promise.all([...listeners].map((listener) => listener(conversation)));
|
|
46
|
+
},
|
|
47
|
+
onCone(listener) {
|
|
48
|
+
listeners.add(listener);
|
|
49
|
+
return () => listeners.delete(listener);
|
|
50
|
+
},
|
|
51
|
+
prompt: (text) => prompt(cone, text, context),
|
|
39
52
|
close: () => harness.close(context)
|
|
40
53
|
};
|
|
41
54
|
}
|
package/dist/agent.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/agent.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { Models } from '@earendil-works/pi-ai/models';\nimport {\n AssistantEntry,\n type Conversation,\n createRegistry,\n type EntryId,\n Harness,\n type HarnessOptions,\n MemoryStorage,\n type ModelRef,\n type Registry,\n type Storage,\n} from '@earendil-works/pi-durable';\n\nexport interface AgentOptions {\n models: Models;\n model: ModelRef;\n storage?: Storage;\n registry?: Registry;\n settings?: HarnessOptions['settings'];\n env?: HarnessOptions['env'];\n context?: Context;\n}\n\nexport interface Agent {\n readonly harness: Harness;\n readonly root: Conversation;\n prompt(text: string): Promise<string>;\n close(): Promise<void>;\n}\n\ninterface Block {\n type: string;\n text?: string;\n}\n\nexport function answerText(content: unknown): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n return (content as Block[])\n .filter((block) => block.type === 'text')\n .map((block) => block.text ?? '')\n .join('');\n}\n\nasync function prompt(root: Conversation, text: string, context: Context): Promise<string> {\n const submission = await root.submit({ type: 'input', content: text }, context);\n const settled = await submission.wait(context);\n const { answer, reason } = settled as { answer?: EntryId; reason?: string };\n if (answer === undefined) throw new Error(`unanswered: ${reason}`);\n const entry = await root.commit((tx) => tx.entry(AssistantEntry, answer), context);\n return answerText(entry?.model?.[0]?.content);\n}\n\nexport async function openAgent(options: AgentOptions): Promise<Agent> {\n const context = options.context ?? BACKGROUND_CONTEXT;\n const harness = await Harness.open(\n options.storage ?? new MemoryStorage(),\n {\n models: options.models,\n registry: options.registry ?? createRegistry(),\n ...(options.settings ? { settings: options.settings } : {}),\n ...(options.env ? { env: options.env } : {}),\n },\n context\n );\n const root = await harness.root(context, { agent: { model: options.model } });\n harness.resume();\n return {\n harness,\n root,\n prompt: (text) => prompt(
|
|
5
|
-
"mappings": "AACA,SAAS,0BAA0B;AAEnC;AAAA,EACE;AAAA,EAEA;AAAA,EAEA;AAAA,EAEA;AAAA,OAIK;
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { Models } from '@earendil-works/pi-ai/models';\nimport {\n AssistantEntry,\n type Conversation,\n createRegistry,\n type EntryId,\n Harness,\n type HarnessOptions,\n MemoryStorage,\n type ModelRef,\n type Registry,\n type Storage,\n} from '@earendil-works/pi-durable';\nimport { activeCone, type Cones, pointCone } from './cone.ts';\n\nexport interface AgentOptions {\n models: Models;\n model: ModelRef;\n storage?: Storage;\n registry?: Registry;\n settings?: HarnessOptions['settings'];\n env?: HarnessOptions['env'];\n context?: Context;\n}\n\nexport interface Agent extends Cones {\n readonly harness: Harness;\n readonly root: Conversation;\n onCone(listener: (conversation: Conversation) => void | Promise<void>): () => void;\n prompt(text: string): Promise<string>;\n close(): Promise<void>;\n}\n\ninterface Block {\n type: string;\n text?: string;\n}\n\nexport function answerText(content: unknown): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n return (content as Block[])\n .filter((block) => block.type === 'text')\n .map((block) => block.text ?? '')\n .join('');\n}\n\nasync function prompt(root: Conversation, text: string, context: Context): Promise<string> {\n const submission = await root.submit({ type: 'input', content: text }, context);\n const settled = await submission.wait(context);\n const { answer, reason } = settled as { answer?: EntryId; reason?: string };\n if (answer === undefined) throw new Error(`unanswered: ${reason}`);\n const entry = await root.commit((tx) => tx.entry(AssistantEntry, answer), context);\n return answerText(entry?.model?.[0]?.content);\n}\n\nexport async function openAgent(options: AgentOptions): Promise<Agent> {\n const context = options.context ?? BACKGROUND_CONTEXT;\n const harness = await Harness.open(\n options.storage ?? new MemoryStorage(),\n {\n models: options.models,\n registry: options.registry ?? createRegistry(),\n ...(options.settings ? { settings: options.settings } : {}),\n ...(options.env ? { env: options.env } : {}),\n },\n context\n );\n const root = await harness.root(context, { agent: { model: options.model } });\n let cone = await activeCone(harness, root, context);\n const listeners = new Set<(conversation: Conversation) => void | Promise<void>>();\n harness.resume();\n return {\n harness,\n root,\n cone: () => cone,\n async switchCone(conversation, switching) {\n await pointCone(harness, conversation, switching);\n cone = conversation;\n await Promise.all([...listeners].map((listener) => listener(conversation)));\n },\n onCone(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n prompt: (text) => prompt(cone, text, context),\n close: () => harness.close(context),\n };\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,0BAA0B;AAEnC;AAAA,EACE;AAAA,EAEA;AAAA,EAEA;AAAA,EAEA;AAAA,OAIK;AACP,SAAS,YAAwB,iBAAiB;AAyB3C,SAAS,WAAW,SAA0B;AACnD,MAAI,OAAO,YAAY,SAAU,QAAO;AACxC,MAAI,CAAC,MAAM,QAAQ,OAAO,EAAG,QAAO;AACpC,SAAQ,QACL,OAAO,CAAC,UAAU,MAAM,SAAS,MAAM,EACvC,IAAI,CAAC,UAAU,MAAM,QAAQ,EAAE,EAC/B,KAAK,EAAE;AACZ;AAEA,eAAe,OAAO,MAAoB,MAAc,SAAmC;AACzF,QAAM,aAAa,MAAM,KAAK,OAAO,EAAE,MAAM,SAAS,SAAS,KAAK,GAAG,OAAO;AAC9E,QAAM,UAAU,MAAM,WAAW,KAAK,OAAO;AAC7C,QAAM,EAAE,QAAQ,OAAO,IAAI;AAC3B,MAAI,WAAW,OAAW,OAAM,IAAI,MAAM,eAAe,MAAM,EAAE;AACjE,QAAM,QAAQ,MAAM,KAAK,OAAO,CAAC,OAAO,GAAG,MAAM,gBAAgB,MAAM,GAAG,OAAO;AACjF,SAAO,WAAW,OAAO,QAAQ,CAAC,GAAG,OAAO;AAC9C;AAEA,eAAsB,UAAU,SAAuC;AACrE,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,QAAQ,WAAW,IAAI,cAAc;AAAA,IACrC;AAAA,MACE,QAAQ,QAAQ;AAAA,MAChB,UAAU,QAAQ,YAAY,eAAe;AAAA,MAC7C,GAAI,QAAQ,WAAW,EAAE,UAAU,QAAQ,SAAS,IAAI,CAAC;AAAA,MACzD,GAAI,QAAQ,MAAM,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC;AAAA,IAC5C;AAAA,IACA;AAAA,EACF;AACA,QAAM,OAAO,MAAM,QAAQ,KAAK,SAAS,EAAE,OAAO,EAAE,OAAO,QAAQ,MAAM,EAAE,CAAC;AAC5E,MAAI,OAAO,MAAM,WAAW,SAAS,MAAM,OAAO;AAClD,QAAM,YAAY,oBAAI,IAA0D;AAChF,UAAQ,OAAO;AACf,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,MAAM,MAAM;AAAA,IACZ,MAAM,WAAW,cAAc,WAAW;AACxC,YAAM,UAAU,SAAS,cAAc,SAAS;AAChD,aAAO;AACP,YAAM,QAAQ,IAAI,CAAC,GAAG,SAAS,EAAE,IAAI,CAAC,aAAa,SAAS,YAAY,CAAC,CAAC;AAAA,IAC5E;AAAA,IACA,OAAO,UAAU;AACf,gBAAU,IAAI,QAAQ;AACtB,aAAO,MAAM,UAAU,OAAO,QAAQ;AAAA,IACxC;AAAA,IACA,QAAQ,CAAC,SAAS,OAAO,MAAM,MAAM,OAAO;AAAA,IAC5C,OAAO,MAAM,QAAQ,MAAM,OAAO;AAAA,EACpC;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/cone.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defineDoc
|
|
3
|
+
} from "@earendil-works/pi-durable";
|
|
4
|
+
const ConeDoc = defineDoc({
|
|
5
|
+
kind: "slicc.cone",
|
|
6
|
+
version: 1,
|
|
7
|
+
scope: "session",
|
|
8
|
+
initial: () => ({ conversation: null })
|
|
9
|
+
});
|
|
10
|
+
async function activeCone(harness, root, context) {
|
|
11
|
+
const id = await harness.commit(async (tx) => (await tx.doc(ConeDoc)).conversation, context);
|
|
12
|
+
const found = id === null ? void 0 : await harness.conversation(id, context);
|
|
13
|
+
return found ?? root;
|
|
14
|
+
}
|
|
15
|
+
async function pointCone(harness, conversation, context) {
|
|
16
|
+
await harness.commit(async (tx) => {
|
|
17
|
+
(await tx.doc(ConeDoc)).conversation = conversation.id;
|
|
18
|
+
}, context);
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
ConeDoc,
|
|
22
|
+
activeCone,
|
|
23
|
+
pointCone
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=cone.js.map
|
package/dist/cone.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/cone.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n type Conversation,\n type ConversationId,\n defineDoc,\n type Harness,\n} from '@earendil-works/pi-durable';\n\nexport const ConeDoc = defineDoc<{ conversation: number | null }>({\n kind: 'slicc.cone',\n version: 1,\n scope: 'session',\n initial: () => ({ conversation: null }),\n});\n\nexport interface Cones {\n cone(): Conversation;\n switchCone(conversation: Conversation, context: Context): Promise<void>;\n}\n\nexport async function activeCone(\n harness: Harness,\n root: Conversation,\n context: Context\n): Promise<Conversation> {\n const id = await harness.commit(async (tx) => (await tx.doc(ConeDoc)).conversation, context);\n const found = id === null ? undefined : await harness.conversation(id as ConversationId, context);\n return found ?? root;\n}\n\nexport async function pointCone(\n harness: Harness,\n conversation: Conversation,\n context: Context\n): Promise<void> {\n await harness.commit(async (tx) => {\n (await tx.doc(ConeDoc)).conversation = conversation.id;\n }, context);\n}\n"],
|
|
5
|
+
"mappings": "AACA;AAAA,EAGE;AAAA,OAEK;AAEA,MAAM,UAAU,UAA2C;AAAA,EAChE,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS,OAAO,EAAE,cAAc,KAAK;AACvC,CAAC;AAOD,eAAsB,WACpB,SACA,MACA,SACuB;AACvB,QAAM,KAAK,MAAM,QAAQ,OAAO,OAAO,QAAQ,MAAM,GAAG,IAAI,OAAO,GAAG,cAAc,OAAO;AAC3F,QAAM,QAAQ,OAAO,OAAO,SAAY,MAAM,QAAQ,aAAa,IAAsB,OAAO;AAChG,SAAO,SAAS;AAClB;AAEA,eAAsB,UACpB,SACA,cACA,SACe;AACf,QAAM,QAAQ,OAAO,OAAO,OAAO;AACjC,KAAC,MAAM,GAAG,IAAI,OAAO,GAAG,eAAe,aAAa;AAAA,EACtD,GAAG,OAAO;AACZ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/control.js
CHANGED
|
@@ -20,23 +20,88 @@ async function accepted(operation) {
|
|
|
20
20
|
return { accepted: false, submissionId: null, error: operationError(error) };
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
-
function
|
|
23
|
+
function cones(target) {
|
|
24
|
+
if ("cone" in target) return target;
|
|
24
25
|
return {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
cone: () => target,
|
|
27
|
+
switchCone: async () => {
|
|
28
|
+
throw new Error("this conversation has no cone to rewind");
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function lastTurn(view, messageId) {
|
|
33
|
+
const { entries } = view;
|
|
34
|
+
const target = entries.findIndex((entry) => `e${entry.id}` === messageId);
|
|
35
|
+
if (target < 0) return null;
|
|
36
|
+
const turn = entries.slice(0, target).findLastIndex((entry) => entry.kind === "pi.user");
|
|
37
|
+
if (turn < 0) return null;
|
|
38
|
+
const user = entries[turn];
|
|
39
|
+
return { text: answerText(user.model?.[0]?.content), at: entries[turn - 1]?.id ?? null };
|
|
40
|
+
}
|
|
41
|
+
function busy(view) {
|
|
42
|
+
const live = view.docs["pi.live"];
|
|
43
|
+
const inbox = view.docs["pi.inbox"];
|
|
44
|
+
return Boolean(live?.run) || Boolean(inbox?.items?.length);
|
|
45
|
+
}
|
|
46
|
+
function createAgentControl(harness, target, deliveries) {
|
|
47
|
+
const cone = cones(target);
|
|
48
|
+
const current = () => cone.cone();
|
|
49
|
+
let lock = Promise.resolve();
|
|
50
|
+
const serial = (operation) => {
|
|
51
|
+
const run = lock.then(operation, operation);
|
|
52
|
+
lock = run.catch(() => void 0);
|
|
53
|
+
return run;
|
|
54
|
+
};
|
|
55
|
+
return {
|
|
56
|
+
send(request, context) {
|
|
57
|
+
return serial(async () => {
|
|
58
|
+
const response = await accepted(
|
|
59
|
+
() => current().submit(
|
|
60
|
+
{
|
|
61
|
+
type: "input",
|
|
62
|
+
content: request.text,
|
|
63
|
+
whenBusy: request.whenBusy,
|
|
64
|
+
...request.requestId ? { requestId: request.requestId } : {}
|
|
65
|
+
},
|
|
66
|
+
context
|
|
67
|
+
)
|
|
68
|
+
);
|
|
69
|
+
const id = response.submissionId === null ? void 0 : submissionId(response.submissionId);
|
|
70
|
+
if (id !== void 0) deliveries?.expect(id, request.whenBusy);
|
|
71
|
+
return response;
|
|
72
|
+
});
|
|
73
|
+
},
|
|
74
|
+
rewind(messageId, context) {
|
|
75
|
+
return serial(async () => {
|
|
76
|
+
const conversation = current();
|
|
77
|
+
const attached = await conversation.viewState(context);
|
|
78
|
+
const view = attached.value;
|
|
79
|
+
attached.dispose();
|
|
80
|
+
const pending = (await harness.inspect(context)).submissions.some(
|
|
81
|
+
(submission) => submission.conversationId === conversation.id
|
|
82
|
+
);
|
|
83
|
+
if (pending || busy(view)) return { done: false, text: null, reason: "busy" };
|
|
84
|
+
const turn = lastTurn(view, messageId);
|
|
85
|
+
if (!turn) return { done: false, text: null, reason: "no-turn" };
|
|
86
|
+
const agent = view.docs["pi.agent"];
|
|
87
|
+
const ownership = { kind: "ownerless" };
|
|
88
|
+
const fork = turn.at === null ? await harness.createConversation(
|
|
28
89
|
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
90
|
+
ownership,
|
|
91
|
+
agent: {
|
|
92
|
+
model: agent?.model ?? null,
|
|
93
|
+
thinkingLevel: agent?.thinkingLevel ?? null
|
|
94
|
+
}
|
|
33
95
|
},
|
|
34
96
|
context
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
97
|
+
) : await conversation.fork(turn.at, { ownership }, context);
|
|
98
|
+
await fork.submit(
|
|
99
|
+
{ type: "write", entry: { kind: "slicc.rewound", data: { turns: 1 } } },
|
|
100
|
+
context
|
|
101
|
+
);
|
|
102
|
+
await cone.switchCone(fork, context);
|
|
103
|
+
return { done: true, text: turn.text, reason: null };
|
|
104
|
+
});
|
|
40
105
|
},
|
|
41
106
|
async wait(id, context) {
|
|
42
107
|
const parsed = submissionId(id);
|
|
@@ -51,14 +116,14 @@ function createAgentControl(harness, conversation, deliveries) {
|
|
|
51
116
|
async withdraw(id, context) {
|
|
52
117
|
const parsed = submissionId(id);
|
|
53
118
|
if (parsed === void 0) return { outcome: "not_found" };
|
|
54
|
-
const result = await harness.abortSubmission(parsed, context,
|
|
119
|
+
const result = await harness.abortSubmission(parsed, context, current().id);
|
|
55
120
|
if (result === "aborted") return { outcome: "withdrawn" };
|
|
56
121
|
return { outcome: result === "not_found" ? "not_found" : "already_placed" };
|
|
57
122
|
},
|
|
58
|
-
abort: (context) =>
|
|
59
|
-
compact: (instructions, context) => accepted(() =>
|
|
60
|
-
reset: (handoff, context) =>
|
|
61
|
-
configure: (change, context) =>
|
|
123
|
+
abort: (context) => current().abort(context),
|
|
124
|
+
compact: (instructions, context) => accepted(() => current().compact(instructions ?? void 0, context)),
|
|
125
|
+
reset: (handoff, context) => current().reset(handoff ?? void 0, context),
|
|
126
|
+
configure: (change, context) => current().configure(
|
|
62
127
|
{
|
|
63
128
|
...change.model ? { model: change.model } : {},
|
|
64
129
|
...change.thinkingLevel ? { thinkingLevel: change.thinkingLevel } : {}
|
package/dist/control.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/control.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n AssistantEntry,\n type Conversation,\n ConversationBusy,\n type EntryId,\n type Harness,\n type SubmissionId,\n} from '@earendil-works/pi-durable';\nimport { answerText } from './agent.ts';\nimport type { Deliveries } from './deliveries.ts';\nimport type { AgentControl, OperationError, SendResponse } from './services.ts';\n\nexport function submissionId(value: string): SubmissionId | undefined {\n const id = Number(value);\n return /^[1-9][0-9]*$/.test(value) && Number.isSafeInteger(id) ? (id as SubmissionId) : undefined;\n}\n\nfunction operationError(error: unknown): OperationError {\n if (error instanceof ConversationBusy) return { code: 'busy', message: error.message };\n return { code: 'failed', message: error instanceof Error ? error.message : String(error) };\n}\n\nasync function accepted(operation: () => Promise<{ id: number } | number>): Promise<SendResponse> {\n try {\n const result = await operation();\n const id = typeof result === 'number' ? result : result.id;\n return { accepted: true, submissionId: String(id), error: null };\n } catch (error) {\n return { accepted: false, submissionId: null, error: operationError(error) };\n }\n}\n\nexport function createAgentControl(\n harness: Harness,\n
|
|
5
|
-
"mappings": "AAAA;AAAA,
|
|
4
|
+
"sourcesContent": ["import {\n type AgentChange,\n AssistantEntry,\n type Conversation,\n ConversationBusy,\n type ConversationView,\n type EntryId,\n type EntryRecord,\n type Harness,\n type SubmissionId,\n} from '@earendil-works/pi-durable';\nimport { answerText } from './agent.ts';\nimport type { Cones } from './cone.ts';\nimport type { Deliveries } from './deliveries.ts';\nimport type { AgentControl, OperationError, SendResponse } from './services.ts';\n\nexport function submissionId(value: string): SubmissionId | undefined {\n const id = Number(value);\n return /^[1-9][0-9]*$/.test(value) && Number.isSafeInteger(id) ? (id as SubmissionId) : undefined;\n}\n\nfunction operationError(error: unknown): OperationError {\n if (error instanceof ConversationBusy) return { code: 'busy', message: error.message };\n return { code: 'failed', message: error instanceof Error ? error.message : String(error) };\n}\n\nasync function accepted(operation: () => Promise<{ id: number } | number>): Promise<SendResponse> {\n try {\n const result = await operation();\n const id = typeof result === 'number' ? result : result.id;\n return { accepted: true, submissionId: String(id), error: null };\n } catch (error) {\n return { accepted: false, submissionId: null, error: operationError(error) };\n }\n}\n\nfunction cones(target: Conversation | Cones): Cones {\n if ('cone' in target) return target;\n return {\n cone: () => target,\n switchCone: async () => {\n throw new Error('this conversation has no cone to rewind');\n },\n };\n}\n\ninterface Turn {\n text: string;\n at: EntryId | null;\n}\n\nfunction lastTurn(view: ConversationView, messageId: string | null): Turn | null {\n const { entries } = view;\n const target = entries.findIndex((entry) => `e${entry.id}` === messageId);\n if (target < 0) return null;\n const turn = entries.slice(0, target).findLastIndex((entry) => entry.kind === 'pi.user');\n if (turn < 0) return null;\n const user = entries[turn] as EntryRecord;\n return { text: answerText(user.model?.[0]?.content), at: entries[turn - 1]?.id ?? null };\n}\n\nfunction busy(view: ConversationView): boolean {\n const live = view.docs['pi.live'] as { run?: unknown } | undefined;\n const inbox = view.docs['pi.inbox'] as { items?: unknown[] } | undefined;\n return Boolean(live?.run) || Boolean(inbox?.items?.length);\n}\n\nexport function createAgentControl(\n harness: Harness,\n target: Conversation | Cones,\n deliveries?: Deliveries\n): AgentControl {\n const cone = cones(target);\n const current = () => cone.cone();\n let lock: Promise<unknown> = Promise.resolve();\n const serial = <T>(operation: () => Promise<T>): Promise<T> => {\n const run = lock.then(operation, operation);\n lock = run.catch(() => undefined);\n return run;\n };\n return {\n send(request, context) {\n return serial(async () => {\n const response = await accepted(() =>\n current().submit(\n {\n type: 'input',\n content: request.text,\n whenBusy: request.whenBusy,\n ...(request.requestId ? { requestId: request.requestId } : {}),\n },\n context\n )\n );\n const id = response.submissionId === null ? undefined : submissionId(response.submissionId);\n if (id !== undefined) deliveries?.expect(id, request.whenBusy);\n return response;\n });\n },\n rewind(messageId, context) {\n return serial(async () => {\n const conversation = current();\n const attached = await conversation.viewState(context);\n const view = attached.value;\n attached.dispose();\n const pending = (await harness.inspect(context)).submissions.some(\n (submission) => submission.conversationId === conversation.id\n );\n if (pending || busy(view)) return { done: false, text: null, reason: 'busy' };\n const turn = lastTurn(view, messageId);\n if (!turn) return { done: false, text: null, reason: 'no-turn' };\n const agent = view.docs['pi.agent'] as AgentChange | undefined;\n const ownership = { kind: 'ownerless' } as const;\n const fork =\n turn.at === null\n ? await harness.createConversation(\n {\n ownership,\n agent: {\n model: agent?.model ?? null,\n thinkingLevel: agent?.thinkingLevel ?? null,\n },\n },\n context\n )\n : await conversation.fork(turn.at, { ownership }, context);\n await fork.submit(\n { type: 'write', entry: { kind: 'slicc.rewound', data: { turns: 1 } } },\n context\n );\n await cone.switchCone(fork, context);\n return { done: true, text: turn.text, reason: null };\n });\n },\n\n async wait(id, context) {\n const parsed = submissionId(id);\n const submission =\n parsed === undefined ? undefined : await harness.submission(parsed, context);\n if (!submission) throw new Error(`unknown submission ${id}`);\n const settled = await submission.wait(context);\n const { answer, reason } = settled as { answer?: EntryId; reason?: string };\n if (answer === undefined) return { status: 'unanswered', text: null, reason: String(reason) };\n const entry = await harness.commit((tx) => tx.entry(AssistantEntry, answer), context);\n return { status: 'done', text: answerText(entry?.model?.[0]?.content), reason: null };\n },\n async withdraw(id, context) {\n const parsed = submissionId(id);\n if (parsed === undefined) return { outcome: 'not_found' };\n const result = await harness.abortSubmission(parsed, context, current().id);\n if (result === 'aborted') return { outcome: 'withdrawn' };\n return { outcome: result === 'not_found' ? 'not_found' : 'already_placed' };\n },\n abort: (context) => current().abort(context),\n compact: (instructions, context) =>\n accepted(() => current().compact(instructions ?? undefined, context)),\n reset: (handoff, context) => current().reset(handoff ?? undefined, context),\n configure: (change, context) =>\n current().configure(\n {\n ...(change.model ? { model: change.model } : {}),\n ...(change.thinkingLevel ? { thinkingLevel: change.thinkingLevel } : {}),\n },\n context\n ),\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/host.js
CHANGED
|
@@ -60,17 +60,40 @@ const noSettings = {
|
|
|
60
60
|
},
|
|
61
61
|
signIn: async () => null
|
|
62
62
|
};
|
|
63
|
+
async function follow(agent, context) {
|
|
64
|
+
let attached = await agent.cone().viewState(context);
|
|
65
|
+
const state = replicatedState(attached.value);
|
|
66
|
+
const mirror = (value, from) => state.replace(from, value);
|
|
67
|
+
let off = attached.subscribe(mirror);
|
|
68
|
+
const stop = agent.onCone(async (conversation) => {
|
|
69
|
+
const next = await conversation.viewState(context);
|
|
70
|
+
off();
|
|
71
|
+
attached.dispose();
|
|
72
|
+
attached = next;
|
|
73
|
+
state.replace(context, next.value);
|
|
74
|
+
off = next.subscribe(mirror);
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
77
|
+
state,
|
|
78
|
+
dispose() {
|
|
79
|
+
stop();
|
|
80
|
+
off();
|
|
81
|
+
attached.dispose();
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
63
85
|
async function hostAgent(agent, options = {}) {
|
|
64
86
|
const context = options.context ?? BACKGROUND_CONTEXT;
|
|
65
87
|
const serverId = options.serverId ?? crypto.randomUUID();
|
|
66
|
-
const
|
|
88
|
+
const transcript = await follow(agent, context);
|
|
89
|
+
const { state } = transcript;
|
|
67
90
|
const provider = new RemoteServiceProvider([
|
|
68
91
|
{ service: AgentControl, mode: "singleton" },
|
|
69
92
|
{ service: AgentTranscript, mode: "singleton" },
|
|
70
93
|
{ service: AgentSettings, mode: "singleton" }
|
|
71
94
|
]);
|
|
72
95
|
const deliveries = trackDeliveries(agent.harness, state, context);
|
|
73
|
-
provider.provide(AgentControl, createAgentControl(agent.harness, agent
|
|
96
|
+
provider.provide(AgentControl, createAgentControl(agent.harness, agent, deliveries));
|
|
74
97
|
provider.provide(AgentTranscript, { state, deliveries: deliveries.state });
|
|
75
98
|
provider.provide(AgentSettings, options.settings ?? noSettings);
|
|
76
99
|
const host = {
|
|
@@ -92,7 +115,7 @@ async function hostAgent(agent, options = {}) {
|
|
|
92
115
|
await server.close();
|
|
93
116
|
provider.dispose();
|
|
94
117
|
deliveries.dispose();
|
|
95
|
-
|
|
118
|
+
transcript.dispose();
|
|
96
119
|
await agent.close();
|
|
97
120
|
}
|
|
98
121
|
};
|
package/dist/host.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/host.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n type Context,\n createRemoteServiceEndpoint,\n RemoteServiceProvider,\n replicatedState,\n} from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport {\n type RoutedServerServiceHost,\n type RoutedSessionAttachment,\n type RoutedSessionHandle,\n Server,\n type ServerHost,\n SessionNotFoundError,\n} from '@earendil-works/pi-server';\nimport type { Agent } from './agent.ts';\nimport { createAgentControl } from './control.ts';\nimport { trackDeliveries } from './deliveries.ts';\nimport { PortListener } from './listener.ts';\nimport {\n AGENT_SESSION,\n AgentControl,\n AgentSessions,\n AgentSettings,\n AgentTranscript,\n} from './services.ts';\nimport type { PortEndpoint } from './wire.ts';\n\nexport interface AgentHost {\n readonly serverId: string;\n readonly agent: Agent;\n connect(endpoint: PortEndpoint): void;\n close(): Promise<void>;\n}\n\nfunction endpointFor(provider: RemoteServiceProvider): RoutedSessionAttachment {\n const endpoint = createRemoteServiceEndpoint(provider);\n return {\n invokeService: (call, publish, context) =>\n endpoint.invoke(call, (id, update) => publish(id, update, context), context),\n release: () => endpoint.dispose(),\n };\n}\n\nfunction sessionServices(provider: RemoteServiceProvider): RoutedSessionHandle {\n return {\n attachClient: () => endpointFor(provider),\n close: async () => {},\n };\n}\n\nfunction serverServices(): RoutedServerServiceHost {\n return {\n attachClient(presentation) {\n const provider = new RemoteServiceProvider([{ service: AgentSessions, mode: 'singleton' }]);\n provider.provide(AgentSessions, {\n attach: (sessionId, context) => presentation.attachSession(sessionId, context),\n detach: (context) => presentation.detachSession(context),\n });\n const attachment = endpointFor(provider);\n return {\n invokeService: attachment.invokeService,\n release(context) {\n attachment.release(context);\n provider.dispose();\n },\n };\n },\n };\n}\n\nconst noSettings: AgentSettings = {\n state: replicatedState({ models: [], accounts: [] }),\n connect: async () => {\n throw new Error('this agent has no accounts');\n },\n disconnect: async () => {},\n signIn: async () => null,\n};\n\nexport async function hostAgent(\n agent: Agent,\n options: { serverId?: string; context?: Context; settings?: AgentSettings } = {}\n): Promise<AgentHost> {\n const context = options.context ?? BACKGROUND_CONTEXT;\n const serverId = options.serverId ?? crypto.randomUUID();\n const
|
|
5
|
-
"mappings": "AAAA;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,0BAA0B;
|
|
4
|
+
"sourcesContent": ["import {\n type Context,\n createRemoteServiceEndpoint,\n RemoteServiceProvider,\n replicatedState,\n} from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport {\n type RoutedServerServiceHost,\n type RoutedSessionAttachment,\n type RoutedSessionHandle,\n Server,\n type ServerHost,\n SessionNotFoundError,\n} from '@earendil-works/pi-server';\nimport type { Agent } from './agent.ts';\nimport { createAgentControl } from './control.ts';\nimport { trackDeliveries } from './deliveries.ts';\nimport { PortListener } from './listener.ts';\nimport {\n AGENT_SESSION,\n AgentControl,\n AgentSessions,\n AgentSettings,\n AgentTranscript,\n} from './services.ts';\nimport type { PortEndpoint } from './wire.ts';\n\nexport interface AgentHost {\n readonly serverId: string;\n readonly agent: Agent;\n connect(endpoint: PortEndpoint): void;\n close(): Promise<void>;\n}\n\nfunction endpointFor(provider: RemoteServiceProvider): RoutedSessionAttachment {\n const endpoint = createRemoteServiceEndpoint(provider);\n return {\n invokeService: (call, publish, context) =>\n endpoint.invoke(call, (id, update) => publish(id, update, context), context),\n release: () => endpoint.dispose(),\n };\n}\n\nfunction sessionServices(provider: RemoteServiceProvider): RoutedSessionHandle {\n return {\n attachClient: () => endpointFor(provider),\n close: async () => {},\n };\n}\n\nfunction serverServices(): RoutedServerServiceHost {\n return {\n attachClient(presentation) {\n const provider = new RemoteServiceProvider([{ service: AgentSessions, mode: 'singleton' }]);\n provider.provide(AgentSessions, {\n attach: (sessionId, context) => presentation.attachSession(sessionId, context),\n detach: (context) => presentation.detachSession(context),\n });\n const attachment = endpointFor(provider);\n return {\n invokeService: attachment.invokeService,\n release(context) {\n attachment.release(context);\n provider.dispose();\n },\n };\n },\n };\n}\n\nconst noSettings: AgentSettings = {\n state: replicatedState({ models: [], accounts: [] }),\n connect: async () => {\n throw new Error('this agent has no accounts');\n },\n disconnect: async () => {},\n signIn: async () => null,\n};\n\nasync function follow(agent: Agent, context: Context) {\n let attached = await agent.cone().viewState(context);\n const state = replicatedState<ConversationView>(attached.value);\n const mirror = (value: ConversationView, from: Context) => state.replace(from, value);\n let off = attached.subscribe(mirror);\n const stop = agent.onCone(async (conversation) => {\n const next = await conversation.viewState(context);\n off();\n attached.dispose();\n attached = next;\n state.replace(context, next.value);\n off = next.subscribe(mirror);\n });\n return {\n state,\n dispose() {\n stop();\n off();\n attached.dispose();\n },\n };\n}\n\nexport async function hostAgent(\n agent: Agent,\n options: { serverId?: string; context?: Context; settings?: AgentSettings } = {}\n): Promise<AgentHost> {\n const context = options.context ?? BACKGROUND_CONTEXT;\n const serverId = options.serverId ?? crypto.randomUUID();\n const transcript = await follow(agent, context);\n const { state } = transcript;\n const provider = new RemoteServiceProvider([\n { service: AgentControl, mode: 'singleton' },\n { service: AgentTranscript, mode: 'singleton' },\n { service: AgentSettings, mode: 'singleton' },\n ]);\n const deliveries = trackDeliveries(agent.harness, state, context);\n provider.provide(AgentControl, createAgentControl(agent.harness, agent, deliveries));\n provider.provide(AgentTranscript, { state, deliveries: deliveries.state });\n provider.provide(AgentSettings, options.settings ?? noSettings);\n const host: ServerHost = {\n serverServices: serverServices(),\n async resolveSession(sessionId) {\n if (sessionId !== AGENT_SESSION)\n throw new SessionNotFoundError(`unknown session ${sessionId}`);\n return { id: sessionId };\n },\n openSession: async () => sessionServices(provider),\n };\n const listener = new PortListener(serverId);\n const server = await new Server(host, { serverId, listeners: [listener] }).start();\n return {\n serverId,\n agent,\n connect: (endpoint) => listener.connect(endpoint),\n async close() {\n await server.close();\n provider.dispose();\n deliveries.dispose();\n transcript.dispose();\n await agent.close();\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/services.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/services.ts"],
|
|
4
|
-
"sourcesContent": ["import { type Context, defineService, type ReplicatedState } from '@earendil-works/chord';\nimport type { ConversationView } from '@earendil-works/pi-durable';\n\nexport type SendMode = 'steer' | 'followUp' | 'reject';\n\nexport interface SendRequest {\n text: string;\n whenBusy: SendMode;\n requestId: string | null;\n}\n\nexport interface OperationError {\n code: string;\n message: string;\n}\n\nexport type SendResponse =\n | { accepted: true; submissionId: string; error: null }\n | { accepted: false; submissionId: null; error: OperationError };\n\nexport type PromptResult =\n | { status: 'done'; text: string; reason: null }\n | { status: 'unanswered'; text: null; reason: string };\n\nexport interface AgentControl {\n send(request: SendRequest, context: Context): Promise<SendResponse>;\n wait(submissionId: string, context: Context): Promise<PromptResult>;\n withdraw(\n submissionId: string,\n context: Context\n ): Promise<{ outcome: 'withdrawn' | 'already_placed' | 'not_found' }>;\n abort(context: Context): Promise<void>;\n compact(instructions: string | null, context: Context): Promise<SendResponse>;\n reset(handoff: string | null, context: Context): Promise<void>;\n configure(change: AgentSettingsChange, context: Context): Promise<void>;\n}\n\nexport interface AgentSettingsChange {\n model: { provider: string; modelId: string } | null;\n thinkingLevel: 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | null;\n}\n\nexport const AgentControl = defineService<AgentControl>('slicc.agent.control');\n\nexport type Delivered = 'run' | 'steer' | 'follow-up';\n\nexport interface AgentTranscript {\n readonly state: ReplicatedState<ConversationView>;\n readonly deliveries: ReplicatedState<Record<string, Delivered>>;\n}\n\nexport const AgentTranscript = defineService<AgentTranscript>('slicc.agent.transcript');\n\nexport interface AgentSessions {\n attach(sessionId: string, context: Context): Promise<void>;\n detach(context: Context): Promise<void>;\n}\n\nexport const AgentSessions = defineService<AgentSessions>('slicc.agent.sessions');\n\nexport const AGENT_SESSION = 'agent';\n\nexport interface ModelChoice {\n id: string;\n label: string;\n provider: string;\n kind: 'chat' | 'classifier';\n reasoning: boolean;\n contextWindow: number;\n}\n\nexport interface AccountState {\n id: string;\n provider: string;\n identity: string;\n status: 'connected' | 'disconnected';\n auth: 'api-key' | 'oauth';\n needs: 'cors-free-transport' | null;\n}\n\nexport interface BudgetState {\n provider: string;\n percent: number;\n window: 'weekly';\n resets: string;\n}\n\nexport interface SignIn {\n clientId: string;\n scopes: string;\n imsEnvironment: string;\n}\n\nexport interface SettingsState {\n models: ModelChoice[];\n accounts: AccountState[];\n budget?: BudgetState | null;\n}\n\nexport interface AgentSettings {\n readonly state: ReplicatedState<SettingsState>;\n connect(\n providerId: string,\n secret: string,\n region: string | null,\n context: Context\n ): Promise<void>;\n disconnect(providerId: string, context: Context): Promise<void>;\n signIn(providerId: string, context: Context): Promise<SignIn | null>;\n}\n\nexport const AgentSettings = defineService<AgentSettings>('slicc.agent.settings');\n"],
|
|
5
|
-
"mappings": "AAAA,SAAuB,qBAA2C;
|
|
4
|
+
"sourcesContent": ["import { type Context, defineService, type ReplicatedState } from '@earendil-works/chord';\nimport type { ConversationView } from '@earendil-works/pi-durable';\n\nexport type SendMode = 'steer' | 'followUp' | 'reject';\n\nexport interface SendRequest {\n text: string;\n whenBusy: SendMode;\n requestId: string | null;\n}\n\nexport interface OperationError {\n code: string;\n message: string;\n}\n\nexport type SendResponse =\n | { accepted: true; submissionId: string; error: null }\n | { accepted: false; submissionId: null; error: OperationError };\n\nexport type PromptResult =\n | { status: 'done'; text: string; reason: null }\n | { status: 'unanswered'; text: null; reason: string };\n\nexport interface AgentControl {\n send(request: SendRequest, context: Context): Promise<SendResponse>;\n wait(submissionId: string, context: Context): Promise<PromptResult>;\n withdraw(\n submissionId: string,\n context: Context\n ): Promise<{ outcome: 'withdrawn' | 'already_placed' | 'not_found' }>;\n abort(context: Context): Promise<void>;\n compact(instructions: string | null, context: Context): Promise<SendResponse>;\n reset(handoff: string | null, context: Context): Promise<void>;\n configure(change: AgentSettingsChange, context: Context): Promise<void>;\n rewind(messageId: string | null, context: Context): Promise<Rewound>;\n}\n\nexport type Rewound =\n | { done: true; text: string; reason: null }\n | { done: false; text: null; reason: 'busy' | 'no-turn' };\n\nexport interface AgentSettingsChange {\n model: { provider: string; modelId: string } | null;\n thinkingLevel: 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | null;\n}\n\nexport const AgentControl = defineService<AgentControl>('slicc.agent.control');\n\nexport type Delivered = 'run' | 'steer' | 'follow-up';\n\nexport interface AgentTranscript {\n readonly state: ReplicatedState<ConversationView>;\n readonly deliveries: ReplicatedState<Record<string, Delivered>>;\n}\n\nexport const AgentTranscript = defineService<AgentTranscript>('slicc.agent.transcript');\n\nexport interface AgentSessions {\n attach(sessionId: string, context: Context): Promise<void>;\n detach(context: Context): Promise<void>;\n}\n\nexport const AgentSessions = defineService<AgentSessions>('slicc.agent.sessions');\n\nexport const AGENT_SESSION = 'agent';\n\nexport interface ModelChoice {\n id: string;\n label: string;\n provider: string;\n kind: 'chat' | 'classifier';\n reasoning: boolean;\n contextWindow: number;\n}\n\nexport interface AccountState {\n id: string;\n provider: string;\n identity: string;\n status: 'connected' | 'disconnected';\n auth: 'api-key' | 'oauth';\n needs: 'cors-free-transport' | null;\n}\n\nexport interface BudgetState {\n provider: string;\n percent: number;\n window: 'weekly';\n resets: string;\n}\n\nexport interface SignIn {\n clientId: string;\n scopes: string;\n imsEnvironment: string;\n}\n\nexport interface SettingsState {\n models: ModelChoice[];\n accounts: AccountState[];\n budget?: BudgetState | null;\n}\n\nexport interface AgentSettings {\n readonly state: ReplicatedState<SettingsState>;\n connect(\n providerId: string,\n secret: string,\n region: string | null,\n context: Context\n ): Promise<void>;\n disconnect(providerId: string, context: Context): Promise<void>;\n signIn(providerId: string, context: Context): Promise<SignIn | null>;\n}\n\nexport const AgentSettings = defineService<AgentSettings>('slicc.agent.settings');\n"],
|
|
5
|
+
"mappings": "AAAA,SAAuB,qBAA2C;AA+C3D,MAAM,eAAe,cAA4B,qBAAqB;AAStE,MAAM,kBAAkB,cAA+B,wBAAwB;AAO/E,MAAM,gBAAgB,cAA6B,sBAAsB;AAEzE,MAAM,gBAAgB;AAmDtB,MAAM,gBAAgB,cAA6B,sBAAsB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -94,6 +94,10 @@ class AgentAdapter extends Emitter {
|
|
|
94
94
|
);
|
|
95
95
|
if (!sent.accepted) throw new Error(sent.error.message);
|
|
96
96
|
}
|
|
97
|
+
async rewind(_agentId, messageId) {
|
|
98
|
+
const rewound = await this.#connection.control.rewind(messageId, BACKGROUND_CONTEXT);
|
|
99
|
+
return rewound.done ? { text: rewound.text } : null;
|
|
100
|
+
}
|
|
97
101
|
stop() {
|
|
98
102
|
void this.#connection.control.abort(BACKGROUND_CONTEXT);
|
|
99
103
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/spectrum/agent-port.ts"],
|
|
4
|
-
"sourcesContent": ["import type {\n Agent,\n AgentEvents,\n AgentPort,\n AgentStatus,\n FrozenCone,\n Message,\n Outgoing,\n SlashCommand,\n Thinking,\n UserMessage,\n} from '@ai-ecoverse/slicc-spectrum/ui';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport type { AgentConnection } from '../client.ts';\nimport type { AgentSettingsChange } from '../services.ts';\nimport { Emitter } from './emitter.ts';\nimport { isBusy, queued, toMessages } from './messages.ts';\n\nexport const CONE = 'cone';\n\nconst thinking: Record<string, Thinking> = {\n off: 'off',\n minimal: 'low',\n low: 'low',\n medium: 'medium',\n high: 'high',\n xhigh: 'high',\n max: 'high',\n};\n\ninterface AgentDoc {\n model?: { provider: string; modelId: string };\n thinkingLevel?: string;\n}\n\nfunction status(view: ConversationView | undefined, messages: readonly Message[]): AgentStatus {\n if (isBusy(view)) {\n const live = view?.docs['pi.live'] as { tools?: { status: string }[] } | undefined;\n return live?.tools?.some((slot) => slot.status === 'running') ? 'working' : 'thinking';\n }\n const last = messages.at(-1);\n return last?.role === 'assistant' && last.status === 'error' ? 'error' : 'idle';\n}\n\nexport class AgentAdapter extends Emitter<AgentEvents> implements AgentPort {\n readonly #connection: AgentConnection;\n readonly #ready: Promise<void>;\n #messages: Message[] = [];\n\n constructor(connection: AgentConnection) {\n super();\n this.#connection = connection;\n this.#ready = new Promise((resolve) => {\n connection.transcript.subscribe((view) => {\n this.#update(view);\n resolve();\n });\n connection.deliveries.subscribe(() => {\n const view = this.#view();\n if (view) this.#update(view);\n });\n });\n }\n\n #view(): ConversationView | undefined {\n return this.#connection.transcript.value;\n }\n\n #update(view: ConversationView): void {\n const before = this.#messages;\n this.#messages = toMessages(view, this.#connection.deliveries.value);\n this.emit('agents', this.list());\n if (before.length !== this.#messages.length) {\n this.emit('messages', CONE);\n return;\n }\n const changed = this.#messages.filter(\n (message, index) => JSON.stringify(message) !== JSON.stringify(before[index])\n );\n if (changed.length === 1)\n this.emit('message', { agentId: CONE, message: changed[0] as Message });\n else if (changed.length > 1) this.emit('messages', CONE);\n }\n\n ready(): Promise<void> {\n return this.#ready;\n }\n\n list(): readonly Agent[] {\n const doc = this.#view()?.docs['pi.agent'] as AgentDoc | undefined;\n return [\n {\n id: CONE,\n name: 'sliccy',\n kind: 'cone',\n parentId: null,\n status: status(this.#view(), this.#messages),\n model: doc?.model ? `${doc.model.provider}/${doc.model.modelId}` : '',\n contextFill: 0,\n unread: 0,\n thinking: thinking[doc?.thinkingLevel ?? 'off'] ?? 'off',\n },\n ];\n }\n\n active(): string {\n return CONE;\n }\n\n select(): void {}\n\n messages(): readonly Message[] {\n return this.#messages;\n }\n\n async send(_agentId: string, input: string | Outgoing): Promise<void> {\n const outgoing = typeof input === 'string' ? { text: input } : input;\n const sent = await this.#connection.control.send(\n {\n text: outgoing.text,\n whenBusy: outgoing.mode === 'queue' ? 'followUp' : 'steer',\n requestId: crypto.randomUUID(),\n },\n BACKGROUND_CONTEXT\n );\n if (!sent.accepted) throw new Error(sent.error.message);\n }\n\n stop(): void {\n void this.#connection.control.abort(BACKGROUND_CONTEXT);\n }\n\n busy(): boolean {\n return isBusy(this.#view());\n }\n\n queue(): readonly UserMessage[] {\n return queued(this.#view());\n }\n\n unqueue(_agentId: string, messageId: string): void {\n void this.#connection.control.withdraw(messageId.replace(/^q/, ''), BACKGROUND_CONTEXT);\n }\n\n suggestion(): string | null {\n return null;\n }\n\n answer(): void {}\n\n resolveLick(): void {}\n\n compact(): void {\n void this.#connection.control.compact(null, BACKGROUND_CONTEXT);\n }\n\n clear(): void {\n void this.#connection.control.reset(null, BACKGROUND_CONTEXT);\n }\n\n #configure(change: Partial<AgentSettingsChange>): void {\n void this.#connection.control.configure(\n { model: change.model ?? null, thinkingLevel: change.thinkingLevel ?? null },\n BACKGROUND_CONTEXT\n );\n }\n\n setModel(_agentId: string, model: string): void {\n const at = model.indexOf('/');\n if (at <= 0) return;\n this.#configure({ model: { provider: model.slice(0, at), modelId: model.slice(at + 1) } });\n }\n\n setThinking(_agentId: string, level: Thinking): void {\n this.#configure({ thinkingLevel: level });\n }\n\n async older(): Promise<readonly Message[]> {\n return [];\n }\n\n commands(): readonly SlashCommand[] {\n return [];\n }\n\n createScoop(): Agent {\n throw new Error('Scoops come with a later slicc-agent');\n }\n\n frozen(): readonly FrozenCone[] {\n return [];\n }\n\n freeze(): void {}\n\n thaw(): Agent | null {\n return null;\n }\n\n discard(): void {}\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type {\n Agent,\n AgentEvents,\n AgentPort,\n AgentStatus,\n FrozenCone,\n Message,\n Outgoing,\n SlashCommand,\n Thinking,\n UserMessage,\n} from '@ai-ecoverse/slicc-spectrum/ui';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport type { AgentConnection } from '../client.ts';\nimport type { AgentSettingsChange } from '../services.ts';\nimport { Emitter } from './emitter.ts';\nimport { isBusy, queued, toMessages } from './messages.ts';\n\nexport const CONE = 'cone';\n\nconst thinking: Record<string, Thinking> = {\n off: 'off',\n minimal: 'low',\n low: 'low',\n medium: 'medium',\n high: 'high',\n xhigh: 'high',\n max: 'high',\n};\n\ninterface AgentDoc {\n model?: { provider: string; modelId: string };\n thinkingLevel?: string;\n}\n\nfunction status(view: ConversationView | undefined, messages: readonly Message[]): AgentStatus {\n if (isBusy(view)) {\n const live = view?.docs['pi.live'] as { tools?: { status: string }[] } | undefined;\n return live?.tools?.some((slot) => slot.status === 'running') ? 'working' : 'thinking';\n }\n const last = messages.at(-1);\n return last?.role === 'assistant' && last.status === 'error' ? 'error' : 'idle';\n}\n\nexport class AgentAdapter extends Emitter<AgentEvents> implements AgentPort {\n readonly #connection: AgentConnection;\n readonly #ready: Promise<void>;\n #messages: Message[] = [];\n\n constructor(connection: AgentConnection) {\n super();\n this.#connection = connection;\n this.#ready = new Promise((resolve) => {\n connection.transcript.subscribe((view) => {\n this.#update(view);\n resolve();\n });\n connection.deliveries.subscribe(() => {\n const view = this.#view();\n if (view) this.#update(view);\n });\n });\n }\n\n #view(): ConversationView | undefined {\n return this.#connection.transcript.value;\n }\n\n #update(view: ConversationView): void {\n const before = this.#messages;\n this.#messages = toMessages(view, this.#connection.deliveries.value);\n this.emit('agents', this.list());\n if (before.length !== this.#messages.length) {\n this.emit('messages', CONE);\n return;\n }\n const changed = this.#messages.filter(\n (message, index) => JSON.stringify(message) !== JSON.stringify(before[index])\n );\n if (changed.length === 1)\n this.emit('message', { agentId: CONE, message: changed[0] as Message });\n else if (changed.length > 1) this.emit('messages', CONE);\n }\n\n ready(): Promise<void> {\n return this.#ready;\n }\n\n list(): readonly Agent[] {\n const doc = this.#view()?.docs['pi.agent'] as AgentDoc | undefined;\n return [\n {\n id: CONE,\n name: 'sliccy',\n kind: 'cone',\n parentId: null,\n status: status(this.#view(), this.#messages),\n model: doc?.model ? `${doc.model.provider}/${doc.model.modelId}` : '',\n contextFill: 0,\n unread: 0,\n thinking: thinking[doc?.thinkingLevel ?? 'off'] ?? 'off',\n },\n ];\n }\n\n active(): string {\n return CONE;\n }\n\n select(): void {}\n\n messages(): readonly Message[] {\n return this.#messages;\n }\n\n async send(_agentId: string, input: string | Outgoing): Promise<void> {\n const outgoing = typeof input === 'string' ? { text: input } : input;\n const sent = await this.#connection.control.send(\n {\n text: outgoing.text,\n whenBusy: outgoing.mode === 'queue' ? 'followUp' : 'steer',\n requestId: crypto.randomUUID(),\n },\n BACKGROUND_CONTEXT\n );\n if (!sent.accepted) throw new Error(sent.error.message);\n }\n\n async rewind(_agentId: string, messageId: string): Promise<Outgoing | null> {\n const rewound = await this.#connection.control.rewind(messageId, BACKGROUND_CONTEXT);\n return rewound.done ? { text: rewound.text } : null;\n }\n\n stop(): void {\n void this.#connection.control.abort(BACKGROUND_CONTEXT);\n }\n\n busy(): boolean {\n return isBusy(this.#view());\n }\n\n queue(): readonly UserMessage[] {\n return queued(this.#view());\n }\n\n unqueue(_agentId: string, messageId: string): void {\n void this.#connection.control.withdraw(messageId.replace(/^q/, ''), BACKGROUND_CONTEXT);\n }\n\n suggestion(): string | null {\n return null;\n }\n\n answer(): void {}\n\n resolveLick(): void {}\n\n compact(): void {\n void this.#connection.control.compact(null, BACKGROUND_CONTEXT);\n }\n\n clear(): void {\n void this.#connection.control.reset(null, BACKGROUND_CONTEXT);\n }\n\n #configure(change: Partial<AgentSettingsChange>): void {\n void this.#connection.control.configure(\n { model: change.model ?? null, thinkingLevel: change.thinkingLevel ?? null },\n BACKGROUND_CONTEXT\n );\n }\n\n setModel(_agentId: string, model: string): void {\n const at = model.indexOf('/');\n if (at <= 0) return;\n this.#configure({ model: { provider: model.slice(0, at), modelId: model.slice(at + 1) } });\n }\n\n setThinking(_agentId: string, level: Thinking): void {\n this.#configure({ thinkingLevel: level });\n }\n\n async older(): Promise<readonly Message[]> {\n return [];\n }\n\n commands(): readonly SlashCommand[] {\n return [];\n }\n\n createScoop(): Agent {\n throw new Error('Scoops come with a later slicc-agent');\n }\n\n frozen(): readonly FrozenCone[] {\n return [];\n }\n\n freeze(): void {}\n\n thaw(): Agent | null {\n return null;\n }\n\n discard(): void {}\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -7,12 +7,71 @@ function title(args) {
|
|
|
7
7
|
const subject = args?.command ?? args?.path;
|
|
8
8
|
return typeof subject === "string" ? subject : "";
|
|
9
9
|
}
|
|
10
|
+
const GAP = "\u2026";
|
|
11
|
+
function seconds(value) {
|
|
12
|
+
return typeof value === "number" ? `timeout ${value}s` : void 0;
|
|
13
|
+
}
|
|
14
|
+
function lines(args) {
|
|
15
|
+
const { offset, limit } = args;
|
|
16
|
+
if (typeof offset === "number" && typeof limit === "number")
|
|
17
|
+
return `lines ${offset}\u2013${offset + limit - 1}`;
|
|
18
|
+
if (typeof offset === "number") return `from line ${offset}`;
|
|
19
|
+
return typeof limit === "number" ? `first ${limit} lines` : void 0;
|
|
20
|
+
}
|
|
21
|
+
function edits(args) {
|
|
22
|
+
const list = Array.isArray(args.edits) ? args.edits : [];
|
|
23
|
+
if (list.length === 0) return void 0;
|
|
24
|
+
return {
|
|
25
|
+
before: list.map((edit) => edit.oldText ?? "").join(`
|
|
26
|
+
${GAP}
|
|
27
|
+
`),
|
|
28
|
+
after: list.map((edit) => edit.newText ?? "").join(`
|
|
29
|
+
${GAP}
|
|
30
|
+
`)
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function fromPatch(patch) {
|
|
34
|
+
const before = [];
|
|
35
|
+
const after = [];
|
|
36
|
+
let hunks = 0;
|
|
37
|
+
for (const line of patch.split("\n")) {
|
|
38
|
+
if (line.startsWith("@@")) {
|
|
39
|
+
if (hunks++ > 0) {
|
|
40
|
+
before.push(GAP);
|
|
41
|
+
after.push(GAP);
|
|
42
|
+
}
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (hunks === 0 || line === "" || line.startsWith("\\")) continue;
|
|
46
|
+
const text = line.slice(1);
|
|
47
|
+
if (line.startsWith("-")) before.push(text);
|
|
48
|
+
else if (line.startsWith("+")) after.push(text);
|
|
49
|
+
else {
|
|
50
|
+
before.push(text);
|
|
51
|
+
after.push(text);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return hunks ? { before: before.join("\n"), after: after.join("\n") } : void 0;
|
|
55
|
+
}
|
|
56
|
+
function shape(name, args) {
|
|
57
|
+
const path = typeof args.path === "string" ? args.path : "";
|
|
58
|
+
const meta = name === "bash" ? seconds(args.timeout) : name === "read" ? lines(args) : void 0;
|
|
59
|
+
const base = meta ? { meta } : {};
|
|
60
|
+
if (name === "bash" && typeof args.command === "string") return { input: args.command, ...base };
|
|
61
|
+
if (name === "read" || name === "write") return { input: path, ...base };
|
|
62
|
+
if (name === "edit") {
|
|
63
|
+
const diff = edits(args);
|
|
64
|
+
return { input: path, ...diff ? { diff } : {} };
|
|
65
|
+
}
|
|
66
|
+
return { input: JSON.stringify(args, null, 2) };
|
|
67
|
+
}
|
|
10
68
|
function toolCall(block) {
|
|
69
|
+
const args = block.arguments ?? {};
|
|
11
70
|
return {
|
|
12
71
|
id: block.id ?? "",
|
|
13
72
|
name: block.name ?? "tool",
|
|
14
73
|
title: title(block.arguments),
|
|
15
|
-
|
|
74
|
+
...shape(block.name ?? "tool", args),
|
|
16
75
|
output: "",
|
|
17
76
|
status: "running",
|
|
18
77
|
paths: paths(block.arguments)
|
|
@@ -40,9 +99,18 @@ const credentials = /security token|unrecognizedclient|not authori[sz]ed|unautho
|
|
|
40
99
|
const models = /model/i;
|
|
41
100
|
const unavailable = /access|invalid|not found|identifier|unsupported|not supported|isn['’]t supported|not available|isn['’]t available/i;
|
|
42
101
|
const providerNames = { "amazon-bedrock": "Bedrock" };
|
|
102
|
+
const filtered = /Provider (?:stopped with|finish_reason): (?:content_filter(?:ed)?|guardrail_intervened|sensitive)|refused to complete the request/i;
|
|
43
103
|
const missing = /no api key|missing|not configured|no credentials/i;
|
|
44
104
|
function errorPart(error, provider) {
|
|
45
105
|
const action = errorAction(error);
|
|
106
|
+
if (action === "drop-turn") {
|
|
107
|
+
return {
|
|
108
|
+
type: "error",
|
|
109
|
+
message: "The model's content filter stopped this reply.",
|
|
110
|
+
detail: error,
|
|
111
|
+
action
|
|
112
|
+
};
|
|
113
|
+
}
|
|
46
114
|
if (action === "settings" && provider === "adobe") {
|
|
47
115
|
return {
|
|
48
116
|
type: "error",
|
|
@@ -56,6 +124,7 @@ function errorPart(error, provider) {
|
|
|
56
124
|
return lead ? { type: "error", message: lead, detail: error, action } : { type: "error", message: error, action };
|
|
57
125
|
}
|
|
58
126
|
function errorAction(message) {
|
|
127
|
+
if (filtered.test(message)) return "drop-turn";
|
|
59
128
|
if (credentials.test(message)) return "settings";
|
|
60
129
|
if (models.test(message) && unavailable.test(message)) return "change-model";
|
|
61
130
|
return "retry";
|
|
@@ -109,11 +178,24 @@ function settle(calls, result) {
|
|
|
109
178
|
if (!call) return;
|
|
110
179
|
call.output = textOf(result.content);
|
|
111
180
|
call.status = result.isError ? "error" : "done";
|
|
181
|
+
const patch = result.details?.patch;
|
|
182
|
+
const diff = typeof patch === "string" ? fromPatch(patch) : void 0;
|
|
183
|
+
if (diff) call.diff = diff;
|
|
184
|
+
else if (result.isError) delete call.diff;
|
|
112
185
|
}
|
|
186
|
+
const rewound = (id) => ({
|
|
187
|
+
id,
|
|
188
|
+
role: "system",
|
|
189
|
+
kind: "notice",
|
|
190
|
+
title: "Rewound 1 turn",
|
|
191
|
+
text: "Its prompt is back in the composer.",
|
|
192
|
+
createdAt: 0
|
|
193
|
+
});
|
|
113
194
|
function entryMessage(entry, out, calls, delivered) {
|
|
114
195
|
const id = `e${entry.id}`;
|
|
115
196
|
const model = entry.model?.[0];
|
|
116
|
-
if (entry.kind === "
|
|
197
|
+
if (entry.kind === "slicc.rewound") out.push(rewound(id));
|
|
198
|
+
else if (entry.kind === "pi.user" && model) out.push(user(id, model, delivered));
|
|
117
199
|
else if (entry.kind === "pi.assistant" && model) {
|
|
118
200
|
const message = assistant(id, model);
|
|
119
201
|
out.push(message);
|
|
@@ -189,6 +271,7 @@ export {
|
|
|
189
271
|
assistant,
|
|
190
272
|
errorAction,
|
|
191
273
|
errorPart,
|
|
274
|
+
fromPatch,
|
|
192
275
|
isBusy,
|
|
193
276
|
queued,
|
|
194
277
|
textOf,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/spectrum/messages.ts"],
|
|
4
|
-
"sourcesContent": ["import type {\n AssistantMessage,\n AssistantStatus,\n ErrorAction,\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';\nimport type { Delivered } from '../services.ts';\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(args: Json | undefined): string {\n const subject = args?.command ?? args?.path;\n return typeof subject === 'string' ? subject : '';\n}\n\nfunction toolCall(block: Block): ToolCall {\n return {\n id: block.id ?? '',\n name: block.name ?? 'tool',\n title: title(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\nconst credentials =\n /security token|unrecognizedclient|not authori[sz]ed|unauthori[sz]ed|forbidden|\\b40[13]\\b|api key|credential|bearer/i;\nconst models = /model/i;\nconst unavailable =\n /access|invalid|not found|identifier|unsupported|not supported|isn['\u2019]t supported|not available|isn['\u2019]t available/i;\n\nconst providerNames: Record<string, string> = { 'amazon-bedrock': 'Bedrock' };\nconst missing = /no api key|missing|not configured|no credentials/i;\n\nexport function errorPart(error: string, provider?: string): MessagePart {\n const action = errorAction(error);\n if (action === 'settings' && provider === 'adobe') {\n return {\n type: 'error',\n message: 'Your Adobe session has expired.',\n detail: error,\n action: 'login',\n };\n }\n const name = providerNames[provider ?? ''] ?? 'The provider';\n const lead =\n action === 'settings'\n ? missing.test(error)\n ? `${name} needs an API key.`\n : `${name} rejected the API key.`\n : action === 'change-model'\n ? \"This model isn't available with the current account.\"\n : null;\n return lead\n ? { type: 'error', message: lead, detail: error, action }\n : { type: 'error', message: error, action };\n}\n\nexport function errorAction(message: string): ErrorAction {\n if (credentials.test(message)) return 'settings';\n if (models.test(message) && unavailable.test(message)) return 'change-model';\n return 'retry';\n}\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) parts.push(errorPart(message.errorMessage, message.provider));\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, delivered: Delivered | undefined): UserMessage {\n return {\n id,\n role: 'user',\n text: textOf(message.content),\n createdAt: message.timestamp ?? 0,\n ...(delivered ? { delivered } : {}),\n };\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(\n entry: EntryRecord,\n out: Message[],\n calls: Map<string, ToolCall>,\n delivered: Delivered | undefined\n): 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, delivered));\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(\n view: ConversationView | undefined,\n deliveries: Readonly<Record<string, Delivered>> = {}\n): Message[] {\n const out: Message[] = [];\n if (!view) return out;\n const calls = new Map<string, ToolCall>();\n let previous: EntryRecord | undefined;\n for (const entry of view.entries) {\n const steered = previous?.kind === 'pi.tool-result' ? 'steer' : undefined;\n entryMessage(entry, out, calls, deliveries[String(entry.id)] ?? steered);\n previous = entry;\n }\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": "
|
|
4
|
+
"sourcesContent": ["import type {\n AssistantMessage,\n AssistantStatus,\n ErrorAction,\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';\nimport type { Delivered } from '../services.ts';\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 details?: { patch?: unknown };\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(args: Json | undefined): string {\n const subject = args?.command ?? args?.path;\n return typeof subject === 'string' ? subject : '';\n}\n\ninterface Replacement {\n oldText?: string;\n newText?: string;\n}\n\nconst GAP = '\u2026';\n\nfunction seconds(value: unknown): string | undefined {\n return typeof value === 'number' ? `timeout ${value}s` : undefined;\n}\n\nfunction lines(args: Json): string | undefined {\n const { offset, limit } = args as { offset?: number; limit?: number };\n if (typeof offset === 'number' && typeof limit === 'number')\n return `lines ${offset}\u2013${offset + limit - 1}`;\n if (typeof offset === 'number') return `from line ${offset}`;\n return typeof limit === 'number' ? `first ${limit} lines` : undefined;\n}\n\nfunction edits(args: Json): ToolCall['diff'] {\n const list = Array.isArray(args.edits) ? (args.edits as Replacement[]) : [];\n if (list.length === 0) return undefined;\n return {\n before: list.map((edit) => edit.oldText ?? '').join(`\\n${GAP}\\n`),\n after: list.map((edit) => edit.newText ?? '').join(`\\n${GAP}\\n`),\n };\n}\n\nexport function fromPatch(patch: string): ToolCall['diff'] {\n const before: string[] = [];\n const after: string[] = [];\n let hunks = 0;\n for (const line of patch.split('\\n')) {\n if (line.startsWith('@@')) {\n if (hunks++ > 0) {\n before.push(GAP);\n after.push(GAP);\n }\n continue;\n }\n if (hunks === 0 || line === '' || line.startsWith('\\\\')) continue;\n const text = line.slice(1);\n if (line.startsWith('-')) before.push(text);\n else if (line.startsWith('+')) after.push(text);\n else {\n before.push(text);\n after.push(text);\n }\n }\n return hunks ? { before: before.join('\\n'), after: after.join('\\n') } : undefined;\n}\n\nfunction shape(name: string, args: Json): Pick<ToolCall, 'input' | 'meta' | 'diff'> {\n const path = typeof args.path === 'string' ? args.path : '';\n const meta = name === 'bash' ? seconds(args.timeout) : name === 'read' ? lines(args) : undefined;\n const base = meta ? { meta } : {};\n if (name === 'bash' && typeof args.command === 'string') return { input: args.command, ...base };\n if (name === 'read' || name === 'write') return { input: path, ...base };\n if (name === 'edit') {\n const diff = edits(args);\n return { input: path, ...(diff ? { diff } : {}) };\n }\n return { input: JSON.stringify(args, null, 2) };\n}\n\nfunction toolCall(block: Block): ToolCall {\n const args = block.arguments ?? {};\n return {\n id: block.id ?? '',\n name: block.name ?? 'tool',\n title: title(block.arguments),\n ...shape(block.name ?? 'tool', args),\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\nconst credentials =\n /security token|unrecognizedclient|not authori[sz]ed|unauthori[sz]ed|forbidden|\\b40[13]\\b|api key|credential|bearer/i;\nconst models = /model/i;\nconst unavailable =\n /access|invalid|not found|identifier|unsupported|not supported|isn['\u2019]t supported|not available|isn['\u2019]t available/i;\n\nconst providerNames: Record<string, string> = { 'amazon-bedrock': 'Bedrock' };\nconst filtered =\n /Provider (?:stopped with|finish_reason): (?:content_filter(?:ed)?|guardrail_intervened|sensitive)|refused to complete the request/i;\nconst missing = /no api key|missing|not configured|no credentials/i;\n\nexport function errorPart(error: string, provider?: string): MessagePart {\n const action = errorAction(error);\n if (action === 'drop-turn') {\n return {\n type: 'error',\n message: \"The model's content filter stopped this reply.\",\n detail: error,\n action,\n };\n }\n if (action === 'settings' && provider === 'adobe') {\n return {\n type: 'error',\n message: 'Your Adobe session has expired.',\n detail: error,\n action: 'login',\n };\n }\n const name = providerNames[provider ?? ''] ?? 'The provider';\n const lead =\n action === 'settings'\n ? missing.test(error)\n ? `${name} needs an API key.`\n : `${name} rejected the API key.`\n : action === 'change-model'\n ? \"This model isn't available with the current account.\"\n : null;\n return lead\n ? { type: 'error', message: lead, detail: error, action }\n : { type: 'error', message: error, action };\n}\n\nexport function errorAction(message: string): ErrorAction {\n if (filtered.test(message)) return 'drop-turn';\n if (credentials.test(message)) return 'settings';\n if (models.test(message) && unavailable.test(message)) return 'change-model';\n return 'retry';\n}\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) parts.push(errorPart(message.errorMessage, message.provider));\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, delivered: Delivered | undefined): UserMessage {\n return {\n id,\n role: 'user',\n text: textOf(message.content),\n createdAt: message.timestamp ?? 0,\n ...(delivered ? { delivered } : {}),\n };\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 const patch = result.details?.patch;\n const diff = typeof patch === 'string' ? fromPatch(patch) : undefined;\n if (diff) call.diff = diff;\n else if (result.isError) delete call.diff;\n}\n\nconst rewound = (id: string): SystemMessage => ({\n id,\n role: 'system',\n kind: 'notice',\n title: 'Rewound 1 turn',\n text: 'Its prompt is back in the composer.',\n createdAt: 0,\n});\n\nfunction entryMessage(\n entry: EntryRecord,\n out: Message[],\n calls: Map<string, ToolCall>,\n delivered: Delivered | undefined\n): void {\n const id = `e${entry.id}`;\n const model = entry.model?.[0] as ModelMessage | undefined;\n if (entry.kind === 'slicc.rewound') out.push(rewound(id));\n else if (entry.kind === 'pi.user' && model) out.push(user(id, model, delivered));\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(\n view: ConversationView | undefined,\n deliveries: Readonly<Record<string, Delivered>> = {}\n): Message[] {\n const out: Message[] = [];\n if (!view) return out;\n const calls = new Map<string, ToolCall>();\n let previous: EntryRecord | undefined;\n for (const entry of view.entries) {\n const steered = previous?.kind === 'pi.tool-result' ? 'steer' : undefined;\n entryMessage(entry, out, calls, deliveries[String(entry.id)] ?? steered);\n previous = entry;\n }\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
6
|
"names": []
|
|
7
7
|
}
|
package/dist/types/agent.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Context } from '@earendil-works/chord';
|
|
2
2
|
import type { Models } from '@earendil-works/pi-ai/models';
|
|
3
3
|
import { type Conversation, Harness, type HarnessOptions, type ModelRef, type Registry, type Storage } from '@earendil-works/pi-durable';
|
|
4
|
+
import { type Cones } from './cone.ts';
|
|
4
5
|
export interface AgentOptions {
|
|
5
6
|
models: Models;
|
|
6
7
|
model: ModelRef;
|
|
@@ -10,9 +11,10 @@ export interface AgentOptions {
|
|
|
10
11
|
env?: HarnessOptions['env'];
|
|
11
12
|
context?: Context;
|
|
12
13
|
}
|
|
13
|
-
export interface Agent {
|
|
14
|
+
export interface Agent extends Cones {
|
|
14
15
|
readonly harness: Harness;
|
|
15
16
|
readonly root: Conversation;
|
|
17
|
+
onCone(listener: (conversation: Conversation) => void | Promise<void>): () => void;
|
|
16
18
|
prompt(text: string): Promise<string>;
|
|
17
19
|
close(): Promise<void>;
|
|
18
20
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Context } from '@earendil-works/chord';
|
|
2
|
+
import { type Conversation, type Harness } from '@earendil-works/pi-durable';
|
|
3
|
+
export declare const ConeDoc: import("@earendil-works/pi-durable").SessionDocToken<{
|
|
4
|
+
conversation: number | null;
|
|
5
|
+
}>;
|
|
6
|
+
export interface Cones {
|
|
7
|
+
cone(): Conversation;
|
|
8
|
+
switchCone(conversation: Conversation, context: Context): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
export declare function activeCone(harness: Harness, root: Conversation, context: Context): Promise<Conversation>;
|
|
11
|
+
export declare function pointCone(harness: Harness, conversation: Conversation, context: Context): Promise<void>;
|
package/dist/types/control.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Conversation, type Harness, type SubmissionId } from '@earendil-works/pi-durable';
|
|
2
|
+
import type { Cones } from './cone.ts';
|
|
2
3
|
import type { Deliveries } from './deliveries.ts';
|
|
3
4
|
import type { AgentControl } from './services.ts';
|
|
4
5
|
export declare function submissionId(value: string): SubmissionId | undefined;
|
|
5
|
-
export declare function createAgentControl(harness: Harness,
|
|
6
|
+
export declare function createAgentControl(harness: Harness, target: Conversation | Cones, deliveries?: Deliveries): AgentControl;
|
package/dist/types/services.d.ts
CHANGED
|
@@ -38,7 +38,17 @@ export interface AgentControl {
|
|
|
38
38
|
compact(instructions: string | null, context: Context): Promise<SendResponse>;
|
|
39
39
|
reset(handoff: string | null, context: Context): Promise<void>;
|
|
40
40
|
configure(change: AgentSettingsChange, context: Context): Promise<void>;
|
|
41
|
+
rewind(messageId: string | null, context: Context): Promise<Rewound>;
|
|
41
42
|
}
|
|
43
|
+
export type Rewound = {
|
|
44
|
+
done: true;
|
|
45
|
+
text: string;
|
|
46
|
+
reason: null;
|
|
47
|
+
} | {
|
|
48
|
+
done: false;
|
|
49
|
+
text: null;
|
|
50
|
+
reason: 'busy' | 'no-turn';
|
|
51
|
+
};
|
|
42
52
|
export interface AgentSettingsChange {
|
|
43
53
|
model: {
|
|
44
54
|
provider: string;
|
|
@@ -11,6 +11,7 @@ export declare class AgentAdapter extends Emitter<AgentEvents> implements AgentP
|
|
|
11
11
|
select(): void;
|
|
12
12
|
messages(): readonly Message[];
|
|
13
13
|
send(_agentId: string, input: string | Outgoing): Promise<void>;
|
|
14
|
+
rewind(_agentId: string, messageId: string): Promise<Outgoing | null>;
|
|
14
15
|
stop(): void;
|
|
15
16
|
busy(): boolean;
|
|
16
17
|
queue(): readonly UserMessage[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AssistantMessage, AssistantStatus, ErrorAction, Message, MessagePart, UserMessage } from '@ai-ecoverse/slicc-spectrum/ui';
|
|
1
|
+
import type { AssistantMessage, AssistantStatus, ErrorAction, Message, MessagePart, ToolCall, UserMessage } from '@ai-ecoverse/slicc-spectrum/ui';
|
|
2
2
|
import type { ConversationView } from '@earendil-works/pi-durable';
|
|
3
3
|
import type { Delivered } from '../services.ts';
|
|
4
4
|
type Json = Record<string, unknown>;
|
|
@@ -29,8 +29,12 @@ interface ModelMessage {
|
|
|
29
29
|
};
|
|
30
30
|
toolCallId?: string;
|
|
31
31
|
isError?: boolean;
|
|
32
|
+
details?: {
|
|
33
|
+
patch?: unknown;
|
|
34
|
+
};
|
|
32
35
|
}
|
|
33
36
|
export declare function textOf(content: ModelMessage['content']): string;
|
|
37
|
+
export declare function fromPatch(patch: string): ToolCall['diff'];
|
|
34
38
|
export declare function errorPart(error: string, provider?: string): MessagePart;
|
|
35
39
|
export declare function errorAction(message: string): ErrorAction;
|
|
36
40
|
export declare function assistant(id: string, message: ModelMessage, status?: AssistantStatus): AssistantMessage;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/slicc-agent",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "SLICC's agent core on pi durable: an agent worker, session storage, remoting and pi extensions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"@sqlite.org/sqlite-wasm": "3.53.4-build2"
|
|
85
85
|
},
|
|
86
86
|
"peerDependencies": {
|
|
87
|
-
"@ai-ecoverse/slicc-spectrum": "^1.
|
|
87
|
+
"@ai-ecoverse/slicc-spectrum": "^1.13.0"
|
|
88
88
|
},
|
|
89
89
|
"peerDependenciesMeta": {
|
|
90
90
|
"@ai-ecoverse/slicc-spectrum": {
|