@ai-ecoverse/slicc-agent 4.2.0 → 4.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -1
- package/dist/client.js +5 -2
- package/dist/client.js.map +2 -2
- package/dist/control.js +3 -2
- package/dist/control.js.map +2 -2
- package/dist/host.js +21 -9
- package/dist/host.js.map +3 -3
- package/dist/main.js +24 -10
- package/dist/main.js.map +2 -2
- package/dist/readonly.js +9 -2
- package/dist/readonly.js.map +2 -2
- package/dist/secrets/legacy.js +112 -0
- package/dist/secrets/legacy.js.map +7 -0
- package/dist/services.js.map +2 -2
- package/dist/settings.js +8 -2
- package/dist/settings.js.map +2 -2
- package/dist/spectrum/agent-port.js +31 -8
- package/dist/spectrum/agent-port.js.map +2 -2
- package/dist/spectrum/index.js +6 -0
- package/dist/spectrum/index.js.map +3 -3
- package/dist/spectrum/messages.js +18 -4
- package/dist/spectrum/messages.js.map +2 -2
- package/dist/spectrum/notices-port.js +42 -0
- package/dist/spectrum/notices-port.js.map +7 -0
- package/dist/titles/cluster.js +61 -0
- package/dist/titles/cluster.js.map +7 -0
- package/dist/titles/generate.js +33 -13
- package/dist/titles/generate.js.map +2 -2
- package/dist/titles/labels.js +218 -0
- package/dist/titles/labels.js.map +7 -0
- package/dist/titles/shown.js +12 -0
- package/dist/titles/shown.js.map +7 -0
- package/dist/types/client.d.ts +1 -0
- package/dist/types/control.d.ts +2 -1
- package/dist/types/host.d.ts +2 -0
- package/dist/types/secrets/legacy.d.ts +36 -0
- package/dist/types/services.d.ts +4 -0
- package/dist/types/settings.d.ts +2 -0
- package/dist/types/spectrum/agent-port.d.ts +1 -0
- package/dist/types/spectrum/index.d.ts +3 -0
- package/dist/types/spectrum/messages.d.ts +2 -0
- package/dist/types/spectrum/notices-port.d.ts +15 -0
- package/dist/types/titles/cluster.d.ts +15 -0
- package/dist/types/titles/generate.d.ts +13 -1
- package/dist/types/titles/labels.d.ts +33 -0
- package/dist/types/titles/shown.d.ts +2 -0
- package/package.json +4 -4
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { answerText } from "../agent.js";
|
|
2
|
+
const OUTPUT_LENGTH = 600;
|
|
3
|
+
function blocks(entry) {
|
|
4
|
+
const content = entry.model?.[0]?.content;
|
|
5
|
+
return entry.kind === "pi.assistant" && Array.isArray(content) ? content : [];
|
|
6
|
+
}
|
|
7
|
+
function callIdsOf(views) {
|
|
8
|
+
const ids = /* @__PURE__ */ new Set();
|
|
9
|
+
for (const view of views)
|
|
10
|
+
for (const entry of view.entries)
|
|
11
|
+
for (const block of blocks(entry))
|
|
12
|
+
if (block.type === "toolCall" && block.id) ids.add(block.id);
|
|
13
|
+
return ids;
|
|
14
|
+
}
|
|
15
|
+
function movedOn(entry) {
|
|
16
|
+
if (entry.kind === "pi.user") return true;
|
|
17
|
+
if (entry.kind !== "pi.assistant") return false;
|
|
18
|
+
const said = entry.model?.[0];
|
|
19
|
+
return said?.stopReason !== "toolUse" || blocks(entry).some((block) => block.type === "text" && block.text?.trim());
|
|
20
|
+
}
|
|
21
|
+
function clusterOf(entries, callIds) {
|
|
22
|
+
const calls = /* @__PURE__ */ new Map();
|
|
23
|
+
const results = /* @__PURE__ */ new Map();
|
|
24
|
+
entries.forEach((entry, index) => {
|
|
25
|
+
const said = entry.model?.[0];
|
|
26
|
+
for (const block of blocks(entry))
|
|
27
|
+
if (block.type === "toolCall" && block.id)
|
|
28
|
+
calls.set(block.id, {
|
|
29
|
+
name: block.name ?? "tool",
|
|
30
|
+
input: JSON.stringify(block.arguments ?? {}),
|
|
31
|
+
at: said?.timestamp ?? 0
|
|
32
|
+
});
|
|
33
|
+
if (entry.kind === "pi.tool-result" && said?.toolCallId)
|
|
34
|
+
results.set(said.toolCallId, { output: answerText(said.content), index });
|
|
35
|
+
});
|
|
36
|
+
const runs = [];
|
|
37
|
+
let at = 0;
|
|
38
|
+
let last = -1;
|
|
39
|
+
for (const id of callIds) {
|
|
40
|
+
const call = calls.get(id);
|
|
41
|
+
if (!call) return void 0;
|
|
42
|
+
const result = results.get(id);
|
|
43
|
+
runs.push({ name: call.name, input: call.input, output: result?.output ?? "", done: !!result });
|
|
44
|
+
at = Math.max(at, call.at);
|
|
45
|
+
last = Math.max(last, result?.index ?? Number.POSITIVE_INFINITY);
|
|
46
|
+
}
|
|
47
|
+
const settled = entries.slice(last + 1).some(movedOn);
|
|
48
|
+
return { runs, at, settled };
|
|
49
|
+
}
|
|
50
|
+
function describe(runs) {
|
|
51
|
+
return runs.map(
|
|
52
|
+
(run) => `${run.name}: ${run.input}
|
|
53
|
+
=> ${run.done ? run.output.slice(0, OUTPUT_LENGTH) : "(running)"}`
|
|
54
|
+
).join("\n\n");
|
|
55
|
+
}
|
|
56
|
+
export {
|
|
57
|
+
callIdsOf,
|
|
58
|
+
clusterOf,
|
|
59
|
+
describe
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=cluster.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/titles/cluster.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ConversationView, EntryRecord } from '@earendil-works/pi-durable';\nimport { answerText } from '../agent.ts';\n\nconst OUTPUT_LENGTH = 600;\n\nexport type Run = { name: string; input: string; output: string; done: boolean };\n\nexport type Cluster = { runs: Run[]; at: number; settled: boolean };\n\ntype Block = { type?: string; id?: string; name?: string; arguments?: unknown; text?: string };\n\ntype Said = {\n content?: unknown;\n toolCallId?: string;\n timestamp?: number;\n stopReason?: string;\n};\n\nfunction blocks(entry: EntryRecord): Block[] {\n const content = (entry.model?.[0] as Said | undefined)?.content;\n return entry.kind === 'pi.assistant' && Array.isArray(content) ? (content as Block[]) : [];\n}\n\nexport function callIdsOf(views: readonly ConversationView[]): Set<string> {\n const ids = new Set<string>();\n for (const view of views)\n for (const entry of view.entries)\n for (const block of blocks(entry))\n if (block.type === 'toolCall' && block.id) ids.add(block.id);\n return ids;\n}\n\nfunction movedOn(entry: EntryRecord): boolean {\n if (entry.kind === 'pi.user') return true;\n if (entry.kind !== 'pi.assistant') return false;\n const said = entry.model?.[0] as Said | undefined;\n return (\n said?.stopReason !== 'toolUse' ||\n blocks(entry).some((block) => block.type === 'text' && block.text?.trim())\n );\n}\n\nexport function clusterOf(\n entries: readonly EntryRecord[],\n callIds: readonly string[]\n): Cluster | undefined {\n const calls = new Map<string, Pick<Run, 'name' | 'input'> & { at: number }>();\n const results = new Map<string, { output: string; index: number }>();\n entries.forEach((entry, index) => {\n const said = entry.model?.[0] as Said | undefined;\n for (const block of blocks(entry))\n if (block.type === 'toolCall' && block.id)\n calls.set(block.id, {\n name: block.name ?? 'tool',\n input: JSON.stringify(block.arguments ?? {}),\n at: said?.timestamp ?? 0,\n });\n if (entry.kind === 'pi.tool-result' && said?.toolCallId)\n results.set(said.toolCallId, { output: answerText(said.content), index });\n });\n const runs: Run[] = [];\n let at = 0;\n let last = -1;\n for (const id of callIds) {\n const call = calls.get(id);\n if (!call) return undefined;\n const result = results.get(id);\n runs.push({ name: call.name, input: call.input, output: result?.output ?? '', done: !!result });\n at = Math.max(at, call.at);\n last = Math.max(last, result?.index ?? Number.POSITIVE_INFINITY);\n }\n const settled = entries.slice(last + 1).some(movedOn);\n return { runs, at, settled };\n}\n\nexport function describe(runs: readonly Run[]): string {\n return runs\n .map(\n (run) =>\n `${run.name}: ${run.input}\\n=> ${run.done ? run.output.slice(0, OUTPUT_LENGTH) : '(running)'}`\n )\n .join('\\n\\n');\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,kBAAkB;AAE3B,MAAM,gBAAgB;AAetB,SAAS,OAAO,OAA6B;AAC3C,QAAM,UAAW,MAAM,QAAQ,CAAC,GAAwB;AACxD,SAAO,MAAM,SAAS,kBAAkB,MAAM,QAAQ,OAAO,IAAK,UAAsB,CAAC;AAC3F;AAEO,SAAS,UAAU,OAAiD;AACzE,QAAM,MAAM,oBAAI,IAAY;AAC5B,aAAW,QAAQ;AACjB,eAAW,SAAS,KAAK;AACvB,iBAAW,SAAS,OAAO,KAAK;AAC9B,YAAI,MAAM,SAAS,cAAc,MAAM,GAAI,KAAI,IAAI,MAAM,EAAE;AACjE,SAAO;AACT;AAEA,SAAS,QAAQ,OAA6B;AAC5C,MAAI,MAAM,SAAS,UAAW,QAAO;AACrC,MAAI,MAAM,SAAS,eAAgB,QAAO;AAC1C,QAAM,OAAO,MAAM,QAAQ,CAAC;AAC5B,SACE,MAAM,eAAe,aACrB,OAAO,KAAK,EAAE,KAAK,CAAC,UAAU,MAAM,SAAS,UAAU,MAAM,MAAM,KAAK,CAAC;AAE7E;AAEO,SAAS,UACd,SACA,SACqB;AACrB,QAAM,QAAQ,oBAAI,IAA0D;AAC5E,QAAM,UAAU,oBAAI,IAA+C;AACnE,UAAQ,QAAQ,CAAC,OAAO,UAAU;AAChC,UAAM,OAAO,MAAM,QAAQ,CAAC;AAC5B,eAAW,SAAS,OAAO,KAAK;AAC9B,UAAI,MAAM,SAAS,cAAc,MAAM;AACrC,cAAM,IAAI,MAAM,IAAI;AAAA,UAClB,MAAM,MAAM,QAAQ;AAAA,UACpB,OAAO,KAAK,UAAU,MAAM,aAAa,CAAC,CAAC;AAAA,UAC3C,IAAI,MAAM,aAAa;AAAA,QACzB,CAAC;AACL,QAAI,MAAM,SAAS,oBAAoB,MAAM;AAC3C,cAAQ,IAAI,KAAK,YAAY,EAAE,QAAQ,WAAW,KAAK,OAAO,GAAG,MAAM,CAAC;AAAA,EAC5E,CAAC;AACD,QAAM,OAAc,CAAC;AACrB,MAAI,KAAK;AACT,MAAI,OAAO;AACX,aAAW,MAAM,SAAS;AACxB,UAAM,OAAO,MAAM,IAAI,EAAE;AACzB,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,SAAS,QAAQ,IAAI,EAAE;AAC7B,SAAK,KAAK,EAAE,MAAM,KAAK,MAAM,OAAO,KAAK,OAAO,QAAQ,QAAQ,UAAU,IAAI,MAAM,CAAC,CAAC,OAAO,CAAC;AAC9F,SAAK,KAAK,IAAI,IAAI,KAAK,EAAE;AACzB,WAAO,KAAK,IAAI,MAAM,QAAQ,SAAS,OAAO,iBAAiB;AAAA,EACjE;AACA,QAAM,UAAU,QAAQ,MAAM,OAAO,CAAC,EAAE,KAAK,OAAO;AACpD,SAAO,EAAE,MAAM,IAAI,QAAQ;AAC7B;AAEO,SAAS,SAAS,MAA8B;AACrD,SAAO,KACJ;AAAA,IACC,CAAC,QACC,GAAG,IAAI,IAAI,KAAK,IAAI,KAAK;AAAA,KAAQ,IAAI,OAAO,IAAI,OAAO,MAAM,GAAG,aAAa,IAAI,WAAW;AAAA,EAChG,EACC,KAAK,MAAM;AAChB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/titles/generate.js
CHANGED
|
@@ -3,14 +3,21 @@ import { extractSettings } from "../memory/extract.js";
|
|
|
3
3
|
import { redactSecrets } from "../memory/format.js";
|
|
4
4
|
import { scrubText } from "../secrets/scrub.js";
|
|
5
5
|
const TITLE_PROMPT = "Give this chat a title of three to six words that says what it is about, in the language the user writes in. Answer with the title only, without quotes.";
|
|
6
|
+
const LABEL_PROMPT = 'Label this group of tool runs in three to eight words, as an imperative that says what they do, such as "Check the failing build logs". Answer with the label only, without quotes.';
|
|
6
7
|
const TRANSCRIPT_LENGTH = 4e3;
|
|
7
|
-
function
|
|
8
|
+
function settingOn(text, key) {
|
|
8
9
|
try {
|
|
9
|
-
return JSON.parse(text ?? "{}")
|
|
10
|
+
return JSON.parse(text ?? "{}")[key] !== false;
|
|
10
11
|
} catch {
|
|
11
12
|
return true;
|
|
12
13
|
}
|
|
13
14
|
}
|
|
15
|
+
function titlesOn(text) {
|
|
16
|
+
return settingOn(text, "titles");
|
|
17
|
+
}
|
|
18
|
+
function labelsOn(text) {
|
|
19
|
+
return settingOn(text, "toolLabels");
|
|
20
|
+
}
|
|
14
21
|
function settingsText(env, home, dir = `${home}/.pi/agent`) {
|
|
15
22
|
return async (context) => {
|
|
16
23
|
const read = await env.readTextFile(`${dir}/settings.json`, context);
|
|
@@ -23,44 +30,57 @@ function textOf(entry) {
|
|
|
23
30
|
function transcriptOf(entries) {
|
|
24
31
|
return entries.filter((entry) => entry.kind === "pi.user" || entry.kind === "pi.assistant").map((entry) => `${entry.kind === "pi.user" ? "User" : "Agent"}: ${textOf(entry)}`).join("\n");
|
|
25
32
|
}
|
|
26
|
-
async function
|
|
27
|
-
const raw = transcriptOf(entries);
|
|
33
|
+
async function scrubbed(raw) {
|
|
28
34
|
const redacted = redactSecrets(raw).text;
|
|
29
|
-
|
|
30
|
-
return scrubbed.slice(0, TRANSCRIPT_LENGTH);
|
|
35
|
+
return (await scrubText(redacted)).slice(0, TRANSCRIPT_LENGTH);
|
|
31
36
|
}
|
|
32
|
-
function
|
|
33
|
-
return
|
|
37
|
+
function scrubTranscript(entries) {
|
|
38
|
+
return scrubbed(transcriptOf(entries));
|
|
39
|
+
}
|
|
40
|
+
function smallModel(models, settings, key, prompt, max) {
|
|
41
|
+
return async (transcript, model, context, admit = () => true) => {
|
|
34
42
|
const text = await settings(context);
|
|
35
|
-
if (!
|
|
43
|
+
if (!settingOn(text, key)) return void 0;
|
|
36
44
|
const chosen = extractSettings(text).model;
|
|
37
45
|
const [provider, ...rest] = model.split("/");
|
|
38
46
|
const ref = chosen ?? (provider && rest.length ? { provider, modelId: rest.join("/") } : null);
|
|
39
47
|
const found = ref ? models.getModel(ref.provider, ref.modelId) : void 0;
|
|
40
|
-
if (!found || !transcript) return void 0;
|
|
48
|
+
if (!found || !transcript || !admit()) return void 0;
|
|
41
49
|
const message = await models.completeSimple(
|
|
42
50
|
found,
|
|
43
51
|
{
|
|
44
52
|
messages: [
|
|
45
53
|
{
|
|
46
54
|
role: "user",
|
|
47
|
-
content: [{ type: "text", text: `${
|
|
55
|
+
content: [{ type: "text", text: `${prompt}
|
|
48
56
|
|
|
49
57
|
${transcript}` }],
|
|
50
58
|
timestamp: Date.now()
|
|
51
59
|
}
|
|
52
60
|
]
|
|
53
61
|
},
|
|
54
|
-
{ maxTokens:
|
|
62
|
+
{ maxTokens: max }
|
|
55
63
|
);
|
|
56
64
|
if (message.stopReason === "error") return void 0;
|
|
57
|
-
|
|
65
|
+
const said = message.content.map((part) => part.type === "text" ? part.text : "").join("").trim();
|
|
66
|
+
return { text: said, answer: message };
|
|
58
67
|
};
|
|
59
68
|
}
|
|
69
|
+
function titler(models, settings) {
|
|
70
|
+
const ask = smallModel(models, settings, "titles", TITLE_PROMPT, 40);
|
|
71
|
+
return async (transcript, model, context) => (await ask(transcript, model, context))?.text;
|
|
72
|
+
}
|
|
73
|
+
function labeler(models, settings) {
|
|
74
|
+
return smallModel(models, settings, "toolLabels", LABEL_PROMPT, 40);
|
|
75
|
+
}
|
|
60
76
|
export {
|
|
77
|
+
LABEL_PROMPT,
|
|
61
78
|
TITLE_PROMPT,
|
|
62
79
|
TRANSCRIPT_LENGTH,
|
|
80
|
+
labeler,
|
|
81
|
+
labelsOn,
|
|
63
82
|
scrubTranscript,
|
|
83
|
+
scrubbed,
|
|
64
84
|
settingsText,
|
|
65
85
|
titler,
|
|
66
86
|
titlesOn,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/titles/generate.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { Message } from '@earendil-works/pi-ai';\nimport type { Models } from '@earendil-works/pi-ai/models';\nimport type { EntryRecord } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { answerText } from '../agent.ts';\nimport { extractSettings } from '../memory/extract.ts';\nimport { redactSecrets } from '../memory/format.ts';\nimport { scrubText } from '../secrets/scrub.ts';\n\nexport const TITLE_PROMPT =\n 'Give this chat a title of three to six words that says what it is about, in the language the user writes in. Answer with the title only, without quotes.';\n\nexport const TRANSCRIPT_LENGTH = 4000;\n\
|
|
5
|
-
"mappings": "AAKA,SAAS,kBAAkB;AAC3B,SAAS,uBAAuB;AAChC,SAAS,qBAAqB;AAC9B,SAAS,iBAAiB;AAEnB,MAAM,eACX;AAEK,MAAM,oBAAoB;
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { AssistantMessage, Message } from '@earendil-works/pi-ai';\nimport type { Models } from '@earendil-works/pi-ai/models';\nimport type { EntryRecord } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport { answerText } from '../agent.ts';\nimport { extractSettings } from '../memory/extract.ts';\nimport { redactSecrets } from '../memory/format.ts';\nimport { scrubText } from '../secrets/scrub.ts';\n\nexport const TITLE_PROMPT =\n 'Give this chat a title of three to six words that says what it is about, in the language the user writes in. Answer with the title only, without quotes.';\n\nexport const LABEL_PROMPT =\n 'Label this group of tool runs in three to eight words, as an imperative that says what they do, such as \"Check the failing build logs\". Answer with the label only, without quotes.';\n\nexport const TRANSCRIPT_LENGTH = 4000;\n\ntype Switch = 'titles' | 'toolLabels';\n\nfunction settingOn(text: string | undefined, key: Switch): boolean {\n try {\n return (JSON.parse(text ?? '{}') as Record<string, unknown>)[key] !== false;\n } catch {\n return true;\n }\n}\n\nexport function titlesOn(text: string | undefined): boolean {\n return settingOn(text, 'titles');\n}\n\nexport function labelsOn(text: string | undefined): boolean {\n return settingOn(text, 'toolLabels');\n}\n\nexport function settingsText(\n env: Pick<ExecutionEnv, 'readTextFile'>,\n home: string,\n dir = `${home}/.pi/agent`\n): (context: Context) => Promise<string | undefined> {\n return async (context) => {\n const read = await env.readTextFile(`${dir}/settings.json`, context);\n return read.ok ? read.value : undefined;\n };\n}\n\nfunction textOf(entry: EntryRecord): string {\n return answerText(entry.model?.[0]?.content).trim();\n}\n\nexport function transcriptOf(entries: readonly EntryRecord[]): string {\n return entries\n .filter((entry) => entry.kind === 'pi.user' || entry.kind === 'pi.assistant')\n .map((entry) => `${entry.kind === 'pi.user' ? 'User' : 'Agent'}: ${textOf(entry)}`)\n .join('\\n');\n}\n\nexport async function scrubbed(raw: string): Promise<string> {\n const redacted = redactSecrets(raw).text;\n return (await scrubText(redacted)).slice(0, TRANSCRIPT_LENGTH);\n}\n\nexport function scrubTranscript(entries: readonly EntryRecord[]): Promise<string> {\n return scrubbed(transcriptOf(entries));\n}\n\ntype Settings = (context: Context) => Promise<string | undefined>;\n\nexport type Asked = { text: string; answer: AssistantMessage };\n\nfunction smallModel(models: Models, settings: Settings, key: Switch, prompt: string, max: number) {\n return async (\n transcript: string,\n model: string,\n context: Context,\n admit: () => boolean = () => true\n ): Promise<Asked | undefined> => {\n const text = await settings(context);\n if (!settingOn(text, key)) return undefined;\n const chosen = extractSettings(text).model;\n const [provider, ...rest] = model.split('/');\n const ref = chosen ?? (provider && rest.length ? { provider, modelId: rest.join('/') } : null);\n const found = ref ? models.getModel(ref.provider, ref.modelId) : undefined;\n if (!found || !transcript || !admit()) return undefined;\n const message = await models.completeSimple(\n found,\n {\n messages: [\n {\n role: 'user',\n content: [{ type: 'text', text: `${prompt}\\n\\n${transcript}` }],\n timestamp: Date.now(),\n } as Message,\n ],\n },\n { maxTokens: max }\n );\n if (message.stopReason === 'error') return undefined;\n const said = message.content\n .map((part) => (part.type === 'text' ? part.text : ''))\n .join('')\n .trim();\n return { text: said, answer: message };\n };\n}\n\nexport function titler(models: Models, settings: Settings) {\n const ask = smallModel(models, settings, 'titles', TITLE_PROMPT, 40);\n return async (transcript: string, model: string, context: Context) =>\n (await ask(transcript, model, context))?.text;\n}\n\nexport function labeler(models: Models, settings: Settings) {\n return smallModel(models, settings, 'toolLabels', LABEL_PROMPT, 40);\n}\n\nexport type Labeler = ReturnType<typeof labeler>;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { replicatedState } from "@earendil-works/chord";
|
|
2
|
+
import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
|
|
3
|
+
import {
|
|
4
|
+
defineDoc,
|
|
5
|
+
UsageDoc
|
|
6
|
+
} from "@earendil-works/pi-durable";
|
|
7
|
+
import { vaultHold } from "../secrets/view.js";
|
|
8
|
+
import { callIdsOf, clusterOf, describe } from "./cluster.js";
|
|
9
|
+
import { scrubbed } from "./generate.js";
|
|
10
|
+
import { labelKey, shownLabel } from "./shown.js";
|
|
11
|
+
const LabelsDoc = defineDoc({
|
|
12
|
+
kind: "slicc.labels",
|
|
13
|
+
version: 1,
|
|
14
|
+
scope: "session",
|
|
15
|
+
initial: () => ({ labels: {} })
|
|
16
|
+
});
|
|
17
|
+
const LABELS_KEPT = 500;
|
|
18
|
+
const LABELS_PER_MINUTE = 6;
|
|
19
|
+
const MINUTE = 6e4;
|
|
20
|
+
const LABEL_WORDS = 8;
|
|
21
|
+
const LABEL_LENGTH = 80;
|
|
22
|
+
const RESERVED = 2;
|
|
23
|
+
const POLL_MS = 1e3;
|
|
24
|
+
const SETTLE_MS = 6e5;
|
|
25
|
+
const LABEL_WAIT_MS = 3e4;
|
|
26
|
+
function visibleLabels(labels, sources, views) {
|
|
27
|
+
const pick = () => {
|
|
28
|
+
const ids = callIdsOf(views());
|
|
29
|
+
return Object.fromEntries(
|
|
30
|
+
Object.entries(labels.value).filter(
|
|
31
|
+
([key]) => key.split(" ").every((id) => ids.has(id))
|
|
32
|
+
)
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
const state = replicatedState(pick());
|
|
36
|
+
let queued = false;
|
|
37
|
+
const update = () => {
|
|
38
|
+
if (queued) return;
|
|
39
|
+
queued = true;
|
|
40
|
+
queueMicrotask(() => {
|
|
41
|
+
queued = false;
|
|
42
|
+
const next = pick();
|
|
43
|
+
const before = state.value;
|
|
44
|
+
const keys = Object.keys(next);
|
|
45
|
+
if (keys.length !== Object.keys(before).length || keys.some((key) => before[key] !== next[key]))
|
|
46
|
+
state.replace(BACKGROUND_CONTEXT, next);
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
const offs = [labels, ...sources].map((source) => source.subscribe(update));
|
|
50
|
+
return {
|
|
51
|
+
state,
|
|
52
|
+
dispose() {
|
|
53
|
+
for (const off of offs) off();
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function watchLabel(labels, waitMs = LABEL_WAIT_MS) {
|
|
58
|
+
return (_agentId, callIds) => {
|
|
59
|
+
const found = shownLabel(labels.value, callIds);
|
|
60
|
+
if (found !== void 0 || !callIds.length) return Promise.resolve(found ?? null);
|
|
61
|
+
return new Promise((resolve) => {
|
|
62
|
+
const done = (label) => {
|
|
63
|
+
clearTimeout(timer);
|
|
64
|
+
off();
|
|
65
|
+
resolve(label);
|
|
66
|
+
};
|
|
67
|
+
const timer = setTimeout(() => done(null), waitMs);
|
|
68
|
+
const off = labels.subscribe(() => {
|
|
69
|
+
const label = shownLabel(labels.value, callIds);
|
|
70
|
+
if (label !== void 0) done(label);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function cleanLabel(text) {
|
|
76
|
+
return text.replace(/\s+/g, " ").trim().replace(/^["'“”]+|["'“”]+$/g, "").replace(/\.+$/, "").trim().split(" ").slice(0, LABEL_WORDS).join(" ").slice(0, LABEL_LENGTH);
|
|
77
|
+
}
|
|
78
|
+
async function spend(tx, conversation, answer) {
|
|
79
|
+
const models = (await tx.doc(UsageDoc, conversation)).models;
|
|
80
|
+
const key = `${answer.provider}/${answer.model}`;
|
|
81
|
+
const usage = answer.usage;
|
|
82
|
+
const total = Object.hasOwn(models, key) ? models[key] : void 0;
|
|
83
|
+
if (!total) {
|
|
84
|
+
models[key] = JSON.parse(JSON.stringify(usage));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
for (const field of ["input", "output", "cacheRead", "cacheWrite", "totalTokens"])
|
|
88
|
+
total[field] += usage[field];
|
|
89
|
+
for (const field of ["input", "output", "cacheRead", "cacheWrite", "total"])
|
|
90
|
+
total.cost[field] += usage.cost[field];
|
|
91
|
+
}
|
|
92
|
+
function modelName(model) {
|
|
93
|
+
return model ? `${model.provider}/${model.modelId}` : "";
|
|
94
|
+
}
|
|
95
|
+
async function attachLabels(options, context = BACKGROUND_CONTEXT) {
|
|
96
|
+
const { harness, agents } = options;
|
|
97
|
+
const now = options.now ?? Date.now;
|
|
98
|
+
const started = now();
|
|
99
|
+
const cap = options.perMinute ?? LABELS_PER_MINUTE;
|
|
100
|
+
const loaded = await harness.commit(
|
|
101
|
+
async (tx) => ({ ...(await tx.doc(LabelsDoc)).labels }),
|
|
102
|
+
context
|
|
103
|
+
);
|
|
104
|
+
const labels = replicatedState(loaded);
|
|
105
|
+
const flying = /* @__PURE__ */ new Map();
|
|
106
|
+
const waiting = /* @__PURE__ */ new Set();
|
|
107
|
+
const spent = /* @__PURE__ */ new Map();
|
|
108
|
+
const pollMs = options.pollMs ?? POLL_MS;
|
|
109
|
+
const rounds = Math.ceil((options.settleMs ?? SETTLE_MS) / pollMs);
|
|
110
|
+
const sleep = () => new Promise((resolve) => setTimeout(resolve, pollMs));
|
|
111
|
+
const used = (agentId) => {
|
|
112
|
+
const at = now();
|
|
113
|
+
const recent = (spent.get(agentId) ?? []).filter((time) => at - time < MINUTE);
|
|
114
|
+
spent.set(agentId, recent);
|
|
115
|
+
return recent;
|
|
116
|
+
};
|
|
117
|
+
const admit = (agentId, settle) => () => {
|
|
118
|
+
const recent = used(agentId);
|
|
119
|
+
if (recent.length >= (settle ? cap : cap - RESERVED)) return false;
|
|
120
|
+
recent.push(now());
|
|
121
|
+
return true;
|
|
122
|
+
};
|
|
123
|
+
const room = async (agentId) => {
|
|
124
|
+
for (let round = 0; round < rounds && used(agentId).length >= cap; round++) await sleep();
|
|
125
|
+
};
|
|
126
|
+
const keep = (key, label, conversation, answer) => harness.commit(async (tx) => {
|
|
127
|
+
const doc = await tx.doc(LabelsDoc);
|
|
128
|
+
doc.labels[key] = label;
|
|
129
|
+
const keys = Object.keys(doc.labels);
|
|
130
|
+
for (const old of keys.slice(0, Math.max(0, keys.length - LABELS_KEPT)))
|
|
131
|
+
delete doc.labels[old];
|
|
132
|
+
await spend(tx, conversation, answer);
|
|
133
|
+
return { ...doc.labels };
|
|
134
|
+
}, BACKGROUND_CONTEXT);
|
|
135
|
+
const read = async (agentId, callIds) => {
|
|
136
|
+
const conversation = await agents.conversation(agentId, BACKGROUND_CONTEXT);
|
|
137
|
+
if (!conversation) return void 0;
|
|
138
|
+
const cluster = clusterOf((await conversation.context(BACKGROUND_CONTEXT)).entries, callIds);
|
|
139
|
+
return cluster ? { conversation, cluster } : void 0;
|
|
140
|
+
};
|
|
141
|
+
const ask = async (agentId, key, found, settle) => {
|
|
142
|
+
if (vaultHold(options.vault?.value)) return null;
|
|
143
|
+
if (settle) await room(agentId);
|
|
144
|
+
const text = await scrubbed(describe(found.cluster.runs));
|
|
145
|
+
const model = modelName((await found.conversation.agent(BACKGROUND_CONTEXT)).model);
|
|
146
|
+
const asked = await options.label(text, model, BACKGROUND_CONTEXT, admit(agentId, settle));
|
|
147
|
+
if (!asked) return null;
|
|
148
|
+
const label = cleanLabel(asked.text);
|
|
149
|
+
labels.replace(BACKGROUND_CONTEXT, await keep(key, label, found.conversation.id, asked.answer));
|
|
150
|
+
return label || null;
|
|
151
|
+
};
|
|
152
|
+
const longer = (key) => [...flying.keys(), ...waiting, ...Object.keys(labels.value)].some(
|
|
153
|
+
(other) => other.startsWith(`${key} `)
|
|
154
|
+
);
|
|
155
|
+
const prefixOf = (callIds) => {
|
|
156
|
+
for (let size = callIds.length - 1; size > 0; size--) {
|
|
157
|
+
const part = callIds.slice(0, size);
|
|
158
|
+
const key = labelKey(part);
|
|
159
|
+
const shown = flying.get(key) ?? shownLabel(labels.value, part);
|
|
160
|
+
if (shown !== void 0) return Promise.resolve(shown);
|
|
161
|
+
if (waiting.has(key)) return Promise.resolve(null);
|
|
162
|
+
}
|
|
163
|
+
return void 0;
|
|
164
|
+
};
|
|
165
|
+
const settled = async (agentId, callIds, key) => {
|
|
166
|
+
for (let round = 0; round < rounds; round++) {
|
|
167
|
+
await sleep();
|
|
168
|
+
if (longer(key)) return void 0;
|
|
169
|
+
const found = await read(agentId, callIds);
|
|
170
|
+
if (found?.cluster.settled) return found;
|
|
171
|
+
}
|
|
172
|
+
return void 0;
|
|
173
|
+
};
|
|
174
|
+
const relabel = async (agentId, callIds, key) => {
|
|
175
|
+
const found = await settled(agentId, callIds, key);
|
|
176
|
+
return found ? ask(agentId, key, found, true) : void 0;
|
|
177
|
+
};
|
|
178
|
+
const make = async (agentId, callIds, key) => {
|
|
179
|
+
const found = await read(agentId, callIds);
|
|
180
|
+
if (!found || found.cluster.at < started) return null;
|
|
181
|
+
const prefix = prefixOf(callIds);
|
|
182
|
+
if (longer(key)) return await prefix ?? null;
|
|
183
|
+
if (found.cluster.settled) return ask(agentId, key, found, prefix !== void 0);
|
|
184
|
+
if (prefix !== void 0) {
|
|
185
|
+
const shown = await prefix;
|
|
186
|
+
return await relabel(agentId, callIds, key) ?? shown;
|
|
187
|
+
}
|
|
188
|
+
const first = await ask(agentId, key, found, false);
|
|
189
|
+
waiting.add(key);
|
|
190
|
+
void relabel(agentId, callIds, key).catch(() => void 0).finally(() => waiting.delete(key));
|
|
191
|
+
return first;
|
|
192
|
+
};
|
|
193
|
+
return {
|
|
194
|
+
labels,
|
|
195
|
+
label(agentId, callIds, _context) {
|
|
196
|
+
if (!callIds.length) return Promise.resolve(null);
|
|
197
|
+
const key = labelKey(callIds);
|
|
198
|
+
const found = shownLabel(labels.value, callIds);
|
|
199
|
+
if (found !== void 0) return Promise.resolve(found);
|
|
200
|
+
const known = flying.get(key);
|
|
201
|
+
if (known !== void 0) return known;
|
|
202
|
+
const made = make(agentId, callIds, key).catch(() => null).finally(() => flying.delete(key));
|
|
203
|
+
flying.set(key, made);
|
|
204
|
+
return made;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
export {
|
|
209
|
+
LABELS_KEPT,
|
|
210
|
+
LABELS_PER_MINUTE,
|
|
211
|
+
LABEL_WAIT_MS,
|
|
212
|
+
LabelsDoc,
|
|
213
|
+
attachLabels,
|
|
214
|
+
cleanLabel,
|
|
215
|
+
visibleLabels,
|
|
216
|
+
watchLabel
|
|
217
|
+
};
|
|
218
|
+
//# sourceMappingURL=labels.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/titles/labels.ts"],
|
|
4
|
+
"sourcesContent": ["import { type Context, type ReplicatedState, replicatedState } from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { AssistantMessage, Usage } from '@earendil-works/pi-ai';\nimport {\n type ConversationId,\n type ConversationView,\n defineDoc,\n type Harness,\n type Tx,\n UsageDoc,\n} from '@earendil-works/pi-durable';\nimport type { Agents } from '../agents.ts';\nimport type { VaultState } from '../secrets/vault.ts';\nimport { vaultHold } from '../secrets/view.ts';\nimport { callIdsOf, clusterOf, describe } from './cluster.ts';\nimport { type Labeler, scrubbed } from './generate.ts';\nimport { labelKey, shownLabel } from './shown.ts';\n\nexport type LabelsState = { labels: Record<string, string> };\n\nexport const LabelsDoc = defineDoc<LabelsState>({\n kind: 'slicc.labels',\n version: 1,\n scope: 'session',\n initial: () => ({ labels: {} }),\n});\n\nexport const LABELS_KEPT = 500;\nexport const LABELS_PER_MINUTE = 6;\nconst MINUTE = 60_000;\nconst LABEL_WORDS = 8;\nconst LABEL_LENGTH = 80;\nconst RESERVED = 2;\nconst POLL_MS = 1000;\nconst SETTLE_MS = 600_000;\n\nexport const LABEL_WAIT_MS = 30_000;\n\nexport function visibleLabels(\n labels: ReplicatedState<Record<string, string>>,\n sources: readonly ReplicatedState<unknown>[],\n views: () => readonly ConversationView[]\n) {\n const pick = () => {\n const ids = callIdsOf(views());\n return Object.fromEntries(\n Object.entries(labels.value as Record<string, string>).filter(([key]) =>\n key.split(' ').every((id) => ids.has(id))\n )\n );\n };\n const state = replicatedState<Record<string, string>>(pick());\n let queued = false;\n const update = () => {\n if (queued) return;\n queued = true;\n queueMicrotask(() => {\n queued = false;\n const next = pick();\n const before = state.value;\n const keys = Object.keys(next);\n if (\n keys.length !== Object.keys(before).length ||\n keys.some((key) => before[key] !== next[key])\n )\n state.replace(BACKGROUND_CONTEXT, next);\n });\n };\n const offs = [labels, ...sources].map((source) => source.subscribe(update));\n return {\n state,\n dispose() {\n for (const off of offs) off();\n },\n };\n}\n\nexport function watchLabel(\n labels: ReplicatedState<Record<string, string>>,\n waitMs = LABEL_WAIT_MS\n) {\n return (_agentId: string, callIds: readonly string[]): Promise<string | null> => {\n const found = shownLabel(labels.value, callIds);\n if (found !== undefined || !callIds.length) return Promise.resolve(found ?? null);\n return new Promise((resolve) => {\n const done = (label: string | null) => {\n clearTimeout(timer);\n off();\n resolve(label);\n };\n const timer = setTimeout(() => done(null), waitMs);\n const off = labels.subscribe(() => {\n const label = shownLabel(labels.value, callIds);\n if (label !== undefined) done(label);\n });\n });\n };\n}\n\nexport function cleanLabel(text: string): string {\n return text\n .replace(/\\s+/g, ' ')\n .trim()\n .replace(/^[\"'\u201C\u201D]+|[\"'\u201C\u201D]+$/g, '')\n .replace(/\\.+$/, '')\n .trim()\n .split(' ')\n .slice(0, LABEL_WORDS)\n .join(' ')\n .slice(0, LABEL_LENGTH);\n}\n\ntype Counted = Record<'input' | 'output' | 'cacheRead' | 'cacheWrite' | 'totalTokens', number> & {\n cost: Record<'input' | 'output' | 'cacheRead' | 'cacheWrite' | 'total', number>;\n};\n\nasync function spend(tx: Tx, conversation: ConversationId, answer: AssistantMessage) {\n const models = (await tx.doc(UsageDoc, conversation)).models as unknown as Record<\n string,\n Counted\n >;\n const key = `${answer.provider}/${answer.model}`;\n const usage = answer.usage as Usage;\n const total = Object.hasOwn(models, key) ? models[key] : undefined;\n if (!total) {\n models[key] = JSON.parse(JSON.stringify(usage)) as Counted;\n return;\n }\n for (const field of ['input', 'output', 'cacheRead', 'cacheWrite', 'totalTokens'] as const)\n total[field] += usage[field];\n for (const field of ['input', 'output', 'cacheRead', 'cacheWrite', 'total'] as const)\n total.cost[field] += usage.cost[field];\n}\n\nexport interface LabelsAttach {\n harness: Pick<Harness, 'commit'>;\n agents: Pick<Agents, 'conversation'>;\n label: Labeler;\n vault?: Pick<ReplicatedState<VaultState>, 'value'>;\n now?: () => number;\n perMinute?: number;\n pollMs?: number;\n settleMs?: number;\n}\n\nexport interface LabelsRuntime {\n labels: ReplicatedState<Record<string, string>>;\n label(agentId: string, callIds: readonly string[], context: Context): Promise<string | null>;\n}\n\nfunction modelName(model: { provider: string; modelId: string } | undefined): string {\n return model ? `${model.provider}/${model.modelId}` : '';\n}\n\nexport async function attachLabels(\n options: LabelsAttach,\n context: Context = BACKGROUND_CONTEXT\n): Promise<LabelsRuntime> {\n const { harness, agents } = options;\n const now = options.now ?? Date.now;\n const started = now();\n const cap = options.perMinute ?? LABELS_PER_MINUTE;\n const loaded = await harness.commit(\n async (tx) => ({ ...(await tx.doc(LabelsDoc)).labels }),\n context\n );\n const labels = replicatedState<Record<string, string>>(loaded);\n const flying = new Map<string, Promise<string | null>>();\n const waiting = new Set<string>();\n const spent = new Map<string, number[]>();\n const pollMs = options.pollMs ?? POLL_MS;\n const rounds = Math.ceil((options.settleMs ?? SETTLE_MS) / pollMs);\n const sleep = () => new Promise((resolve) => setTimeout(resolve, pollMs));\n const used = (agentId: string) => {\n const at = now();\n const recent = (spent.get(agentId) ?? []).filter((time) => at - time < MINUTE);\n spent.set(agentId, recent);\n return recent;\n };\n const admit = (agentId: string, settle: boolean) => () => {\n const recent = used(agentId);\n if (recent.length >= (settle ? cap : cap - RESERVED)) return false;\n recent.push(now());\n return true;\n };\n const room = async (agentId: string) => {\n for (let round = 0; round < rounds && used(agentId).length >= cap; round++) await sleep();\n };\n const keep = (\n key: string,\n label: string,\n conversation: ConversationId,\n answer: AssistantMessage\n ) =>\n harness.commit(async (tx) => {\n const doc = await tx.doc(LabelsDoc);\n doc.labels[key] = label;\n const keys = Object.keys(doc.labels);\n for (const old of keys.slice(0, Math.max(0, keys.length - LABELS_KEPT)))\n delete doc.labels[old];\n await spend(tx, conversation, answer);\n return { ...doc.labels };\n }, BACKGROUND_CONTEXT);\n const read = async (agentId: string, callIds: readonly string[]) => {\n const conversation = await agents.conversation(agentId, BACKGROUND_CONTEXT);\n if (!conversation) return undefined;\n const cluster = clusterOf((await conversation.context(BACKGROUND_CONTEXT)).entries, callIds);\n return cluster ? { conversation, cluster } : undefined;\n };\n type Read = NonNullable<Awaited<ReturnType<typeof read>>>;\n const ask = async (agentId: string, key: string, found: Read, settle: boolean) => {\n if (vaultHold(options.vault?.value)) return null;\n if (settle) await room(agentId);\n const text = await scrubbed(describe(found.cluster.runs));\n const model = modelName((await found.conversation.agent(BACKGROUND_CONTEXT)).model);\n const asked = await options.label(text, model, BACKGROUND_CONTEXT, admit(agentId, settle));\n if (!asked) return null;\n const label = cleanLabel(asked.text);\n labels.replace(BACKGROUND_CONTEXT, await keep(key, label, found.conversation.id, asked.answer));\n return label || null;\n };\n const longer = (key: string) =>\n [...flying.keys(), ...waiting, ...Object.keys(labels.value)].some((other) =>\n other.startsWith(`${key} `)\n );\n const prefixOf = (callIds: readonly string[]) => {\n for (let size = callIds.length - 1; size > 0; size--) {\n const part = callIds.slice(0, size);\n const key = labelKey(part);\n const shown = flying.get(key) ?? shownLabel(labels.value, part);\n if (shown !== undefined) return Promise.resolve(shown);\n if (waiting.has(key)) return Promise.resolve(null);\n }\n return undefined;\n };\n const settled = async (agentId: string, callIds: readonly string[], key: string) => {\n for (let round = 0; round < rounds; round++) {\n await sleep();\n if (longer(key)) return undefined;\n const found = await read(agentId, callIds);\n if (found?.cluster.settled) return found;\n }\n return undefined;\n };\n const relabel = async (agentId: string, callIds: readonly string[], key: string) => {\n const found = await settled(agentId, callIds, key);\n return found ? ask(agentId, key, found, true) : undefined;\n };\n const make = async (agentId: string, callIds: readonly string[], key: string) => {\n const found = await read(agentId, callIds);\n if (!found || found.cluster.at < started) return null;\n const prefix = prefixOf(callIds);\n if (longer(key)) return (await prefix) ?? null;\n if (found.cluster.settled) return ask(agentId, key, found, prefix !== undefined);\n if (prefix !== undefined) {\n const shown = await prefix;\n return (await relabel(agentId, callIds, key)) ?? shown;\n }\n const first = await ask(agentId, key, found, false);\n waiting.add(key);\n void relabel(agentId, callIds, key)\n .catch(() => undefined)\n .finally(() => waiting.delete(key));\n return first;\n };\n return {\n labels,\n label(agentId, callIds, _context) {\n if (!callIds.length) return Promise.resolve(null);\n const key = labelKey(callIds);\n const found = shownLabel(labels.value, callIds);\n if (found !== undefined) return Promise.resolve(found);\n const known = flying.get(key);\n if (known !== undefined) return known;\n const made = make(agentId, callIds, key)\n .catch(() => null)\n .finally(() => flying.delete(key));\n flying.set(key, made);\n return made;\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAA6C,uBAAuB;AACpE,SAAS,0BAA0B;AAEnC;AAAA,EAGE;AAAA,EAGA;AAAA,OACK;AAGP,SAAS,iBAAiB;AAC1B,SAAS,WAAW,WAAW,gBAAgB;AAC/C,SAAuB,gBAAgB;AACvC,SAAS,UAAU,kBAAkB;AAI9B,MAAM,YAAY,UAAuB;AAAA,EAC9C,MAAM;AAAA,EACN,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS,OAAO,EAAE,QAAQ,CAAC,EAAE;AAC/B,CAAC;AAEM,MAAM,cAAc;AACpB,MAAM,oBAAoB;AACjC,MAAM,SAAS;AACf,MAAM,cAAc;AACpB,MAAM,eAAe;AACrB,MAAM,WAAW;AACjB,MAAM,UAAU;AAChB,MAAM,YAAY;AAEX,MAAM,gBAAgB;AAEtB,SAAS,cACd,QACA,SACA,OACA;AACA,QAAM,OAAO,MAAM;AACjB,UAAM,MAAM,UAAU,MAAM,CAAC;AAC7B,WAAO,OAAO;AAAA,MACZ,OAAO,QAAQ,OAAO,KAA+B,EAAE;AAAA,QAAO,CAAC,CAAC,GAAG,MACjE,IAAI,MAAM,GAAG,EAAE,MAAM,CAAC,OAAO,IAAI,IAAI,EAAE,CAAC;AAAA,MAC1C;AAAA,IACF;AAAA,EACF;AACA,QAAM,QAAQ,gBAAwC,KAAK,CAAC;AAC5D,MAAI,SAAS;AACb,QAAM,SAAS,MAAM;AACnB,QAAI,OAAQ;AACZ,aAAS;AACT,mBAAe,MAAM;AACnB,eAAS;AACT,YAAM,OAAO,KAAK;AAClB,YAAM,SAAS,MAAM;AACrB,YAAM,OAAO,OAAO,KAAK,IAAI;AAC7B,UACE,KAAK,WAAW,OAAO,KAAK,MAAM,EAAE,UACpC,KAAK,KAAK,CAAC,QAAQ,OAAO,GAAG,MAAM,KAAK,GAAG,CAAC;AAE5C,cAAM,QAAQ,oBAAoB,IAAI;AAAA,IAC1C,CAAC;AAAA,EACH;AACA,QAAM,OAAO,CAAC,QAAQ,GAAG,OAAO,EAAE,IAAI,CAAC,WAAW,OAAO,UAAU,MAAM,CAAC;AAC1E,SAAO;AAAA,IACL;AAAA,IACA,UAAU;AACR,iBAAW,OAAO,KAAM,KAAI;AAAA,IAC9B;AAAA,EACF;AACF;AAEO,SAAS,WACd,QACA,SAAS,eACT;AACA,SAAO,CAAC,UAAkB,YAAuD;AAC/E,UAAM,QAAQ,WAAW,OAAO,OAAO,OAAO;AAC9C,QAAI,UAAU,UAAa,CAAC,QAAQ,OAAQ,QAAO,QAAQ,QAAQ,SAAS,IAAI;AAChF,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,YAAM,OAAO,CAAC,UAAyB;AACrC,qBAAa,KAAK;AAClB,YAAI;AACJ,gBAAQ,KAAK;AAAA,MACf;AACA,YAAM,QAAQ,WAAW,MAAM,KAAK,IAAI,GAAG,MAAM;AACjD,YAAM,MAAM,OAAO,UAAU,MAAM;AACjC,cAAM,QAAQ,WAAW,OAAO,OAAO,OAAO;AAC9C,YAAI,UAAU,OAAW,MAAK,KAAK;AAAA,MACrC,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AACF;AAEO,SAAS,WAAW,MAAsB;AAC/C,SAAO,KACJ,QAAQ,QAAQ,GAAG,EACnB,KAAK,EACL,QAAQ,sBAAsB,EAAE,EAChC,QAAQ,QAAQ,EAAE,EAClB,KAAK,EACL,MAAM,GAAG,EACT,MAAM,GAAG,WAAW,EACpB,KAAK,GAAG,EACR,MAAM,GAAG,YAAY;AAC1B;AAMA,eAAe,MAAM,IAAQ,cAA8B,QAA0B;AACnF,QAAM,UAAU,MAAM,GAAG,IAAI,UAAU,YAAY,GAAG;AAItD,QAAM,MAAM,GAAG,OAAO,QAAQ,IAAI,OAAO,KAAK;AAC9C,QAAM,QAAQ,OAAO;AACrB,QAAM,QAAQ,OAAO,OAAO,QAAQ,GAAG,IAAI,OAAO,GAAG,IAAI;AACzD,MAAI,CAAC,OAAO;AACV,WAAO,GAAG,IAAI,KAAK,MAAM,KAAK,UAAU,KAAK,CAAC;AAC9C;AAAA,EACF;AACA,aAAW,SAAS,CAAC,SAAS,UAAU,aAAa,cAAc,aAAa;AAC9E,UAAM,KAAK,KAAK,MAAM,KAAK;AAC7B,aAAW,SAAS,CAAC,SAAS,UAAU,aAAa,cAAc,OAAO;AACxE,UAAM,KAAK,KAAK,KAAK,MAAM,KAAK,KAAK;AACzC;AAkBA,SAAS,UAAU,OAAkE;AACnF,SAAO,QAAQ,GAAG,MAAM,QAAQ,IAAI,MAAM,OAAO,KAAK;AACxD;AAEA,eAAsB,aACpB,SACA,UAAmB,oBACK;AACxB,QAAM,EAAE,SAAS,OAAO,IAAI;AAC5B,QAAM,MAAM,QAAQ,OAAO,KAAK;AAChC,QAAM,UAAU,IAAI;AACpB,QAAM,MAAM,QAAQ,aAAa;AACjC,QAAM,SAAS,MAAM,QAAQ;AAAA,IAC3B,OAAO,QAAQ,EAAE,IAAI,MAAM,GAAG,IAAI,SAAS,GAAG,OAAO;AAAA,IACrD;AAAA,EACF;AACA,QAAM,SAAS,gBAAwC,MAAM;AAC7D,QAAM,SAAS,oBAAI,IAAoC;AACvD,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,QAAQ,oBAAI,IAAsB;AACxC,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,SAAS,KAAK,MAAM,QAAQ,YAAY,aAAa,MAAM;AACjE,QAAM,QAAQ,MAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,MAAM,CAAC;AACxE,QAAM,OAAO,CAAC,YAAoB;AAChC,UAAM,KAAK,IAAI;AACf,UAAM,UAAU,MAAM,IAAI,OAAO,KAAK,CAAC,GAAG,OAAO,CAAC,SAAS,KAAK,OAAO,MAAM;AAC7E,UAAM,IAAI,SAAS,MAAM;AACzB,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,CAAC,SAAiB,WAAoB,MAAM;AACxD,UAAM,SAAS,KAAK,OAAO;AAC3B,QAAI,OAAO,WAAW,SAAS,MAAM,MAAM,UAAW,QAAO;AAC7D,WAAO,KAAK,IAAI,CAAC;AACjB,WAAO;AAAA,EACT;AACA,QAAM,OAAO,OAAO,YAAoB;AACtC,aAAS,QAAQ,GAAG,QAAQ,UAAU,KAAK,OAAO,EAAE,UAAU,KAAK,QAAS,OAAM,MAAM;AAAA,EAC1F;AACA,QAAM,OAAO,CACX,KACA,OACA,cACA,WAEA,QAAQ,OAAO,OAAO,OAAO;AAC3B,UAAM,MAAM,MAAM,GAAG,IAAI,SAAS;AAClC,QAAI,OAAO,GAAG,IAAI;AAClB,UAAM,OAAO,OAAO,KAAK,IAAI,MAAM;AACnC,eAAW,OAAO,KAAK,MAAM,GAAG,KAAK,IAAI,GAAG,KAAK,SAAS,WAAW,CAAC;AACpE,aAAO,IAAI,OAAO,GAAG;AACvB,UAAM,MAAM,IAAI,cAAc,MAAM;AACpC,WAAO,EAAE,GAAG,IAAI,OAAO;AAAA,EACzB,GAAG,kBAAkB;AACvB,QAAM,OAAO,OAAO,SAAiB,YAA+B;AAClE,UAAM,eAAe,MAAM,OAAO,aAAa,SAAS,kBAAkB;AAC1E,QAAI,CAAC,aAAc,QAAO;AAC1B,UAAM,UAAU,WAAW,MAAM,aAAa,QAAQ,kBAAkB,GAAG,SAAS,OAAO;AAC3F,WAAO,UAAU,EAAE,cAAc,QAAQ,IAAI;AAAA,EAC/C;AAEA,QAAM,MAAM,OAAO,SAAiB,KAAa,OAAa,WAAoB;AAChF,QAAI,UAAU,QAAQ,OAAO,KAAK,EAAG,QAAO;AAC5C,QAAI,OAAQ,OAAM,KAAK,OAAO;AAC9B,UAAM,OAAO,MAAM,SAAS,SAAS,MAAM,QAAQ,IAAI,CAAC;AACxD,UAAM,QAAQ,WAAW,MAAM,MAAM,aAAa,MAAM,kBAAkB,GAAG,KAAK;AAClF,UAAM,QAAQ,MAAM,QAAQ,MAAM,MAAM,OAAO,oBAAoB,MAAM,SAAS,MAAM,CAAC;AACzF,QAAI,CAAC,MAAO,QAAO;AACnB,UAAM,QAAQ,WAAW,MAAM,IAAI;AACnC,WAAO,QAAQ,oBAAoB,MAAM,KAAK,KAAK,OAAO,MAAM,aAAa,IAAI,MAAM,MAAM,CAAC;AAC9F,WAAO,SAAS;AAAA,EAClB;AACA,QAAM,SAAS,CAAC,QACd,CAAC,GAAG,OAAO,KAAK,GAAG,GAAG,SAAS,GAAG,OAAO,KAAK,OAAO,KAAK,CAAC,EAAE;AAAA,IAAK,CAAC,UACjE,MAAM,WAAW,GAAG,GAAG,GAAG;AAAA,EAC5B;AACF,QAAM,WAAW,CAAC,YAA+B;AAC/C,aAAS,OAAO,QAAQ,SAAS,GAAG,OAAO,GAAG,QAAQ;AACpD,YAAM,OAAO,QAAQ,MAAM,GAAG,IAAI;AAClC,YAAM,MAAM,SAAS,IAAI;AACzB,YAAM,QAAQ,OAAO,IAAI,GAAG,KAAK,WAAW,OAAO,OAAO,IAAI;AAC9D,UAAI,UAAU,OAAW,QAAO,QAAQ,QAAQ,KAAK;AACrD,UAAI,QAAQ,IAAI,GAAG,EAAG,QAAO,QAAQ,QAAQ,IAAI;AAAA,IACnD;AACA,WAAO;AAAA,EACT;AACA,QAAM,UAAU,OAAO,SAAiB,SAA4B,QAAgB;AAClF,aAAS,QAAQ,GAAG,QAAQ,QAAQ,SAAS;AAC3C,YAAM,MAAM;AACZ,UAAI,OAAO,GAAG,EAAG,QAAO;AACxB,YAAM,QAAQ,MAAM,KAAK,SAAS,OAAO;AACzC,UAAI,OAAO,QAAQ,QAAS,QAAO;AAAA,IACrC;AACA,WAAO;AAAA,EACT;AACA,QAAM,UAAU,OAAO,SAAiB,SAA4B,QAAgB;AAClF,UAAM,QAAQ,MAAM,QAAQ,SAAS,SAAS,GAAG;AACjD,WAAO,QAAQ,IAAI,SAAS,KAAK,OAAO,IAAI,IAAI;AAAA,EAClD;AACA,QAAM,OAAO,OAAO,SAAiB,SAA4B,QAAgB;AAC/E,UAAM,QAAQ,MAAM,KAAK,SAAS,OAAO;AACzC,QAAI,CAAC,SAAS,MAAM,QAAQ,KAAK,QAAS,QAAO;AACjD,UAAM,SAAS,SAAS,OAAO;AAC/B,QAAI,OAAO,GAAG,EAAG,QAAQ,MAAM,UAAW;AAC1C,QAAI,MAAM,QAAQ,QAAS,QAAO,IAAI,SAAS,KAAK,OAAO,WAAW,MAAS;AAC/E,QAAI,WAAW,QAAW;AACxB,YAAM,QAAQ,MAAM;AACpB,aAAQ,MAAM,QAAQ,SAAS,SAAS,GAAG,KAAM;AAAA,IACnD;AACA,UAAM,QAAQ,MAAM,IAAI,SAAS,KAAK,OAAO,KAAK;AAClD,YAAQ,IAAI,GAAG;AACf,SAAK,QAAQ,SAAS,SAAS,GAAG,EAC/B,MAAM,MAAM,MAAS,EACrB,QAAQ,MAAM,QAAQ,OAAO,GAAG,CAAC;AACpC,WAAO;AAAA,EACT;AACA,SAAO;AAAA,IACL;AAAA,IACA,MAAM,SAAS,SAAS,UAAU;AAChC,UAAI,CAAC,QAAQ,OAAQ,QAAO,QAAQ,QAAQ,IAAI;AAChD,YAAM,MAAM,SAAS,OAAO;AAC5B,YAAM,QAAQ,WAAW,OAAO,OAAO,OAAO;AAC9C,UAAI,UAAU,OAAW,QAAO,QAAQ,QAAQ,KAAK;AACrD,YAAM,QAAQ,OAAO,IAAI,GAAG;AAC5B,UAAI,UAAU,OAAW,QAAO;AAChC,YAAM,OAAO,KAAK,SAAS,SAAS,GAAG,EACpC,MAAM,MAAM,IAAI,EAChB,QAAQ,MAAM,OAAO,OAAO,GAAG,CAAC;AACnC,aAAO,IAAI,KAAK,IAAI;AACpB,aAAO;AAAA,IACT;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
function labelKey(callIds) {
|
|
2
|
+
return callIds.join(" ");
|
|
3
|
+
}
|
|
4
|
+
function shownLabel(labels, callIds) {
|
|
5
|
+
const key = labelKey(callIds);
|
|
6
|
+
return labels && Object.hasOwn(labels, key) ? labels[key] || null : void 0;
|
|
7
|
+
}
|
|
8
|
+
export {
|
|
9
|
+
labelKey,
|
|
10
|
+
shownLabel
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=shown.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/titles/shown.ts"],
|
|
4
|
+
"sourcesContent": ["export function labelKey(callIds: readonly string[]): string {\n return callIds.join(' ');\n}\n\nexport function shownLabel(\n labels: Readonly<Record<string, string>> | undefined,\n callIds: readonly string[]\n): string | null | undefined {\n const key = labelKey(callIds);\n return labels && Object.hasOwn(labels, key) ? labels[key] || null : undefined;\n}\n"],
|
|
5
|
+
"mappings": "AAAO,SAAS,SAAS,SAAoC;AAC3D,SAAO,QAAQ,KAAK,GAAG;AACzB;AAEO,SAAS,WACd,QACA,SAC2B;AAC3B,QAAM,MAAM,SAAS,OAAO;AAC5B,SAAO,UAAU,OAAO,OAAO,QAAQ,GAAG,IAAI,OAAO,GAAG,KAAK,OAAO;AACtE;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/types/client.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export interface AgentConnection {
|
|
|
25
25
|
readonly changes?: ReplicatedState<ChangesView>;
|
|
26
26
|
readonly monitor?: ReplicatedState<MonitorFeed>;
|
|
27
27
|
readonly vault?: ReplicatedState<VaultState>;
|
|
28
|
+
readonly labels?: ReplicatedState<Record<string, string>>;
|
|
28
29
|
readonly settings: AgentSettings;
|
|
29
30
|
readonly remote?: AgentRemote;
|
|
30
31
|
readonly pages?: AgentPages;
|
package/dist/types/control.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type { SecretName } from './secrets/names.ts';
|
|
|
11
11
|
import type { AgentControl } from './services.ts';
|
|
12
12
|
import type { SkillsRuntime } from './skills/index.ts';
|
|
13
13
|
import type { SprinklesRuntime } from './sprinkles/index.ts';
|
|
14
|
+
import type { LabelsRuntime } from './titles/labels.ts';
|
|
14
15
|
import { type Uploads } from './uploads.ts';
|
|
15
16
|
export interface HostLicks {
|
|
16
17
|
licks: Licks;
|
|
@@ -22,4 +23,4 @@ export interface HostScoops {
|
|
|
22
23
|
}
|
|
23
24
|
export declare function readable(out: string): string;
|
|
24
25
|
export declare function submissionId(value: string): SubmissionId | undefined;
|
|
25
|
-
export declare function createAgentControl(harness: Harness, target: Conversation | Cones, deliveries?: Deliveries, licks?: HostLicks, scoops?: HostScoops, skills?: Pick<SkillsRuntime, 'expand' | 'commandsFor'>, sprinkles?: Pick<SprinklesRuntime, 'send' | 'call'>, memory?: Pick<MemoryRuntime, 'save' | 'remove'>, freezer?: Pick<FreezerRuntime, 'freeze' | 'newChat' | 'thaw' | 'discard'>, changes?: Pick<ChangesRuntime, 'open' | 'accept' | 'revert'>, monitor?: Pick<Monitor, 'open' | 'close'>, secrets?: () => readonly SecretName[], uploads?: Uploads): AgentControl;
|
|
26
|
+
export declare function createAgentControl(harness: Harness, target: Conversation | Cones, deliveries?: Deliveries, licks?: HostLicks, scoops?: HostScoops, skills?: Pick<SkillsRuntime, 'expand' | 'commandsFor'>, sprinkles?: Pick<SprinklesRuntime, 'send' | 'call'>, memory?: Pick<MemoryRuntime, 'save' | 'remove'>, freezer?: Pick<FreezerRuntime, 'freeze' | 'newChat' | 'thaw' | 'discard'>, changes?: Pick<ChangesRuntime, 'open' | 'accept' | 'revert'>, monitor?: Pick<Monitor, 'open' | 'close'>, secrets?: () => readonly SecretName[], uploads?: Uploads, labels?: Pick<LabelsRuntime, 'label'>): AgentControl;
|
package/dist/types/host.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { AgentSettings } from './services.ts';
|
|
|
14
14
|
import type { SkillsRuntime } from './skills/index.ts';
|
|
15
15
|
import type { SprinklesRuntime } from './sprinkles/index.ts';
|
|
16
16
|
import { type Thumbnail } from './thumbs.ts';
|
|
17
|
+
import { type LabelsRuntime } from './titles/labels.ts';
|
|
17
18
|
import type { Uploads } from './uploads.ts';
|
|
18
19
|
import type { PortEndpoint } from './wire.ts';
|
|
19
20
|
export interface ConnectOptions {
|
|
@@ -51,4 +52,5 @@ export declare function hostAgent(agent: Agent, options?: {
|
|
|
51
52
|
secrets?: HostSecrets;
|
|
52
53
|
uploads?: Uploads;
|
|
53
54
|
thumbnail?: Thumbnail;
|
|
55
|
+
labels?: LabelsRuntime;
|
|
54
56
|
}): Promise<AgentHost>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type Context, type ReplicatedState } from '@earendil-works/chord';
|
|
2
|
+
import { type Harness } from '@earendil-works/pi-durable';
|
|
3
|
+
import type { VaultRecords } from './records.ts';
|
|
4
|
+
import type { Vault } from './vault.ts';
|
|
5
|
+
export declare const LEGACY_DATABASE = "slicc-agent-credentials";
|
|
6
|
+
export type MovedAccount = {
|
|
7
|
+
kind: 'model';
|
|
8
|
+
provider: string;
|
|
9
|
+
label: string;
|
|
10
|
+
} | {
|
|
11
|
+
kind: 'mcp';
|
|
12
|
+
server: string;
|
|
13
|
+
label: string;
|
|
14
|
+
};
|
|
15
|
+
export type MovedState = {
|
|
16
|
+
accounts: MovedAccount[];
|
|
17
|
+
dismissed: boolean;
|
|
18
|
+
};
|
|
19
|
+
export declare const MovedDoc: import("@earendil-works/pi-durable").SessionDocToken<MovedState>;
|
|
20
|
+
export declare function movedAccount(key: string): MovedAccount;
|
|
21
|
+
export declare const WIPE_WAIT_MS = 3000;
|
|
22
|
+
export interface Wiped {
|
|
23
|
+
now: MovedAccount[];
|
|
24
|
+
later: Promise<MovedAccount[]>;
|
|
25
|
+
}
|
|
26
|
+
export declare function wipeLegacy(factory?: IDBFactory | undefined, wait?: number): Promise<Wiped>;
|
|
27
|
+
export interface KeysMoved {
|
|
28
|
+
readonly state: ReplicatedState<string[]>;
|
|
29
|
+
dismiss(context: Context): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
type Records = Pick<VaultRecords, 'state' | 'model' | 'mcp' | 'refresh'>;
|
|
32
|
+
export declare function keysMoved(harness: Pick<Harness, 'commit'>, sources: {
|
|
33
|
+
records: Records;
|
|
34
|
+
vault: Pick<Vault, 'connected'>;
|
|
35
|
+
}, wiped: Wiped, context?: Context): Promise<KeysMoved>;
|
|
36
|
+
export {};
|
package/dist/types/services.d.ts
CHANGED
|
@@ -87,6 +87,7 @@ export interface AgentControl {
|
|
|
87
87
|
monitorClose(lease: string, context: Context): Promise<void>;
|
|
88
88
|
image(ref: ImageRef, context: Context): Promise<StoredImage>;
|
|
89
89
|
agentCommands(agentId: string, context: Context): Promise<Command[]>;
|
|
90
|
+
toolLabel(agentId: string, callIds: string[], context: Context): Promise<string | null>;
|
|
90
91
|
}
|
|
91
92
|
export interface ImageRef {
|
|
92
93
|
entry: number;
|
|
@@ -247,6 +248,7 @@ export interface AgentTranscript {
|
|
|
247
248
|
readonly changes: ReplicatedState<ChangesView>;
|
|
248
249
|
readonly monitor: ReplicatedState<MonitorFeed>;
|
|
249
250
|
readonly vault: ReplicatedState<VaultState>;
|
|
251
|
+
readonly labels: ReplicatedState<Record<string, string>>;
|
|
250
252
|
}
|
|
251
253
|
export interface PrivateRevision {
|
|
252
254
|
revision: number;
|
|
@@ -310,11 +312,13 @@ export interface SettingsState {
|
|
|
310
312
|
models: ModelChoice[];
|
|
311
313
|
accounts: AccountState[];
|
|
312
314
|
budget?: BudgetState | null;
|
|
315
|
+
moved?: string[];
|
|
313
316
|
}
|
|
314
317
|
export interface AgentSettings {
|
|
315
318
|
readonly state: ReplicatedState<SettingsState>;
|
|
316
319
|
connect(providerId: string, secret: string | null, fields: Record<string, string> | null, context: Context): Promise<void>;
|
|
317
320
|
disconnect(providerId: string, context: Context): Promise<void>;
|
|
318
321
|
signIn(providerId: string, context: Context): Promise<SignIn | McpSignIn | null>;
|
|
322
|
+
dismissMoved(context: Context): Promise<void>;
|
|
319
323
|
}
|
|
320
324
|
export declare const AgentSettings: import("@earendil-works/chord").Service<AgentSettings>;
|
package/dist/types/settings.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { CredentialStore, Provider } from '@earendil-works/pi-ai';
|
|
|
2
2
|
import { type MutableModels } from '@earendil-works/pi-ai/models';
|
|
3
3
|
import { type Budget } from './adobe.ts';
|
|
4
4
|
import type { McpAccounts } from './mcp/index.ts';
|
|
5
|
+
import type { KeysMoved } from './secrets/legacy.ts';
|
|
5
6
|
import type { VaultRecords } from './secrets/records.ts';
|
|
6
7
|
import type { PlainStore } from './secrets/store.ts';
|
|
7
8
|
import type { AccountField, AgentSettings } from './services.ts';
|
|
@@ -37,4 +38,5 @@ export declare function createAgentSettings(models: MutableModels, sources: Sett
|
|
|
37
38
|
usage?: UsageReader;
|
|
38
39
|
every?: number;
|
|
39
40
|
mcp?: McpAccounts;
|
|
41
|
+
moved?: KeysMoved;
|
|
40
42
|
}): Promise<AgentSettings>;
|
|
@@ -38,6 +38,7 @@ export declare class AgentAdapter extends Emitter<AgentAdapterEvents> implements
|
|
|
38
38
|
createScoop(parentId: string, name: string): Agent;
|
|
39
39
|
drop(agentId: string): Promise<void>;
|
|
40
40
|
createCone(name: string): Promise<Agent>;
|
|
41
|
+
toolLabel(agentId: string, callIds: readonly string[]): Promise<string | null>;
|
|
41
42
|
frozen(): readonly FrozenCone[];
|
|
42
43
|
thaw(id: string): Agent | null;
|
|
43
44
|
discard(id: string): void;
|
|
@@ -4,6 +4,7 @@ import { AgentAdapter } from './agent-port.ts';
|
|
|
4
4
|
import { ChangesAdapter } from './changes-port.ts';
|
|
5
5
|
import { MemoryAdapter } from './memory-port.ts';
|
|
6
6
|
import { MonitorAdapter } from './monitor-port.ts';
|
|
7
|
+
import { NoticesAdapter } from './notices-port.ts';
|
|
7
8
|
import { PageAdapter, type PageOptions } from './page-port.ts';
|
|
8
9
|
import { type Login, SettingsAdapter } from './settings-port.ts';
|
|
9
10
|
import { SprinkleAdapter } from './sprinkle-port.ts';
|
|
@@ -13,6 +14,7 @@ export { ChangesAdapter, type ChangesEvents } from './changes-port.ts';
|
|
|
13
14
|
export { MemoryAdapter } from './memory-port.ts';
|
|
14
15
|
export { assistant, errorAction, errorPart, isBusy, queued, textOf, toMessages, } from './messages.ts';
|
|
15
16
|
export { MonitorAdapter, type MonitorAdapterOptions, type MonitorInput, monitorSnapshot, } from './monitor-port.ts';
|
|
17
|
+
export { KEYS_MOVED, movedNotice, NoticesAdapter } from './notices-port.ts';
|
|
16
18
|
export { PageAdapter, type PageDocument, type PageOptions, type PageRun, type PageTarget, } from './page-port.ts';
|
|
17
19
|
export { type Login, SETTINGS_KEY, SettingsAdapter } from './settings-port.ts';
|
|
18
20
|
export { SprinkleAdapter } from './sprinkle-port.ts';
|
|
@@ -32,5 +34,6 @@ export declare function createAgentModel(connection: AgentConnection, options?:
|
|
|
32
34
|
memory: MemoryAdapter;
|
|
33
35
|
changes: ChangesAdapter;
|
|
34
36
|
monitor: MonitorAdapter;
|
|
37
|
+
notices: NoticesAdapter;
|
|
35
38
|
page: PageAdapter | null;
|
|
36
39
|
};
|