@ai-ecoverse/slicc-agent 3.17.1 → 3.19.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 +23 -3
- package/bin/agent +1 -1
- package/dist/agents.js +8 -5
- package/dist/agents.js.map +2 -2
- package/dist/client.js +3 -1
- package/dist/client.js.map +2 -2
- package/dist/control.js +17 -1
- package/dist/control.js.map +2 -2
- package/dist/freezer/index.js +272 -0
- package/dist/freezer/index.js.map +7 -0
- package/dist/freezer/title.js +40 -0
- package/dist/freezer/title.js.map +7 -0
- package/dist/gelatiere/index.js +1 -1
- package/dist/gelatiere/index.js.map +2 -2
- package/dist/host.js +5 -2
- package/dist/host.js.map +2 -2
- package/dist/licks/licks.js +20 -1
- package/dist/licks/licks.js.map +2 -2
- package/dist/main.js +13 -1
- package/dist/main.js.map +2 -2
- package/dist/memory/extract.js +2 -2
- package/dist/memory/extract.js.map +2 -2
- package/dist/memory/index.js +1 -0
- package/dist/memory/index.js.map +2 -2
- package/dist/scoops/cli.js +18 -5
- package/dist/scoops/cli.js.map +2 -2
- package/dist/scoops/index.js +3 -2
- package/dist/scoops/index.js.map +2 -2
- package/dist/scoops/requests.js +1 -1
- package/dist/scoops/requests.js.map +2 -2
- package/dist/services.js.map +2 -2
- package/dist/spectrum/agent-port.js +48 -7
- package/dist/spectrum/agent-port.js.map +2 -2
- package/dist/spectrum/index.js +1 -1
- package/dist/spectrum/index.js.map +2 -2
- package/dist/spectrum/settings-port.js +29 -4
- package/dist/spectrum/settings-port.js.map +2 -2
- package/dist/sprinkles/index.js +15 -6
- package/dist/sprinkles/index.js.map +2 -2
- package/dist/types/agents.d.ts +14 -0
- package/dist/types/client.d.ts +2 -0
- package/dist/types/control.d.ts +2 -1
- package/dist/types/freezer/index.d.ts +49 -0
- package/dist/types/freezer/title.d.ts +6 -0
- package/dist/types/host.d.ts +2 -0
- package/dist/types/licks/licks.d.ts +7 -0
- package/dist/types/memory/extract.d.ts +1 -1
- package/dist/types/memory/index.d.ts +1 -0
- package/dist/types/scoops/cli.d.ts +3 -1
- package/dist/types/scoops/index.d.ts +1 -0
- package/dist/types/scoops/requests.d.ts +1 -1
- package/dist/types/services.d.ts +6 -0
- package/dist/types/spectrum/agent-port.d.ts +8 -4
- package/dist/types/spectrum/index.d.ts +3 -2
- package/dist/types/spectrum/settings-port.d.ts +4 -1
- package/dist/types/sprinkles/index.d.ts +2 -1
- package/package.json +3 -3
- package/packages/vfs-root/gelatiere/GELATIERE.md +1 -0
|
@@ -26,9 +26,12 @@ class AgentAdapter extends Emitter {
|
|
|
26
26
|
#messages = /* @__PURE__ */ new Map();
|
|
27
27
|
#active = null;
|
|
28
28
|
#creating = /* @__PURE__ */ new Map();
|
|
29
|
-
|
|
29
|
+
#newChat;
|
|
30
|
+
constructor(connection, options = {}) {
|
|
30
31
|
super();
|
|
31
32
|
this.#connection = connection;
|
|
33
|
+
this.#newChat = options.newChat ?? "freeze";
|
|
34
|
+
connection.frozen?.subscribe(() => this.emit("frozen", this.frozen()));
|
|
32
35
|
this.#ready = new Promise((resolve) => {
|
|
33
36
|
connection.transcript.subscribe(() => {
|
|
34
37
|
this.#refresh();
|
|
@@ -165,8 +168,13 @@ class AgentAdapter extends Emitter {
|
|
|
165
168
|
compact() {
|
|
166
169
|
void this.#connection.control.compact(null, BACKGROUND_CONTEXT);
|
|
167
170
|
}
|
|
168
|
-
clear() {
|
|
169
|
-
|
|
171
|
+
clear(agentId = this.active()) {
|
|
172
|
+
if (this.#newChat === "freeze")
|
|
173
|
+
void this.#connection.control.newChat(agentId, BACKGROUND_CONTEXT);
|
|
174
|
+
else void this.#connection.control.reset(null, BACKGROUND_CONTEXT);
|
|
175
|
+
}
|
|
176
|
+
freeze(agentId = this.active()) {
|
|
177
|
+
void this.#connection.control.freeze(agentId, BACKGROUND_CONTEXT);
|
|
170
178
|
}
|
|
171
179
|
#configure(agentId, change) {
|
|
172
180
|
void this.#connection.control.configure(
|
|
@@ -239,12 +247,45 @@ class AgentAdapter extends Emitter {
|
|
|
239
247
|
};
|
|
240
248
|
}
|
|
241
249
|
frozen() {
|
|
242
|
-
return []
|
|
250
|
+
return (this.#connection.frozen?.value ?? []).map(
|
|
251
|
+
({ id, name, title, model, messages, frozenAt }) => ({
|
|
252
|
+
id,
|
|
253
|
+
name,
|
|
254
|
+
title,
|
|
255
|
+
model,
|
|
256
|
+
messages,
|
|
257
|
+
frozenAt
|
|
258
|
+
})
|
|
259
|
+
);
|
|
243
260
|
}
|
|
244
|
-
thaw() {
|
|
245
|
-
|
|
261
|
+
thaw(id) {
|
|
262
|
+
const record = this.frozen().find((item) => item.id === id);
|
|
263
|
+
if (!record) return null;
|
|
264
|
+
const provisional = `thawing-${crypto.randomUUID()}`;
|
|
265
|
+
const thawed = this.#connection.control.thaw(id, BACKGROUND_CONTEXT).then(
|
|
266
|
+
(result) => result.id,
|
|
267
|
+
() => null
|
|
268
|
+
);
|
|
269
|
+
this.#creating.set(provisional, thawed);
|
|
270
|
+
void thawed.then((cone) => {
|
|
271
|
+
if (this.#active !== provisional) return;
|
|
272
|
+
this.#active = cone ?? CONE;
|
|
273
|
+
this.emit("active", this.#active);
|
|
274
|
+
this.emit("messages", this.#active);
|
|
275
|
+
});
|
|
276
|
+
return {
|
|
277
|
+
id: provisional,
|
|
278
|
+
name: record.name,
|
|
279
|
+
kind: "cone",
|
|
280
|
+
parentId: null,
|
|
281
|
+
status: "idle",
|
|
282
|
+
model: record.model,
|
|
283
|
+
contextFill: 0,
|
|
284
|
+
unread: 0
|
|
285
|
+
};
|
|
246
286
|
}
|
|
247
|
-
discard() {
|
|
287
|
+
discard(id) {
|
|
288
|
+
void this.#connection.control.discard(id, BACKGROUND_CONTEXT);
|
|
248
289
|
}
|
|
249
290
|
}
|
|
250
291
|
export {
|
|
@@ -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 { entryLick } from '../licks/state.ts';\nimport type { AgentSettingsChange, AgentSummary } 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 readonly #messages = new Map<string, Message[]>();\n #active: string | null = null;\n #creating = new Map<string, Promise<string | null>>();\n\n constructor(connection: AgentConnection) {\n super();\n this.#connection = connection;\n this.#ready = new Promise((resolve) => {\n connection.transcript.subscribe(() => {\n this.#refresh();\n resolve();\n });\n connection.deliveries.subscribe(() => this.#refresh());\n connection.views?.subscribe(() => this.#refresh());\n connection.agents?.subscribe(() => this.#refresh());\n });\n }\n\n #summary(): readonly AgentSummary[] {\n const agents = this.#connection.agents?.value?.agents;\n return agents?.length\n ? agents\n : [{ id: CONE, name: 'sliccy', kind: 'cone', parentId: null, role: null }];\n }\n\n #view(agentId: string = this.active()): ConversationView | undefined {\n const views = this.#connection.views?.value;\n const view = views?.[agentId];\n if (view) return view;\n const cone = this.#connection.agents?.value?.active ?? CONE;\n return agentId === cone ? this.#connection.transcript.value : undefined;\n }\n\n #refresh(): void {\n this.emit('agents', this.list());\n for (const summary of this.#summary()) this.#update(summary.id);\n for (const id of [...this.#messages.keys()])\n if (!this.#summary().some((summary) => summary.id === id)) this.#messages.delete(id);\n }\n\n #update(agentId: string): void {\n const view = this.#view(agentId);\n if (!view) return;\n const before = this.#messages.get(agentId) ?? [];\n const after = toMessages(view, this.#connection.deliveries.value);\n this.#messages.set(agentId, after);\n if (before.length !== after.length) {\n this.emit('messages', agentId);\n return;\n }\n const changed = after.filter(\n (message, index) => JSON.stringify(message) !== JSON.stringify(before[index])\n );\n if (changed.length === 1) this.emit('message', { agentId, message: changed[0] as Message });\n else if (changed.length > 1) this.emit('messages', agentId);\n }\n\n ready(): Promise<void> {\n return this.#ready;\n }\n\n list(): readonly Agent[] {\n return this.#summary().map((summary) => {\n const view = this.#view(summary.id);\n const doc = view?.docs['pi.agent'] as AgentDoc | undefined;\n return {\n id: summary.id,\n name: summary.role ? `${summary.name} \u00B7 ${summary.role}` : summary.name,\n kind: summary.kind,\n parentId: summary.parentId,\n status: status(view, this.#messages.get(summary.id) ?? []),\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 const known = this.#summary();\n if (this.#active && this.#creating.has(this.#active)) return this.#active;\n if (this.#active && known.some((summary) => summary.id === this.#active)) return this.#active;\n return this.#connection.agents?.value?.active ?? CONE;\n }\n\n select(id: string): void {\n if (this.#creating.has(id)) {\n this.#active = id;\n this.emit('active', id);\n return;\n }\n const found = this.#summary().find((summary) => summary.id === id);\n if (!found) return;\n this.#active = id;\n if (found.kind === 'cone') void this.#connection.control.selectCone(id, BACKGROUND_CONTEXT);\n this.emit('active', id);\n this.emit('messages', id);\n }\n\n messages(agentId: string = this.active()): readonly Message[] {\n return this.#messages.get(agentId) ?? [];\n }\n\n async send(agentId: string, input: string | Outgoing): Promise<void> {\n const outgoing = typeof input === 'string' ? { text: input } : input;\n const target = this.#creating.has(agentId) ? await this.#creating.get(agentId) : agentId;\n if (target === null) throw new Error('The scoop could not be created.');\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 agentId: target || null,\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.rewindAgent(\n agentId,\n messageId,\n BACKGROUND_CONTEXT\n );\n return rewound.done ? { text: rewound.text } : null;\n }\n\n stop(agentId: string = this.active()): void {\n void this.#connection.control.stopAgent(agentId, BACKGROUND_CONTEXT);\n }\n\n busy(agentId: string = this.active()): boolean {\n return isBusy(this.#view(agentId));\n }\n\n queue(agentId: string = this.active()): readonly UserMessage[] {\n return queued(this.#view(agentId));\n }\n\n unqueue(agentId: string, messageId: string): void {\n void this.#connection.control.unqueue(agentId, messageId.replace(/^q/, ''), BACKGROUND_CONTEXT);\n }\n\n suggestion(): string | null {\n return null;\n }\n\n answer(): void {}\n\n resolveLick(agentId: string, messageId: string, state: 'confirmed' | 'dismissed'): void {\n const message = this.messages(agentId).find((candidate) => candidate.id === messageId);\n const entry = this.#view(agentId)?.entries.find(\n (candidate) => `e${candidate.id}` === messageId\n );\n const lick = message?.role === 'lick' && entry ? entryLick(entry) : undefined;\n if (lick) void this.#connection.control.resolveLick(lick.id, state, BACKGROUND_CONTEXT);\n }\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(agentId: string, change: Partial<AgentSettingsChange>): void {\n void this.#connection.control.configure(\n {\n model: change.model ?? null,\n thinkingLevel: change.thinkingLevel ?? null,\n agentId,\n },\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(agentId, {\n model: { provider: model.slice(0, at), modelId: model.slice(at + 1) },\n });\n }\n\n setThinking(agentId: string, level: Thinking): void {\n this.#configure(agentId, { thinkingLevel: level });\n }\n\n async older(): Promise<readonly Message[]> {\n return [];\n }\n\n commands(): readonly SlashCommand[] {\n return (this.#connection.commands?.value ?? []).map((command) => ({ ...command }));\n }\n\n createScoop(parentId: string, name: string): Agent {\n const provisional = `scoop:creating-${crypto.randomUUID()}`;\n const created = this.#connection.control.createScoop(parentId, name, BACKGROUND_CONTEXT).then(\n (result) => result.id,\n () => null\n );\n this.#creating.set(provisional, created);\n void created.then((id) => {\n if (this.#active !== provisional) return;\n this.#active = id ?? parentId;\n this.emit('active', this.#active);\n this.emit('messages', this.#active);\n });\n return {\n id: provisional,\n name,\n kind: 'scoop',\n parentId,\n status: 'idle',\n model: '',\n contextFill: 0,\n unread: 0,\n };\n }\n\n async drop(agentId: string): Promise<void> {\n const parent = this.#summary().find((summary) => summary.id === agentId)?.parentId ?? CONE;\n const dropped = await this.#connection.control.drop(agentId, BACKGROUND_CONTEXT);\n if (dropped.error !== null) throw new Error(dropped.error);\n if (this.#active === agentId) this.select(parent);\n }\n\n async createCone(name: string): Promise<Agent> {\n const created = await this.#connection.control.createCone(name, BACKGROUND_CONTEXT);\n if (created.error !== null) throw new Error(created.error);\n return {\n id: created.id,\n name: name.trim(),\n kind: 'cone',\n parentId: null,\n status: 'idle',\n model: '',\n contextFill: 0,\n unread: 0,\n };\n }\n\n frozen(): readonly FrozenCone[] {\n return [];\n }\n\n thaw(): Agent | null {\n return null;\n }\n\n discard(): void {}\n}\n"],
|
|
5
|
-
"mappings": "AAYA,SAAS,0BAA0B;AAGnC,SAAS,iBAAiB;AAE1B,SAAS,eAAe;AACxB,SAAS,QAAQ,QAAQ,kBAAkB;AAEpC,MAAM,OAAO;
|
|
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 { entryLick } from '../licks/state.ts';\nimport type { AgentSettingsChange, AgentSummary } from '../services.ts';\nimport { Emitter } from './emitter.ts';\nimport { isBusy, queued, toMessages } from './messages.ts';\n\nexport const CONE = 'cone';\n\nexport type NewChat = 'freeze' | 'reset';\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 readonly #messages = new Map<string, Message[]>();\n #active: string | null = null;\n #creating = new Map<string, Promise<string | null>>();\n\n readonly #newChat: NewChat;\n\n constructor(connection: AgentConnection, options: { newChat?: NewChat } = {}) {\n super();\n this.#connection = connection;\n this.#newChat = options.newChat ?? 'freeze';\n connection.frozen?.subscribe(() => this.emit('frozen', this.frozen()));\n this.#ready = new Promise((resolve) => {\n connection.transcript.subscribe(() => {\n this.#refresh();\n resolve();\n });\n connection.deliveries.subscribe(() => this.#refresh());\n connection.views?.subscribe(() => this.#refresh());\n connection.agents?.subscribe(() => this.#refresh());\n });\n }\n\n #summary(): readonly AgentSummary[] {\n const agents = this.#connection.agents?.value?.agents;\n return agents?.length\n ? agents\n : [{ id: CONE, name: 'sliccy', kind: 'cone', parentId: null, role: null }];\n }\n\n #view(agentId: string = this.active()): ConversationView | undefined {\n const views = this.#connection.views?.value;\n const view = views?.[agentId];\n if (view) return view;\n const cone = this.#connection.agents?.value?.active ?? CONE;\n return agentId === cone ? this.#connection.transcript.value : undefined;\n }\n\n #refresh(): void {\n this.emit('agents', this.list());\n for (const summary of this.#summary()) this.#update(summary.id);\n for (const id of [...this.#messages.keys()])\n if (!this.#summary().some((summary) => summary.id === id)) this.#messages.delete(id);\n }\n\n #update(agentId: string): void {\n const view = this.#view(agentId);\n if (!view) return;\n const before = this.#messages.get(agentId) ?? [];\n const after = toMessages(view, this.#connection.deliveries.value);\n this.#messages.set(agentId, after);\n if (before.length !== after.length) {\n this.emit('messages', agentId);\n return;\n }\n const changed = after.filter(\n (message, index) => JSON.stringify(message) !== JSON.stringify(before[index])\n );\n if (changed.length === 1) this.emit('message', { agentId, message: changed[0] as Message });\n else if (changed.length > 1) this.emit('messages', agentId);\n }\n\n ready(): Promise<void> {\n return this.#ready;\n }\n\n list(): readonly Agent[] {\n return this.#summary().map((summary) => {\n const view = this.#view(summary.id);\n const doc = view?.docs['pi.agent'] as AgentDoc | undefined;\n return {\n id: summary.id,\n name: summary.role ? `${summary.name} \u00B7 ${summary.role}` : summary.name,\n kind: summary.kind,\n parentId: summary.parentId,\n status: status(view, this.#messages.get(summary.id) ?? []),\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 const known = this.#summary();\n if (this.#active && this.#creating.has(this.#active)) return this.#active;\n if (this.#active && known.some((summary) => summary.id === this.#active)) return this.#active;\n return this.#connection.agents?.value?.active ?? CONE;\n }\n\n select(id: string): void {\n if (this.#creating.has(id)) {\n this.#active = id;\n this.emit('active', id);\n return;\n }\n const found = this.#summary().find((summary) => summary.id === id);\n if (!found) return;\n this.#active = id;\n if (found.kind === 'cone') void this.#connection.control.selectCone(id, BACKGROUND_CONTEXT);\n this.emit('active', id);\n this.emit('messages', id);\n }\n\n messages(agentId: string = this.active()): readonly Message[] {\n return this.#messages.get(agentId) ?? [];\n }\n\n async send(agentId: string, input: string | Outgoing): Promise<void> {\n const outgoing = typeof input === 'string' ? { text: input } : input;\n const target = this.#creating.has(agentId) ? await this.#creating.get(agentId) : agentId;\n if (target === null) throw new Error('The scoop could not be created.');\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 agentId: target || null,\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.rewindAgent(\n agentId,\n messageId,\n BACKGROUND_CONTEXT\n );\n return rewound.done ? { text: rewound.text } : null;\n }\n\n stop(agentId: string = this.active()): void {\n void this.#connection.control.stopAgent(agentId, BACKGROUND_CONTEXT);\n }\n\n busy(agentId: string = this.active()): boolean {\n return isBusy(this.#view(agentId));\n }\n\n queue(agentId: string = this.active()): readonly UserMessage[] {\n return queued(this.#view(agentId));\n }\n\n unqueue(agentId: string, messageId: string): void {\n void this.#connection.control.unqueue(agentId, messageId.replace(/^q/, ''), BACKGROUND_CONTEXT);\n }\n\n suggestion(): string | null {\n return null;\n }\n\n answer(): void {}\n\n resolveLick(agentId: string, messageId: string, state: 'confirmed' | 'dismissed'): void {\n const message = this.messages(agentId).find((candidate) => candidate.id === messageId);\n const entry = this.#view(agentId)?.entries.find(\n (candidate) => `e${candidate.id}` === messageId\n );\n const lick = message?.role === 'lick' && entry ? entryLick(entry) : undefined;\n if (lick) void this.#connection.control.resolveLick(lick.id, state, BACKGROUND_CONTEXT);\n }\n\n compact(): void {\n void this.#connection.control.compact(null, BACKGROUND_CONTEXT);\n }\n\n clear(agentId: string = this.active()): void {\n if (this.#newChat === 'freeze')\n void this.#connection.control.newChat(agentId, BACKGROUND_CONTEXT);\n else void this.#connection.control.reset(null, BACKGROUND_CONTEXT);\n }\n\n freeze(agentId: string = this.active()): void {\n void this.#connection.control.freeze(agentId, BACKGROUND_CONTEXT);\n }\n\n #configure(agentId: string, change: Partial<AgentSettingsChange>): void {\n void this.#connection.control.configure(\n {\n model: change.model ?? null,\n thinkingLevel: change.thinkingLevel ?? null,\n agentId,\n },\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(agentId, {\n model: { provider: model.slice(0, at), modelId: model.slice(at + 1) },\n });\n }\n\n setThinking(agentId: string, level: Thinking): void {\n this.#configure(agentId, { thinkingLevel: level });\n }\n\n async older(): Promise<readonly Message[]> {\n return [];\n }\n\n commands(): readonly SlashCommand[] {\n return (this.#connection.commands?.value ?? []).map((command) => ({ ...command }));\n }\n\n createScoop(parentId: string, name: string): Agent {\n const provisional = `scoop:creating-${crypto.randomUUID()}`;\n const created = this.#connection.control.createScoop(parentId, name, BACKGROUND_CONTEXT).then(\n (result) => result.id,\n () => null\n );\n this.#creating.set(provisional, created);\n void created.then((id) => {\n if (this.#active !== provisional) return;\n this.#active = id ?? parentId;\n this.emit('active', this.#active);\n this.emit('messages', this.#active);\n });\n return {\n id: provisional,\n name,\n kind: 'scoop',\n parentId,\n status: 'idle',\n model: '',\n contextFill: 0,\n unread: 0,\n };\n }\n\n async drop(agentId: string): Promise<void> {\n const parent = this.#summary().find((summary) => summary.id === agentId)?.parentId ?? CONE;\n const dropped = await this.#connection.control.drop(agentId, BACKGROUND_CONTEXT);\n if (dropped.error !== null) throw new Error(dropped.error);\n if (this.#active === agentId) this.select(parent);\n }\n\n async createCone(name: string): Promise<Agent> {\n const created = await this.#connection.control.createCone(name, BACKGROUND_CONTEXT);\n if (created.error !== null) throw new Error(created.error);\n return {\n id: created.id,\n name: name.trim(),\n kind: 'cone',\n parentId: null,\n status: 'idle',\n model: '',\n contextFill: 0,\n unread: 0,\n };\n }\n\n frozen(): readonly FrozenCone[] {\n return (this.#connection.frozen?.value ?? []).map(\n ({ id, name, title, model, messages, frozenAt }) => ({\n id,\n name,\n title,\n model,\n messages,\n frozenAt,\n })\n );\n }\n\n thaw(id: string): Agent | null {\n const record = this.frozen().find((item) => item.id === id);\n if (!record) return null;\n const provisional = `thawing-${crypto.randomUUID()}`;\n const thawed = this.#connection.control.thaw(id, BACKGROUND_CONTEXT).then(\n (result) => result.id,\n () => null\n );\n this.#creating.set(provisional, thawed);\n void thawed.then((cone) => {\n if (this.#active !== provisional) return;\n this.#active = cone ?? CONE;\n this.emit('active', this.#active);\n this.emit('messages', this.#active);\n });\n return {\n id: provisional,\n name: record.name,\n kind: 'cone',\n parentId: null,\n status: 'idle',\n model: record.model,\n contextFill: 0,\n unread: 0,\n };\n }\n\n discard(id: string): void {\n void this.#connection.control.discard(id, BACKGROUND_CONTEXT);\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/spectrum/index.js
CHANGED
|
@@ -18,7 +18,7 @@ import { SETTINGS_KEY, SettingsAdapter as SettingsAdapter2 } from "./settings-po
|
|
|
18
18
|
import { SprinkleAdapter as SprinkleAdapter2 } from "./sprinkle-port.js";
|
|
19
19
|
import { TrayAdapter as TrayAdapter2 } from "./tray-port.js";
|
|
20
20
|
function createAgentModel(connection, options = {}) {
|
|
21
|
-
const agent = new AgentAdapter(connection);
|
|
21
|
+
const agent = new AgentAdapter(connection, options.newChat ? { newChat: options.newChat } : {});
|
|
22
22
|
return {
|
|
23
23
|
agent,
|
|
24
24
|
settings: new SettingsAdapter(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/spectrum/index.ts"],
|
|
4
|
-
"sourcesContent": ["import type { AgentConnection } from '../client.ts';\nimport { AgentAdapter } from './agent-port.ts';\nimport { MemoryAdapter } from './memory-port.ts';\nimport { type Login, SettingsAdapter } from './settings-port.ts';\nimport { SprinkleAdapter } from './sprinkle-port.ts';\nimport { TrayAdapter } from './tray-port.ts';\n\nexport { AgentAdapter, CONE } from './agent-port.ts';\nexport { MemoryAdapter } from './memory-port.ts';\nexport {\n assistant,\n errorAction,\n errorPart,\n isBusy,\n queued,\n textOf,\n toMessages,\n} from './messages.ts';\nexport { type Login, SETTINGS_KEY, SettingsAdapter } from './settings-port.ts';\nexport { SprinkleAdapter } from './sprinkle-port.ts';\nexport { TrayAdapter } from './tray-port.ts';\n\nexport function createAgentModel(\n connection: AgentConnection,\n options: {
|
|
5
|
-
"mappings": "AACA,SAAS,
|
|
4
|
+
"sourcesContent": ["import type { AgentConnection } from '../client.ts';\nimport { AgentAdapter, type NewChat } from './agent-port.ts';\nimport { MemoryAdapter } from './memory-port.ts';\nimport { type Login, SettingsAdapter } from './settings-port.ts';\nimport { SprinkleAdapter } from './sprinkle-port.ts';\nimport { TrayAdapter } from './tray-port.ts';\n\nexport { AgentAdapter, CONE, type NewChat } from './agent-port.ts';\nexport { MemoryAdapter } from './memory-port.ts';\nexport {\n assistant,\n errorAction,\n errorPart,\n isBusy,\n queued,\n textOf,\n toMessages,\n} from './messages.ts';\nexport { type Login, SETTINGS_KEY, SettingsAdapter } from './settings-port.ts';\nexport { SprinkleAdapter } from './sprinkle-port.ts';\nexport { TrayAdapter } from './tray-port.ts';\n\nexport function createAgentModel(\n connection: AgentConnection,\n options: {\n storage?: Pick<Storage, 'getItem' | 'setItem'> | null;\n login?: Login;\n newChat?: NewChat;\n } = {}\n): {\n agent: AgentAdapter;\n settings: SettingsAdapter;\n tray: TrayAdapter;\n sprinkles: SprinkleAdapter;\n memory: MemoryAdapter;\n} {\n const agent = new AgentAdapter(connection, options.newChat ? { newChat: options.newChat } : {});\n return {\n agent,\n settings: new SettingsAdapter(\n connection.settings,\n agent,\n options.storage ?? null,\n options.login ?? null\n ),\n tray: new TrayAdapter(connection.settings),\n sprinkles: new SprinkleAdapter(connection),\n memory: new MemoryAdapter(connection),\n };\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,oBAAkC;AAC3C,SAAS,qBAAqB;AAC9B,SAAqB,uBAAuB;AAC5C,SAAS,uBAAuB;AAChC,SAAS,mBAAmB;AAE5B,SAAS,gBAAAA,eAAc,YAA0B;AACjD,SAAS,iBAAAC,sBAAqB;AAC9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAqB,cAAc,mBAAAC,wBAAuB;AAC1D,SAAS,mBAAAC,wBAAuB;AAChC,SAAS,eAAAC,oBAAmB;AAErB,SAAS,iBACd,YACA,UAII,CAAC,GAOL;AACA,QAAM,QAAQ,IAAI,aAAa,YAAY,QAAQ,UAAU,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC,CAAC;AAC9F,SAAO;AAAA,IACL;AAAA,IACA,UAAU,IAAI;AAAA,MACZ,WAAW;AAAA,MACX;AAAA,MACA,QAAQ,WAAW;AAAA,MACnB,QAAQ,SAAS;AAAA,IACnB;AAAA,IACA,MAAM,IAAI,YAAY,WAAW,QAAQ;AAAA,IACzC,WAAW,IAAI,gBAAgB,UAAU;AAAA,IACzC,QAAQ,IAAI,cAAc,UAAU;AAAA,EACtC;AACF;",
|
|
6
6
|
"names": ["AgentAdapter", "MemoryAdapter", "SettingsAdapter", "SprinkleAdapter", "TrayAdapter"]
|
|
7
7
|
}
|
|
@@ -14,6 +14,7 @@ class SettingsAdapter extends Emitter {
|
|
|
14
14
|
#agent;
|
|
15
15
|
#storage;
|
|
16
16
|
#login;
|
|
17
|
+
#signing = /* @__PURE__ */ new Map();
|
|
17
18
|
#local;
|
|
18
19
|
constructor(settings, agent, storage, login = null) {
|
|
19
20
|
super();
|
|
@@ -57,18 +58,42 @@ class SettingsAdapter extends Emitter {
|
|
|
57
58
|
}));
|
|
58
59
|
}
|
|
59
60
|
accounts() {
|
|
60
|
-
return this.#state().accounts.map(
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
return this.#state().accounts.map(({ needs, ...account }) => {
|
|
62
|
+
const shown = this.#signing.has(account.id) ? { ...account, status: "signing-in" } : account;
|
|
63
|
+
return needs ? { ...shown, needs } : shown;
|
|
64
|
+
});
|
|
63
65
|
}
|
|
64
66
|
async connect(id, secret, options = {}) {
|
|
65
67
|
const account = this.#state().accounts.find((candidate) => candidate.id === id);
|
|
66
68
|
const login = account?.auth === "oauth" ? this.#login : null;
|
|
67
|
-
const key = secret ?? (login ? await
|
|
69
|
+
const key = secret ?? (login ? await this.#signIn(id, login) : void 0);
|
|
68
70
|
if (!key)
|
|
69
71
|
throw new Error(`${id} needs ${account?.auth === "oauth" ? "a sign-in" : "an API key"}`);
|
|
70
72
|
await this.#settings.connect(id, key, options.region ?? null, BACKGROUND_CONTEXT);
|
|
71
73
|
}
|
|
74
|
+
async #signIn(id, login) {
|
|
75
|
+
const controller = new AbortController();
|
|
76
|
+
this.#signing.get(id)?.abort(new DOMException("A newer sign-in replaced this one.", "AbortError"));
|
|
77
|
+
this.#signing.set(id, controller);
|
|
78
|
+
this.emit("accounts", this.accounts());
|
|
79
|
+
try {
|
|
80
|
+
const key = await login(id, () => this.#settings.signIn(id, BACKGROUND_CONTEXT), {
|
|
81
|
+
signal: controller.signal
|
|
82
|
+
});
|
|
83
|
+
controller.signal.throwIfAborted();
|
|
84
|
+
return key;
|
|
85
|
+
} finally {
|
|
86
|
+
if (this.#signing.get(id) === controller) this.#signing.delete(id);
|
|
87
|
+
this.emit("accounts", this.accounts());
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
cancel(id) {
|
|
91
|
+
const controller = this.#signing.get(id);
|
|
92
|
+
if (!controller) return;
|
|
93
|
+
this.#signing.delete(id);
|
|
94
|
+
controller.abort(new DOMException("The sign-in was cancelled.", "AbortError"));
|
|
95
|
+
this.emit("accounts", this.accounts());
|
|
96
|
+
}
|
|
72
97
|
disconnect(id) {
|
|
73
98
|
void this.#settings.disconnect(id, BACKGROUND_CONTEXT);
|
|
74
99
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/spectrum/settings-port.ts"],
|
|
4
|
-
"sourcesContent": ["import type {\n Account,\n ModelOption,\n Settings,\n SettingsEvents,\n SettingsPort,\n} from '@ai-ecoverse/slicc-spectrum/ui';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { AgentSettings, SettingsState, SignIn } from '../services.ts';\nimport type { AgentAdapter } from './agent-port.ts';\nimport { Emitter } from './emitter.ts';\n\nexport const SETTINGS_KEY = 'slicc-agent.settings';\n\nconst defaults: Settings = {\n color: 'system',\n model: '',\n thinking: 'off',\n sendOnEnter: true,\n showThinking: false,\n diffStyle: 'unified',\n};\n\ntype Local = Omit<Settings, 'model' | 'thinking'>;\n\nexport type Login = (providerId: string
|
|
5
|
-
"mappings": "AAOA,SAAS,0BAA0B;AAGnC,SAAS,eAAe;AAEjB,MAAM,eAAe;AAE5B,MAAM,WAAqB;AAAA,EACzB,OAAO;AAAA,EACP,OAAO;AAAA,EACP,UAAU;AAAA,EACV,aAAa;AAAA,EACb,cAAc;AAAA,EACd,WAAW;AACb;
|
|
4
|
+
"sourcesContent": ["import type {\n Account,\n ModelOption,\n Settings,\n SettingsEvents,\n SettingsPort,\n} from '@ai-ecoverse/slicc-spectrum/ui';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { AgentSettings, SettingsState, SignIn } from '../services.ts';\nimport type { AgentAdapter } from './agent-port.ts';\nimport { Emitter } from './emitter.ts';\n\nexport const SETTINGS_KEY = 'slicc-agent.settings';\n\nconst defaults: Settings = {\n color: 'system',\n model: '',\n thinking: 'off',\n sendOnEnter: true,\n showThinking: false,\n diffStyle: 'unified',\n};\n\ntype Local = Omit<Settings, 'model' | 'thinking'>;\n\nexport type Login = (\n providerId: string,\n signIn: () => Promise<SignIn | null>,\n options: { signal: AbortSignal }\n) => Promise<string>;\n\nexport class SettingsAdapter extends Emitter<SettingsEvents> implements SettingsPort {\n readonly #settings: AgentSettings;\n readonly #agent: AgentAdapter;\n readonly #storage: Pick<Storage, 'getItem' | 'setItem'> | null;\n readonly #login: Login | null;\n readonly #signing = new Map<string, AbortController>();\n #local: Local;\n\n constructor(\n settings: AgentSettings,\n agent: AgentAdapter,\n storage: Pick<Storage, 'getItem' | 'setItem'> | null,\n login: Login | null = null\n ) {\n super();\n this.#settings = settings;\n this.#agent = agent;\n this.#storage = storage;\n this.#login = login;\n const saved = JSON.parse(storage?.getItem(SETTINGS_KEY) ?? '{}') as Partial<Local>;\n const { model: _model, thinking: _thinking, ...local } = { ...defaults, ...saved };\n this.#local = local;\n settings.state.subscribe(() => {\n this.emit('accounts', this.accounts());\n this.emit('settings', this.get());\n });\n agent.on('agents', () => this.emit('settings', this.get()));\n }\n\n #state(): SettingsState {\n return this.#settings.state.value ?? { models: [], accounts: [] };\n }\n\n get(): Settings {\n const cone = this.#agent.list()[0];\n return { ...this.#local, model: cone?.model ?? '', thinking: cone?.thinking ?? 'off' };\n }\n\n update(patch: Partial<Settings>): void {\n const { model, thinking, ...local } = patch;\n const id = this.#agent.active();\n if (model !== undefined) this.#agent.setModel(id, model);\n if (thinking !== undefined) this.#agent.setThinking(id, thinking);\n this.#local = { ...this.#local, ...local };\n this.#storage?.setItem(SETTINGS_KEY, JSON.stringify(this.#local));\n this.emit('settings', this.get());\n }\n\n models(): readonly ModelOption[] {\n return this.#state().models.map(({ id, label, provider, kind, reasoning, images }) => ({\n id,\n label,\n provider,\n kind,\n reasoning,\n ...(images === undefined ? {} : { images }),\n }));\n }\n\n accounts(): readonly Account[] {\n return this.#state().accounts.map(({ needs, ...account }) => {\n const shown = this.#signing.has(account.id)\n ? { ...account, status: 'signing-in' as const }\n : account;\n return needs ? { ...shown, needs } : shown;\n });\n }\n\n async connect(id: string, secret?: string, options: { region?: string } = {}): Promise<void> {\n const account = this.#state().accounts.find((candidate) => candidate.id === id);\n const login = account?.auth === 'oauth' ? this.#login : null;\n const key = secret ?? (login ? await this.#signIn(id, login) : undefined);\n if (!key)\n throw new Error(`${id} needs ${account?.auth === 'oauth' ? 'a sign-in' : 'an API key'}`);\n await this.#settings.connect(id, key, options.region ?? null, BACKGROUND_CONTEXT);\n }\n\n async #signIn(id: string, login: Login): Promise<string> {\n const controller = new AbortController();\n this.#signing\n .get(id)\n ?.abort(new DOMException('A newer sign-in replaced this one.', 'AbortError'));\n this.#signing.set(id, controller);\n this.emit('accounts', this.accounts());\n try {\n const key = await login(id, () => this.#settings.signIn(id, BACKGROUND_CONTEXT), {\n signal: controller.signal,\n });\n controller.signal.throwIfAborted();\n return key;\n } finally {\n if (this.#signing.get(id) === controller) this.#signing.delete(id);\n this.emit('accounts', this.accounts());\n }\n }\n\n cancel(id: string): void {\n const controller = this.#signing.get(id);\n if (!controller) return;\n this.#signing.delete(id);\n controller.abort(new DOMException('The sign-in was cancelled.', 'AbortError'));\n this.emit('accounts', this.accounts());\n }\n\n disconnect(id: string): void {\n void this.#settings.disconnect(id, BACKGROUND_CONTEXT);\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/sprinkles/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
replicatedState
|
|
3
3
|
} from "@earendil-works/chord";
|
|
4
|
+
import { live } from "../agents.js";
|
|
4
5
|
import {
|
|
5
6
|
describe,
|
|
6
7
|
homePath,
|
|
@@ -43,6 +44,12 @@ function named(agents, value) {
|
|
|
43
44
|
const id = value.startsWith("scoop:") ? value : `scoop:${value}`;
|
|
44
45
|
return id in state.scoops ? id : void 0;
|
|
45
46
|
}
|
|
47
|
+
function ownerOf(agents, owner) {
|
|
48
|
+
const state = agents.state();
|
|
49
|
+
if (owner && state.cones[owner]) return owner;
|
|
50
|
+
const scoop = owner ? state.scoops[owner] : void 0;
|
|
51
|
+
return scoop && live(scoop) ? owner : agents.activeCone();
|
|
52
|
+
}
|
|
46
53
|
function entry(name, html, agentId) {
|
|
47
54
|
return { id: name, name, ...describe(name, html), agentId, html };
|
|
48
55
|
}
|
|
@@ -58,14 +65,15 @@ function reloader(options, dir, builtins, sprinkles, owners) {
|
|
|
58
65
|
for (const { name, path } of await sprinkleFiles(env, dir, using)) {
|
|
59
66
|
if (reserved.has(name)) continue;
|
|
60
67
|
const read = await env.readTextFile(path, using);
|
|
61
|
-
if (read.ok) out.push(entry(name, read.value, owned[name]
|
|
68
|
+
if (read.ok) out.push(entry(name, read.value, ownerOf(agents, owned[name])));
|
|
62
69
|
}
|
|
63
70
|
if (offered)
|
|
64
|
-
out.push(
|
|
65
|
-
entry(SUGGESTIONS, suggestions, owned[SUGGESTIONS] ?? agents.activeCone())
|
|
66
|
-
);
|
|
71
|
+
out.push(entry(SUGGESTIONS, suggestions, ownerOf(agents, owned[SUGGESTIONS])));
|
|
67
72
|
if (welcome !== void 0)
|
|
68
|
-
out.push({
|
|
73
|
+
out.push({
|
|
74
|
+
...entry(WELCOME, welcome, ownerOf(agents, owned[WELCOME] ?? "cone")),
|
|
75
|
+
inline: true
|
|
76
|
+
});
|
|
69
77
|
sprinkles.replace(using, out);
|
|
70
78
|
};
|
|
71
79
|
}
|
|
@@ -166,7 +174,7 @@ function setupSprinkles() {
|
|
|
166
174
|
title: sprinkle.title,
|
|
167
175
|
text: payload.action || "(no action)",
|
|
168
176
|
...payload.data === null ? {} : { body: JSON.stringify(payload.data, null, 2) },
|
|
169
|
-
target: targetOf((await owners(using))[id]
|
|
177
|
+
target: targetOf(ownerOf(agents, (await owners(using))[id]))
|
|
170
178
|
},
|
|
171
179
|
using
|
|
172
180
|
);
|
|
@@ -219,6 +227,7 @@ function setupSprinkles() {
|
|
|
219
227
|
}
|
|
220
228
|
export {
|
|
221
229
|
USAGE,
|
|
230
|
+
ownerOf,
|
|
222
231
|
setupSprinkles
|
|
223
232
|
};
|
|
224
233
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/sprinkles/index.ts"],
|
|
4
|
-
"sourcesContent": ["import {\n type Context,\n type JsonValue,\n type ReplicatedState,\n replicatedState,\n} from '@earendil-works/chord';\nimport type { Harness } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv, FileWatcher } from '@earendil-works/pi-durable/env';\nimport type { Agents } from '../agents.ts';\nimport type { LickTarget } from '../licks/lick.ts';\nimport type { Licks } from '../licks/licks.ts';\nimport {\n describe,\n homePath,\n NO_EXEC,\n SPRINKLE_KIND,\n SPRINKLES_DIR,\n type Sprinkle,\n type SprinkleMethod,\n SprinklesDoc,\n SUGGESTIONS,\n SUGGESTIONS_DIR,\n SUGGESTIONS_STORE,\n sprinkleFiles,\n WELCOME,\n WELCOMED,\n} from './sprinkles.ts';\n\nexport type Answer = { code: number; out: string };\n\nexport interface SprinklePayload {\n action: string;\n data: JsonValue | null;\n target: string | null;\n}\n\nexport interface SprinklesRuntime {\n readonly sprinkles: ReplicatedState<Sprinkle[]>;\n send(id: string, payload: SprinklePayload, context: Context): Promise<{ delivered: boolean }>;\n call(id: string, method: string, args: readonly unknown[], context: Context): Promise<JsonValue>;\n command(argv: readonly string[], caller: string | null, context: Context): Promise<Answer>;\n reload(context: Context): Promise<void>;\n close(context: Context): Promise<void>;\n}\n\nexport interface SprinklesAttach {\n harness: Harness;\n agents: Agents;\n licks: Licks;\n env: ExecutionEnv;\n assets?: (path: string) => Promise<string>;\n reloadMs: number;\n dir?: string;\n intercept?: (id: string, payload: SprinklePayload, context: Context) => Promise<boolean>;\n}\n\nexport const USAGE = `usage:\n sprinkle list every sprinkle in /home/sprinkles, with its owner\n sprinkle show <name> show a sprinkle in your chat\n sprinkle own <name> [<agent>] send its messages to <agent> (cone, cone-<n> or a scoop's handle)\n\nA sprinkle is an .shtml file in /home/sprinkles, as <name>.shtml or <name>/<name>.shtml.\nEvery one of them has a button in the rail; edit the file and the open panel reloads.\n`;\n\nconst RETIRED: Record<string, string> = {\n open: 'every sprinkle in /home/sprinkles already has a button in the rail; sprinkle show <name> shows one in the chat',\n reload: 'editing the .shtml file reloads the sprinkle',\n close: 'delete the .shtml file to retire a sprinkle; the user closes panels',\n send: 'a sprinkle reads data with slicc.readFile() or keeps it with slicc.setState(); write the file it reads',\n chat: 'show the sprinkle with sprinkle show <name> and wait for its lick',\n};\n\nfunction targetOf(agentId: string): LickTarget {\n return agentId.startsWith('scoop:') ? (agentId as LickTarget) : `cone:${agentId}`;\n}\n\nfunction agentOf(caller: string | null, agents: Agents): string {\n if (caller?.startsWith('cone:')) return caller.slice('cone:'.length);\n return caller ?? agents.activeCone();\n}\n\nfunction named(agents: Agents, value: string): string | undefined {\n const state = agents.state();\n if (value in state.cones) return value;\n const id = value.startsWith('scoop:') ? value : `scoop:${value}`;\n return id in state.scoops ? id : undefined;\n}\n\nfunction entry(name: string, html: string, agentId: string): Sprinkle {\n return { id: name, name, ...describe(name, html), agentId, html };\n}\n\nfunction reloader(\n options: SprinklesAttach,\n dir: string,\n builtins: { welcome: string | undefined; suggestions: string | undefined },\n sprinkles: ReplicatedState<Sprinkle[]> & { replace(context: Context, value: Sprinkle[]): void },\n owners: (context: Context) => Promise<Record<string, string>>\n) {\n const { env, agents } = options;\n const { welcome, suggestions } = builtins;\n return async (using: Context) => {\n const owned = await owners(using);\n const stored =\n suggestions === undefined ? undefined : await env.exists(SUGGESTIONS_STORE, using);\n const offered = stored?.ok === true && stored.value;\n const reserved = new Set([WELCOME, ...(offered ? [SUGGESTIONS] : [])]);\n const out: Sprinkle[] = [];\n for (const { name, path } of await sprinkleFiles(env, dir, using)) {\n if (reserved.has(name)) continue;\n const read = await env.readTextFile(path, using);\n if (read.ok) out.push(entry(name, read.value, owned[name] ?? agents.activeCone()));\n }\n if (offered)\n out.push(\n entry(SUGGESTIONS, suggestions as string, owned[SUGGESTIONS] ?? agents.activeCone())\n );\n if (welcome !== undefined)\n out.push({ ...entry(WELCOME, welcome, owned[WELCOME] ?? 'cone'), inline: true });\n sprinkles.replace(using, out);\n };\n}\n\nexport function setupSprinkles() {\n return {\n async attach(options: SprinklesAttach, context: Context): Promise<SprinklesRuntime> {\n const { harness, agents, licks, env } = options;\n const dir = options.dir ?? SPRINKLES_DIR;\n const sprinkles = replicatedState<Sprinkle[]>([]);\n const builtin = (name: string) =>\n options.assets\n ? options.assets(`packages/vfs-root/sprinkles/${name}.shtml`).catch(() => undefined)\n : Promise.resolve(undefined);\n const welcome = await builtin(WELCOME);\n const suggestions = await builtin(SUGGESTIONS);\n const owners = async (using: Context) =>\n (await harness.snapshot(SprinklesDoc, using))?.owners ?? {};\n const reload = reloader(options, dir, { welcome, suggestions }, sprinkles, owners);\n const inside = async (path: unknown, using: Context) => {\n const asked = homePath(path);\n const real = await env.canonicalPath(asked, using);\n return real.ok ? homePath(real.value) : asked;\n };\n const offCone = agents.onCone(() => reload(context).catch(() => undefined));\n const find = (id: string) => sprinkles.value.find((item) => item.id === id);\n const post = async (id: string, agentId: string, using: Context) => {\n const conversation = await agents.conversation(agentId, using);\n if (!conversation) throw new Error(`there is no agent ${agentId}`);\n await conversation.submit(\n { type: 'write', entry: { kind: SPRINKLE_KIND, data: { sprinkle: id, at: Date.now() } } },\n using\n );\n };\n const setOwner = async (name: string, agentId: string, using: Context) => {\n await harness.commit(async (tx) => {\n (await tx.doc(SprinklesDoc)).owners[name] = agentId;\n }, using);\n await reload(using);\n };\n await reload(context);\n const state = await harness.snapshot(SprinklesDoc, context);\n if (welcome !== undefined && !state?.welcomed) {\n await post(WELCOME, 'cone', context);\n await harness.commit(async (tx) => {\n (await tx.doc(SprinklesDoc)).welcomed = true;\n }, context);\n }\n let timer: ReturnType<typeof setTimeout> | undefined;\n const watched = await env.watch(\n [{ path: dir, recursive: true }, { path: SUGGESTIONS_DIR }],\n () => {\n clearTimeout(timer);\n timer = setTimeout(() => void reload(context).catch(() => undefined), options.reloadMs);\n },\n context\n );\n const watcher: FileWatcher | undefined = watched.ok ? watched.value : undefined;\n const commands: Record<\n string,\n (argv: readonly string[], caller: string | null, using: Context) => Promise<Answer>\n > = {\n async list(_argv, _caller, using) {\n await reload(using);\n const rows = sprinkles.value\n .filter((item) => !item.inline)\n .map((item) => `${item.name}\\t${item.title}\\t${item.agentId}`);\n return { code: 0, out: rows.length ? `${rows.join('\\n')}\\n` : 'no sprinkles\\n' };\n },\n async show(argv, caller, using) {\n await reload(using);\n const name = argv[1] ?? '';\n if (!find(name)) return { code: 1, out: `sprinkle: there is no sprinkle ${name}\\n` };\n const agentId = agentOf(caller, agents);\n await post(name, agentId, using);\n if (name !== SUGGESTIONS && !(await owners(using))[name])\n await setOwner(name, agentId, using);\n return { code: 0, out: `showed ${name} in the chat of ${agentId}\\n` };\n },\n async own(argv, caller, using) {\n const name = argv[1] ?? '';\n if (!find(name)) return { code: 1, out: `sprinkle: there is no sprinkle ${name}\\n` };\n const agentId = argv[2] ? named(agents, argv[2]) : agentOf(caller, agents);\n if (!agentId) return { code: 1, out: `sprinkle: there is no agent ${argv[2]}\\n` };\n await setOwner(name, agentId, using);\n return { code: 0, out: `${name} now talks to ${agentId}\\n` };\n },\n };\n return {\n sprinkles,\n async send(id, payload, using) {\n const sprinkle = find(id);\n if (!sprinkle) return { delivered: false };\n if (await options.intercept?.(id, payload, using)) return { delivered: true };\n if (id === WELCOME && payload.action === 'onboarding-complete')\n await env.writeFile(WELCOMED, `${new Date().toISOString()}\\n`, using);\n await licks.deliver(\n {\n channel: 'sprinkle',\n source: `sprinkle:${id}`,\n title: sprinkle.title,\n text: payload.action || '(no action)',\n ...(payload.data === null ? {} : { body: JSON.stringify(payload.data, null, 2) }),\n target: targetOf((await owners(using))[id] ?? agents.activeCone()),\n },\n using\n );\n return { delivered: true };\n },\n async call(id, method, args, using) {\n if (!find(id)) throw new Error(`there is no sprinkle ${id}`);\n const handlers: Record<SprinkleMethod, () => Promise<JsonValue>> = {\n async readFile() {\n const read = await env.readTextFile(await inside(args[0], using), using);\n if (!read.ok) throw new Error(read.error.message);\n return read.value;\n },\n async exists() {\n const found = await env.exists(await inside(args[0], using), using);\n return found.ok && found.value;\n },\n async getState() {\n return (await harness.snapshot(SprinklesDoc, using))?.state[id] ?? null;\n },\n async setState() {\n await harness.commit(async (tx) => {\n (await tx.doc(SprinklesDoc)).state[id] = (args[0] ?? null) as JsonValue;\n }, using);\n return null;\n },\n };\n const handler = handlers[method as SprinkleMethod];\n if (!handler) throw new Error(NO_EXEC);\n return handler();\n },\n async command(argv, caller, using) {\n const verb = argv[0] ?? 'help';\n const run = commands[verb];\n if (run) return run(argv, caller, using);\n if (verb in RETIRED)\n return { code: 2, out: `sprinkle ${verb} isn't in SLICC: ${RETIRED[verb]}\\n` };\n return { code: verb === 'help' || verb === '--help' ? 0 : 2, out: USAGE };\n },\n reload,\n async close(using) {\n offCone();\n clearTimeout(timer);\n await watcher?.close(using);\n },\n };\n },\n };\n}\n"],
|
|
5
|
-
"mappings": "AAAA;AAAA,EAIE;AAAA,OACK;
|
|
4
|
+
"sourcesContent": ["import {\n type Context,\n type JsonValue,\n type ReplicatedState,\n replicatedState,\n} from '@earendil-works/chord';\nimport type { Harness } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv, FileWatcher } from '@earendil-works/pi-durable/env';\nimport { type Agents, live } from '../agents.ts';\nimport type { LickTarget } from '../licks/lick.ts';\nimport type { Licks } from '../licks/licks.ts';\nimport {\n describe,\n homePath,\n NO_EXEC,\n SPRINKLE_KIND,\n SPRINKLES_DIR,\n type Sprinkle,\n type SprinkleMethod,\n SprinklesDoc,\n SUGGESTIONS,\n SUGGESTIONS_DIR,\n SUGGESTIONS_STORE,\n sprinkleFiles,\n WELCOME,\n WELCOMED,\n} from './sprinkles.ts';\n\nexport type Answer = { code: number; out: string };\n\nexport interface SprinklePayload {\n action: string;\n data: JsonValue | null;\n target: string | null;\n}\n\nexport interface SprinklesRuntime {\n readonly sprinkles: ReplicatedState<Sprinkle[]>;\n send(id: string, payload: SprinklePayload, context: Context): Promise<{ delivered: boolean }>;\n call(id: string, method: string, args: readonly unknown[], context: Context): Promise<JsonValue>;\n command(argv: readonly string[], caller: string | null, context: Context): Promise<Answer>;\n reload(context: Context): Promise<void>;\n close(context: Context): Promise<void>;\n}\n\nexport interface SprinklesAttach {\n harness: Harness;\n agents: Agents;\n licks: Licks;\n env: ExecutionEnv;\n assets?: (path: string) => Promise<string>;\n reloadMs: number;\n dir?: string;\n intercept?: (id: string, payload: SprinklePayload, context: Context) => Promise<boolean>;\n}\n\nexport const USAGE = `usage:\n sprinkle list every sprinkle in /home/sprinkles, with its owner\n sprinkle show <name> show a sprinkle in your chat\n sprinkle own <name> [<agent>] send its messages to <agent> (cone, cone-<n> or a scoop's handle)\n\nA sprinkle is an .shtml file in /home/sprinkles, as <name>.shtml or <name>/<name>.shtml.\nEvery one of them has a button in the rail; edit the file and the open panel reloads.\n`;\n\nconst RETIRED: Record<string, string> = {\n open: 'every sprinkle in /home/sprinkles already has a button in the rail; sprinkle show <name> shows one in the chat',\n reload: 'editing the .shtml file reloads the sprinkle',\n close: 'delete the .shtml file to retire a sprinkle; the user closes panels',\n send: 'a sprinkle reads data with slicc.readFile() or keeps it with slicc.setState(); write the file it reads',\n chat: 'show the sprinkle with sprinkle show <name> and wait for its lick',\n};\n\nfunction targetOf(agentId: string): LickTarget {\n return agentId.startsWith('scoop:') ? (agentId as LickTarget) : `cone:${agentId}`;\n}\n\nfunction agentOf(caller: string | null, agents: Agents): string {\n if (caller?.startsWith('cone:')) return caller.slice('cone:'.length);\n return caller ?? agents.activeCone();\n}\n\nfunction named(agents: Agents, value: string): string | undefined {\n const state = agents.state();\n if (value in state.cones) return value;\n const id = value.startsWith('scoop:') ? value : `scoop:${value}`;\n return id in state.scoops ? id : undefined;\n}\n\nexport function ownerOf(agents: Agents, owner: string | undefined): string {\n const state = agents.state();\n if (owner && state.cones[owner]) return owner;\n const scoop = owner ? state.scoops[owner] : undefined;\n return scoop && live(scoop) ? (owner as string) : agents.activeCone();\n}\n\nfunction entry(name: string, html: string, agentId: string): Sprinkle {\n return { id: name, name, ...describe(name, html), agentId, html };\n}\n\nfunction reloader(\n options: SprinklesAttach,\n dir: string,\n builtins: { welcome: string | undefined; suggestions: string | undefined },\n sprinkles: ReplicatedState<Sprinkle[]> & { replace(context: Context, value: Sprinkle[]): void },\n owners: (context: Context) => Promise<Record<string, string>>\n) {\n const { env, agents } = options;\n const { welcome, suggestions } = builtins;\n return async (using: Context) => {\n const owned = await owners(using);\n const stored =\n suggestions === undefined ? undefined : await env.exists(SUGGESTIONS_STORE, using);\n const offered = stored?.ok === true && stored.value;\n const reserved = new Set([WELCOME, ...(offered ? [SUGGESTIONS] : [])]);\n const out: Sprinkle[] = [];\n for (const { name, path } of await sprinkleFiles(env, dir, using)) {\n if (reserved.has(name)) continue;\n const read = await env.readTextFile(path, using);\n if (read.ok) out.push(entry(name, read.value, ownerOf(agents, owned[name])));\n }\n if (offered)\n out.push(entry(SUGGESTIONS, suggestions as string, ownerOf(agents, owned[SUGGESTIONS])));\n if (welcome !== undefined)\n out.push({\n ...entry(WELCOME, welcome, ownerOf(agents, owned[WELCOME] ?? 'cone')),\n inline: true,\n });\n sprinkles.replace(using, out);\n };\n}\n\nexport function setupSprinkles() {\n return {\n async attach(options: SprinklesAttach, context: Context): Promise<SprinklesRuntime> {\n const { harness, agents, licks, env } = options;\n const dir = options.dir ?? SPRINKLES_DIR;\n const sprinkles = replicatedState<Sprinkle[]>([]);\n const builtin = (name: string) =>\n options.assets\n ? options.assets(`packages/vfs-root/sprinkles/${name}.shtml`).catch(() => undefined)\n : Promise.resolve(undefined);\n const welcome = await builtin(WELCOME);\n const suggestions = await builtin(SUGGESTIONS);\n const owners = async (using: Context) =>\n (await harness.snapshot(SprinklesDoc, using))?.owners ?? {};\n const reload = reloader(options, dir, { welcome, suggestions }, sprinkles, owners);\n const inside = async (path: unknown, using: Context) => {\n const asked = homePath(path);\n const real = await env.canonicalPath(asked, using);\n return real.ok ? homePath(real.value) : asked;\n };\n const offCone = agents.onCone(() => reload(context).catch(() => undefined));\n const find = (id: string) => sprinkles.value.find((item) => item.id === id);\n const post = async (id: string, agentId: string, using: Context) => {\n const conversation = await agents.conversation(agentId, using);\n if (!conversation) throw new Error(`there is no agent ${agentId}`);\n await conversation.submit(\n { type: 'write', entry: { kind: SPRINKLE_KIND, data: { sprinkle: id, at: Date.now() } } },\n using\n );\n };\n const setOwner = async (name: string, agentId: string, using: Context) => {\n await harness.commit(async (tx) => {\n (await tx.doc(SprinklesDoc)).owners[name] = agentId;\n }, using);\n await reload(using);\n };\n await reload(context);\n const state = await harness.snapshot(SprinklesDoc, context);\n if (welcome !== undefined && !state?.welcomed) {\n await post(WELCOME, 'cone', context);\n await harness.commit(async (tx) => {\n (await tx.doc(SprinklesDoc)).welcomed = true;\n }, context);\n }\n let timer: ReturnType<typeof setTimeout> | undefined;\n const watched = await env.watch(\n [{ path: dir, recursive: true }, { path: SUGGESTIONS_DIR }],\n () => {\n clearTimeout(timer);\n timer = setTimeout(() => void reload(context).catch(() => undefined), options.reloadMs);\n },\n context\n );\n const watcher: FileWatcher | undefined = watched.ok ? watched.value : undefined;\n const commands: Record<\n string,\n (argv: readonly string[], caller: string | null, using: Context) => Promise<Answer>\n > = {\n async list(_argv, _caller, using) {\n await reload(using);\n const rows = sprinkles.value\n .filter((item) => !item.inline)\n .map((item) => `${item.name}\\t${item.title}\\t${item.agentId}`);\n return { code: 0, out: rows.length ? `${rows.join('\\n')}\\n` : 'no sprinkles\\n' };\n },\n async show(argv, caller, using) {\n await reload(using);\n const name = argv[1] ?? '';\n if (!find(name)) return { code: 1, out: `sprinkle: there is no sprinkle ${name}\\n` };\n const agentId = agentOf(caller, agents);\n await post(name, agentId, using);\n if (name !== SUGGESTIONS && !(await owners(using))[name])\n await setOwner(name, agentId, using);\n return { code: 0, out: `showed ${name} in the chat of ${agentId}\\n` };\n },\n async own(argv, caller, using) {\n const name = argv[1] ?? '';\n if (!find(name)) return { code: 1, out: `sprinkle: there is no sprinkle ${name}\\n` };\n const agentId = argv[2] ? named(agents, argv[2]) : agentOf(caller, agents);\n if (!agentId) return { code: 1, out: `sprinkle: there is no agent ${argv[2]}\\n` };\n await setOwner(name, agentId, using);\n return { code: 0, out: `${name} now talks to ${agentId}\\n` };\n },\n };\n return {\n sprinkles,\n async send(id, payload, using) {\n const sprinkle = find(id);\n if (!sprinkle) return { delivered: false };\n if (await options.intercept?.(id, payload, using)) return { delivered: true };\n if (id === WELCOME && payload.action === 'onboarding-complete')\n await env.writeFile(WELCOMED, `${new Date().toISOString()}\\n`, using);\n await licks.deliver(\n {\n channel: 'sprinkle',\n source: `sprinkle:${id}`,\n title: sprinkle.title,\n text: payload.action || '(no action)',\n ...(payload.data === null ? {} : { body: JSON.stringify(payload.data, null, 2) }),\n target: targetOf(ownerOf(agents, (await owners(using))[id])),\n },\n using\n );\n return { delivered: true };\n },\n async call(id, method, args, using) {\n if (!find(id)) throw new Error(`there is no sprinkle ${id}`);\n const handlers: Record<SprinkleMethod, () => Promise<JsonValue>> = {\n async readFile() {\n const read = await env.readTextFile(await inside(args[0], using), using);\n if (!read.ok) throw new Error(read.error.message);\n return read.value;\n },\n async exists() {\n const found = await env.exists(await inside(args[0], using), using);\n return found.ok && found.value;\n },\n async getState() {\n return (await harness.snapshot(SprinklesDoc, using))?.state[id] ?? null;\n },\n async setState() {\n await harness.commit(async (tx) => {\n (await tx.doc(SprinklesDoc)).state[id] = (args[0] ?? null) as JsonValue;\n }, using);\n return null;\n },\n };\n const handler = handlers[method as SprinkleMethod];\n if (!handler) throw new Error(NO_EXEC);\n return handler();\n },\n async command(argv, caller, using) {\n const verb = argv[0] ?? 'help';\n const run = commands[verb];\n if (run) return run(argv, caller, using);\n if (verb in RETIRED)\n return { code: 2, out: `sprinkle ${verb} isn't in SLICC: ${RETIRED[verb]}\\n` };\n return { code: verb === 'help' || verb === '--help' ? 0 : 2, out: USAGE };\n },\n reload,\n async close(using) {\n offCone();\n clearTimeout(timer);\n await watcher?.close(using);\n },\n };\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/types/agents.d.ts
CHANGED
|
@@ -33,6 +33,17 @@ export type ScoopRecord = {
|
|
|
33
33
|
created: Mark | null;
|
|
34
34
|
dropped: Mark | null;
|
|
35
35
|
gone: boolean;
|
|
36
|
+
frozen?: string;
|
|
37
|
+
};
|
|
38
|
+
export type FrozenRecord = {
|
|
39
|
+
cone: string;
|
|
40
|
+
name: string;
|
|
41
|
+
conversation: number;
|
|
42
|
+
title: string;
|
|
43
|
+
model: string;
|
|
44
|
+
messages: number;
|
|
45
|
+
frozenAt: number;
|
|
46
|
+
scoops: string[];
|
|
36
47
|
};
|
|
37
48
|
export declare function live(record: ScoopRecord): boolean;
|
|
38
49
|
export type AgentsState = {
|
|
@@ -40,6 +51,8 @@ export type AgentsState = {
|
|
|
40
51
|
active: string;
|
|
41
52
|
next: number;
|
|
42
53
|
scoops: Record<string, ScoopRecord>;
|
|
54
|
+
frozen?: Record<string, FrozenRecord>;
|
|
55
|
+
frozenNext?: number;
|
|
43
56
|
};
|
|
44
57
|
export declare const AgentsDoc: import("@earendil-works/pi-durable").SessionDocToken<AgentsState>;
|
|
45
58
|
export declare const SCOOP_PREFIX = "scoop:";
|
|
@@ -54,6 +67,7 @@ export interface Agents {
|
|
|
54
67
|
selectCone(id: string, context: Context): Promise<void>;
|
|
55
68
|
createCone(name: string, context: Context): Promise<string>;
|
|
56
69
|
switchCone(conversation: Conversation, context: Context): Promise<void>;
|
|
70
|
+
setConversation(coneId: string, conversation: Conversation, context: Context): Promise<void>;
|
|
57
71
|
update<T>(change: (tx: Tx, state: AgentsState) => T | Promise<T>, context: Context): Promise<T>;
|
|
58
72
|
onChange(listener: (state: Readonly<AgentsState>) => void): () => void;
|
|
59
73
|
onCone(listener: (conversation: Conversation) => void | Promise<void>): () => void;
|
package/dist/types/client.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Context, type ReplicatedState } from '@earendil-works/chord';
|
|
2
2
|
import type { ConversationView } from '@earendil-works/pi-durable';
|
|
3
|
+
import type { FrozenCone } from './freezer/index.ts';
|
|
3
4
|
import type { MemoryEntry, MemoryScope } from './memory/format.ts';
|
|
4
5
|
import { AgentControl, AgentSettings, type AgentsSummary, type Command, type Delivered, type SendMode } from './services.ts';
|
|
5
6
|
import type { Sprinkle } from './sprinkles/kind.ts';
|
|
@@ -15,6 +16,7 @@ export interface AgentConnection {
|
|
|
15
16
|
readonly sprinkles?: ReplicatedState<Sprinkle[]>;
|
|
16
17
|
readonly memories?: ReplicatedState<MemoryEntry[]>;
|
|
17
18
|
readonly memoryScopes?: ReplicatedState<MemoryScope[]>;
|
|
19
|
+
readonly frozen?: ReplicatedState<FrozenCone[]>;
|
|
18
20
|
readonly settings: AgentSettings;
|
|
19
21
|
prompt(text: string, whenBusy?: SendMode): Promise<string>;
|
|
20
22
|
close(): Promise<void>;
|
package/dist/types/control.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type Conversation, type Harness, type SubmissionId } from '@earendil-works/pi-durable';
|
|
2
2
|
import type { Cones } from './cone.ts';
|
|
3
3
|
import type { Deliveries } from './deliveries.ts';
|
|
4
|
+
import type { FreezerRuntime } from './freezer/index.ts';
|
|
4
5
|
import type { LickSources, Licks } from './licks/index.ts';
|
|
5
6
|
import type { MemoryRuntime } from './memory/index.ts';
|
|
6
7
|
import type { Scoops, ScoopsRuntime } from './scoops/index.ts';
|
|
@@ -17,4 +18,4 @@ export interface HostScoops {
|
|
|
17
18
|
}
|
|
18
19
|
export declare function readable(out: string): string;
|
|
19
20
|
export declare function submissionId(value: string): SubmissionId | undefined;
|
|
20
|
-
export declare function createAgentControl(harness: Harness, target: Conversation | Cones, deliveries?: Deliveries, licks?: HostLicks, scoops?: HostScoops, skills?: Pick<SkillsRuntime, 'expand'>, sprinkles?: Pick<SprinklesRuntime, 'send' | 'call'>, memory?: Pick<MemoryRuntime, 'save' | 'remove'>): AgentControl;
|
|
21
|
+
export declare function createAgentControl(harness: Harness, target: Conversation | Cones, deliveries?: Deliveries, licks?: HostLicks, scoops?: HostScoops, skills?: Pick<SkillsRuntime, 'expand'>, sprinkles?: Pick<SprinklesRuntime, 'send' | 'call'>, memory?: Pick<MemoryRuntime, 'save' | 'remove'>, freezer?: Pick<FreezerRuntime, 'freeze' | 'newChat' | 'thaw' | 'discard'>): AgentControl;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type Context, type ReplicatedState } from '@earendil-works/chord';
|
|
2
|
+
import type { EntryRecord, Harness } from '@earendil-works/pi-durable';
|
|
3
|
+
import type { ExecutionEnv } from '@earendil-works/pi-durable/env';
|
|
4
|
+
import { type Agents, type AgentsState, type FrozenRecord } from '../agents.ts';
|
|
5
|
+
import type { LickTarget } from '../licks/lick.ts';
|
|
6
|
+
export type Answer = {
|
|
7
|
+
code: number;
|
|
8
|
+
out: string;
|
|
9
|
+
};
|
|
10
|
+
export interface FrozenCone {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
title: string;
|
|
14
|
+
model: string;
|
|
15
|
+
messages: number;
|
|
16
|
+
frozenAt: number;
|
|
17
|
+
}
|
|
18
|
+
export type Frozen = {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string;
|
|
21
|
+
at: number;
|
|
22
|
+
};
|
|
23
|
+
export interface FreezerAttach {
|
|
24
|
+
harness: Harness;
|
|
25
|
+
agents: Agents;
|
|
26
|
+
env: Pick<ExecutionEnv, 'remove'>;
|
|
27
|
+
title?: (transcript: string, model: string, context: Context) => Promise<string | undefined>;
|
|
28
|
+
extract?: (conversation: number, tail: number, context: Context, cone: string) => Promise<unknown>;
|
|
29
|
+
now?: () => number;
|
|
30
|
+
}
|
|
31
|
+
export interface FreezerRuntime {
|
|
32
|
+
readonly frozen: ReplicatedState<FrozenCone[]>;
|
|
33
|
+
freeze(agentId: string, context: Context): Promise<string>;
|
|
34
|
+
newChat(agentId: string, context: Context): Promise<string | null>;
|
|
35
|
+
thaw(id: string, context: Context): Promise<string>;
|
|
36
|
+
discard(id: string, context: Context): Promise<void>;
|
|
37
|
+
frozenTarget(target: LickTarget): Frozen | undefined;
|
|
38
|
+
command(argv: readonly string[], caller: string | null, context: Context): Promise<Answer>;
|
|
39
|
+
close(): void;
|
|
40
|
+
}
|
|
41
|
+
export declare const USAGE = "usage:\n freezer list every frozen chat: id, name, title, messages, when\n freezer show <id> a frozen chat as Markdown\n freezer thaw <id> bring a frozen chat back as a cone\n\nFreeze a cone, or start a new chat, from the chat toolbar or the command palette.\n";
|
|
42
|
+
export declare function modelName(model: {
|
|
43
|
+
provider: string;
|
|
44
|
+
modelId: string;
|
|
45
|
+
} | undefined): string;
|
|
46
|
+
export declare function firstLine(entries: readonly EntryRecord[]): string;
|
|
47
|
+
export declare function markdown(record: FrozenRecord, entries: readonly EntryRecord[]): string;
|
|
48
|
+
export declare function frozenTarget(state: Readonly<AgentsState>, target: LickTarget): Frozen | undefined;
|
|
49
|
+
export declare function attachFreezer(options: FreezerAttach): FreezerRuntime;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Context } from '@earendil-works/chord';
|
|
2
|
+
import type { Models } from '@earendil-works/pi-ai/models';
|
|
3
|
+
import type { ExecutionEnv } from '@earendil-works/pi-durable/env';
|
|
4
|
+
export declare const TITLE_PROMPT = "Give this chat a title of three to six words that says what it was about. Answer with the title only, without quotes.";
|
|
5
|
+
export declare function settingsText(env: Pick<ExecutionEnv, 'readTextFile'>, home: string): (context: Context) => Promise<string | undefined>;
|
|
6
|
+
export declare function titler(models: Models, settings: (context: Context) => Promise<string | undefined>): (transcript: string, model: string, context: Context) => Promise<string | undefined>;
|
package/dist/types/host.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type Context } from '@earendil-works/chord';
|
|
2
2
|
import type { Agent } from './agent.ts';
|
|
3
3
|
import { type HostLicks, type HostScoops } from './control.ts';
|
|
4
|
+
import type { FreezerRuntime } from './freezer/index.ts';
|
|
4
5
|
import type { MemoryRuntime } from './memory/index.ts';
|
|
5
6
|
import { AgentSettings } from './services.ts';
|
|
6
7
|
import type { SkillsRuntime } from './skills/index.ts';
|
|
@@ -21,4 +22,5 @@ export declare function hostAgent(agent: Agent, options?: {
|
|
|
21
22
|
skills?: SkillsRuntime;
|
|
22
23
|
sprinkles?: SprinklesRuntime;
|
|
23
24
|
memory?: MemoryRuntime;
|
|
25
|
+
freezer?: FreezerRuntime;
|
|
24
26
|
}): Promise<AgentHost>;
|
|
@@ -46,7 +46,14 @@ export interface Licks {
|
|
|
46
46
|
handle(channel: LickChannel, handler: LickHandler): void;
|
|
47
47
|
pending(context: Context): Promise<number>;
|
|
48
48
|
forget(target: LickTarget, channel: LickChannel, source: string, context: Context): Promise<void>;
|
|
49
|
+
guard(check: (target: LickTarget) => Blocked | undefined): void;
|
|
49
50
|
}
|
|
51
|
+
export type Blocked = {
|
|
52
|
+
id: string;
|
|
53
|
+
name: string;
|
|
54
|
+
at: number;
|
|
55
|
+
};
|
|
56
|
+
export declare function frozenNotice(event: LickEvent, blocked: Blocked): LickEvent;
|
|
50
57
|
export interface LicksHost {
|
|
51
58
|
harness: Harness;
|
|
52
59
|
resolve(target: LickTarget, channel: LickChannel, context: Context): Promise<Conversation>;
|
|
@@ -40,7 +40,7 @@ export type ExtractResult = {
|
|
|
40
40
|
skipped: string | null;
|
|
41
41
|
};
|
|
42
42
|
export declare function extractTask(ready: Promise<Host>, settings: (context: Context) => Promise<ExtractSettings>): import("@earendil-works/pi-durable").Task<Input, State, ExtractResult, object>;
|
|
43
|
-
export type Schedule = (conversation: number, tail: number, context: Context) => Promise<boolean>;
|
|
43
|
+
export type Schedule = (conversation: number, tail: number, context: Context, cone?: string) => Promise<boolean>;
|
|
44
44
|
export declare function compactionHook(schedule: Schedule): import("@earendil-works/pi-durable").HookRegistration;
|
|
45
45
|
export declare function scheduler(ready: Promise<Host>, task: ReturnType<typeof extractTask>, settings: (context: Context) => Promise<ExtractSettings>): Schedule;
|
|
46
46
|
export declare function settingsReader(ready: Promise<Host>): (context: Context) => Promise<ExtractSettings>;
|