@ai-ecoverse/slicc-agent 6.12.0 → 6.13.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/dist/licks/lick.js +16 -1
- package/dist/licks/lick.js.map +2 -2
- package/dist/licks/licks.js +14 -4
- package/dist/licks/licks.js.map +2 -2
- package/dist/scoops/extension.js +2 -0
- package/dist/scoops/extension.js.map +2 -2
- package/dist/scoops/summary.js +69 -0
- package/dist/scoops/summary.js.map +7 -0
- package/dist/spectrum/messages.js +3 -1
- package/dist/spectrum/messages.js.map +2 -2
- package/dist/types/licks/lick.d.ts +4 -0
- package/dist/types/licks/licks.d.ts +2 -0
- package/dist/types/scoops/summary.d.ts +9 -0
- package/package.json +3 -3
package/dist/licks/lick.js
CHANGED
|
@@ -17,6 +17,11 @@ const LICK_CHANNELS = [
|
|
|
17
17
|
"scoop-wait",
|
|
18
18
|
"sudo-request"
|
|
19
19
|
];
|
|
20
|
+
const SUMMARY = 120;
|
|
21
|
+
function clip(value, size = SUMMARY) {
|
|
22
|
+
const line = value.replace(/\s+/g, " ").trim();
|
|
23
|
+
return line.length > size ? `${line.slice(0, size).trimEnd()}\u2026` : line;
|
|
24
|
+
}
|
|
20
25
|
const ENTITIES = {
|
|
21
26
|
"&": "&",
|
|
22
27
|
"<": "<",
|
|
@@ -43,6 +48,11 @@ function lickId() {
|
|
|
43
48
|
function oneLine(value) {
|
|
44
49
|
return value.replace(/\s+/g, " ").trim();
|
|
45
50
|
}
|
|
51
|
+
function card(attributes) {
|
|
52
|
+
const summary = attributes.get("summary");
|
|
53
|
+
const report = attributes.get("report");
|
|
54
|
+
return { ...summary ? { summary } : {}, ...report ? { report } : {} };
|
|
55
|
+
}
|
|
46
56
|
function formatLick(lick) {
|
|
47
57
|
const attributes = [
|
|
48
58
|
["id", lick.id],
|
|
@@ -54,6 +64,8 @@ function formatLick(lick) {
|
|
|
54
64
|
];
|
|
55
65
|
if (lick.actions?.length) attributes.push(["actions", lick.actions.join(" ")]);
|
|
56
66
|
if (lick.severity) attributes.push(["severity", lick.severity]);
|
|
67
|
+
if (lick.summary) attributes.push(["summary", oneLine(lick.summary)]);
|
|
68
|
+
if (lick.report) attributes.push(["report", lick.report]);
|
|
57
69
|
const head = attributes.map(([name, value]) => `${name}="${escapeAttribute(value)}"`).join(" ");
|
|
58
70
|
const lines = [escapeText(oneLine(lick.text))];
|
|
59
71
|
if (lick.body) lines.push(escapeText(lick.body));
|
|
@@ -92,11 +104,14 @@ function parseLick(text) {
|
|
|
92
104
|
count: Number.isSafeInteger(count) && count > 0 ? count : 1,
|
|
93
105
|
at: Number.isNaN(at) ? 0 : at,
|
|
94
106
|
...actions.length ? { actions } : {},
|
|
95
|
-
...severityOf(attributes.get("severity"))
|
|
107
|
+
...severityOf(attributes.get("severity")),
|
|
108
|
+
...card(attributes)
|
|
96
109
|
};
|
|
97
110
|
}
|
|
98
111
|
export {
|
|
99
112
|
LICK_CHANNELS,
|
|
113
|
+
SUMMARY,
|
|
114
|
+
clip,
|
|
100
115
|
escapeAttribute,
|
|
101
116
|
escapeText,
|
|
102
117
|
formatLick,
|
package/dist/licks/lick.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/licks/lick.ts"],
|
|
4
|
-
"sourcesContent": ["export const LICK_CHANNELS = [\n 'webhook',\n 'cron',\n 'sprinkle',\n 'fswatch',\n 'session-reload',\n 'navigate',\n 'discovery',\n 'upgrade',\n 'workflow',\n 'bash',\n 'jshd',\n 'preview',\n 'cherry',\n 'scoop-notify',\n 'scoop-idle',\n 'scoop-wait',\n 'sudo-request',\n] as const;\n\nexport type LickChannel = (typeof LICK_CHANNELS)[number];\n\nexport type LickAction = 'confirm' | 'dismiss';\n\nexport type LickSeverity = 'warn' | 'error';\n\nexport type LickTarget = 'cone' | `cone:${string}` | `scoop:${string}`;\n\nexport type Lick = {\n id: string;\n channel: LickChannel;\n source: string;\n title: string;\n text: string;\n body?: string;\n count: number;\n at: number;\n actions?: LickAction[];\n severity?: LickSeverity;\n};\n\nconst ENTITIES: Record<string, string> = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n '\\n': ' ',\n '\\r': ' ',\n};\n\nconst DECODE: Record<string, string> = Object.fromEntries(\n Object.entries(ENTITIES).map(([raw, entity]) => [entity, raw])\n);\n\nexport function escapeText(value: string): string {\n return value.replace(/[&<>]/g, (char) => ENTITIES[char] as string).replace(/\\r/g, ' ');\n}\n\nexport function escapeAttribute(value: string): string {\n return value.replace(/[&<>\"\\n\\r]/g, (char) => ENTITIES[char] as string);\n}\n\nexport function unescape(value: string): string {\n return value.replace(/&(?:amp|lt|gt|quot|#10|#13);/g, (entity) => DECODE[entity] as string);\n}\n\nexport function lickId(): string {\n return `lk-${Date.now().toString(36)}-${crypto.randomUUID().slice(0, 8)}`;\n}\n\nfunction oneLine(value: string): string {\n return value.replace(/\\s+/g, ' ').trim();\n}\n\nexport function formatLick(lick: Lick): string {\n const attributes: [string, string][] = [\n ['id', lick.id],\n ['channel', lick.channel],\n ['source', lick.source],\n ['title', oneLine(lick.title)],\n ['count', String(lick.count)],\n ['at', new Date(lick.at).toISOString()],\n ];\n if (lick.actions?.length) attributes.push(['actions', lick.actions.join(' ')]);\n if (lick.severity) attributes.push(['severity', lick.severity]);\n const head = attributes.map(([name, value]) => `${name}=\"${escapeAttribute(value)}\"`).join(' ');\n const lines = [escapeText(oneLine(lick.text))];\n if (lick.body) lines.push(escapeText(lick.body));\n return `<lick ${head}>\\n${lines.join('\\n')}\\n</lick>`;\n}\n\nconst LICK = /^<lick ((?:[a-z]+=\"[^\"<>\\n]*\" ?)+)>\\n([^\\n]*)(?:\\n([\\s\\S]*?))?\\n<\\/lick>$/;\nconst ATTRIBUTE = /([a-z]+)=\"([^\"]*)\"/g;\n\nfunction isChannel(value: string | undefined): value is LickChannel {\n return (LICK_CHANNELS as readonly string[]).includes(value ?? '');\n}\n\nfunction severityOf(value: string | undefined): { severity?: LickSeverity } {\n return value === 'warn' || value === 'error' ? { severity: value } : {};\n}\n\nexport function parseLick(text: string): Lick | undefined {\n const match = LICK.exec(text);\n if (!match) return undefined;\n const attributes = new Map<string, string>();\n for (const [, name, value] of (match[1] as string).matchAll(ATTRIBUTE))\n attributes.set(name as string, unescape(value as string));\n const channel = attributes.get('channel');\n const id = attributes.get('id');\n if (!id || !isChannel(channel)) return undefined;\n const at = Date.parse(attributes.get('at') ?? '');\n const count = Number(attributes.get('count'));\n const actions = (attributes.get('actions') ?? '')\n .split(' ')\n .filter((action): action is LickAction => action === 'confirm' || action === 'dismiss');\n const body = match[3] === undefined ? undefined : unescape(match[3]);\n return {\n id,\n channel,\n source: attributes.get('source') ?? '',\n title: attributes.get('title') ?? '',\n text: unescape(match[2] as string),\n ...(body ? { body } : {}),\n count: Number.isSafeInteger(count) && count > 0 ? count : 1,\n at: Number.isNaN(at) ? 0 : at,\n ...(actions.length ? { actions } : {}),\n ...severityOf(attributes.get('severity')),\n };\n}\n"],
|
|
5
|
-
"mappings": "AAAO,MAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;
|
|
4
|
+
"sourcesContent": ["export const LICK_CHANNELS = [\n 'webhook',\n 'cron',\n 'sprinkle',\n 'fswatch',\n 'session-reload',\n 'navigate',\n 'discovery',\n 'upgrade',\n 'workflow',\n 'bash',\n 'jshd',\n 'preview',\n 'cherry',\n 'scoop-notify',\n 'scoop-idle',\n 'scoop-wait',\n 'sudo-request',\n] as const;\n\nexport type LickChannel = (typeof LICK_CHANNELS)[number];\n\nexport type LickAction = 'confirm' | 'dismiss';\n\nexport type LickSeverity = 'warn' | 'error';\n\nexport const SUMMARY = 120;\n\nexport function clip(value: string, size = SUMMARY): string {\n const line = value.replace(/\\s+/g, ' ').trim();\n return line.length > size ? `${line.slice(0, size).trimEnd()}\u2026` : line;\n}\n\nexport type LickTarget = 'cone' | `cone:${string}` | `scoop:${string}`;\n\nexport type Lick = {\n id: string;\n channel: LickChannel;\n source: string;\n title: string;\n text: string;\n body?: string;\n count: number;\n at: number;\n actions?: LickAction[];\n severity?: LickSeverity;\n summary?: string;\n report?: string;\n};\n\nconst ENTITIES: Record<string, string> = {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n '\\n': ' ',\n '\\r': ' ',\n};\n\nconst DECODE: Record<string, string> = Object.fromEntries(\n Object.entries(ENTITIES).map(([raw, entity]) => [entity, raw])\n);\n\nexport function escapeText(value: string): string {\n return value.replace(/[&<>]/g, (char) => ENTITIES[char] as string).replace(/\\r/g, ' ');\n}\n\nexport function escapeAttribute(value: string): string {\n return value.replace(/[&<>\"\\n\\r]/g, (char) => ENTITIES[char] as string);\n}\n\nexport function unescape(value: string): string {\n return value.replace(/&(?:amp|lt|gt|quot|#10|#13);/g, (entity) => DECODE[entity] as string);\n}\n\nexport function lickId(): string {\n return `lk-${Date.now().toString(36)}-${crypto.randomUUID().slice(0, 8)}`;\n}\n\nfunction oneLine(value: string): string {\n return value.replace(/\\s+/g, ' ').trim();\n}\n\nfunction card(attributes: Map<string, string>): { summary?: string; report?: string } {\n const summary = attributes.get('summary');\n const report = attributes.get('report');\n return { ...(summary ? { summary } : {}), ...(report ? { report } : {}) };\n}\n\nexport function formatLick(lick: Lick): string {\n const attributes: [string, string][] = [\n ['id', lick.id],\n ['channel', lick.channel],\n ['source', lick.source],\n ['title', oneLine(lick.title)],\n ['count', String(lick.count)],\n ['at', new Date(lick.at).toISOString()],\n ];\n if (lick.actions?.length) attributes.push(['actions', lick.actions.join(' ')]);\n if (lick.severity) attributes.push(['severity', lick.severity]);\n if (lick.summary) attributes.push(['summary', oneLine(lick.summary)]);\n if (lick.report) attributes.push(['report', lick.report]);\n const head = attributes.map(([name, value]) => `${name}=\"${escapeAttribute(value)}\"`).join(' ');\n const lines = [escapeText(oneLine(lick.text))];\n if (lick.body) lines.push(escapeText(lick.body));\n return `<lick ${head}>\\n${lines.join('\\n')}\\n</lick>`;\n}\n\nconst LICK = /^<lick ((?:[a-z]+=\"[^\"<>\\n]*\" ?)+)>\\n([^\\n]*)(?:\\n([\\s\\S]*?))?\\n<\\/lick>$/;\nconst ATTRIBUTE = /([a-z]+)=\"([^\"]*)\"/g;\n\nfunction isChannel(value: string | undefined): value is LickChannel {\n return (LICK_CHANNELS as readonly string[]).includes(value ?? '');\n}\n\nfunction severityOf(value: string | undefined): { severity?: LickSeverity } {\n return value === 'warn' || value === 'error' ? { severity: value } : {};\n}\n\nexport function parseLick(text: string): Lick | undefined {\n const match = LICK.exec(text);\n if (!match) return undefined;\n const attributes = new Map<string, string>();\n for (const [, name, value] of (match[1] as string).matchAll(ATTRIBUTE))\n attributes.set(name as string, unescape(value as string));\n const channel = attributes.get('channel');\n const id = attributes.get('id');\n if (!id || !isChannel(channel)) return undefined;\n const at = Date.parse(attributes.get('at') ?? '');\n const count = Number(attributes.get('count'));\n const actions = (attributes.get('actions') ?? '')\n .split(' ')\n .filter((action): action is LickAction => action === 'confirm' || action === 'dismiss');\n const body = match[3] === undefined ? undefined : unescape(match[3]);\n return {\n id,\n channel,\n source: attributes.get('source') ?? '',\n title: attributes.get('title') ?? '',\n text: unescape(match[2] as string),\n ...(body ? { body } : {}),\n count: Number.isSafeInteger(count) && count > 0 ? count : 1,\n at: Number.isNaN(at) ? 0 : at,\n ...(actions.length ? { actions } : {}),\n ...severityOf(attributes.get('severity')),\n ...card(attributes),\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/licks/licks.js
CHANGED
|
@@ -3,11 +3,18 @@ import {
|
|
|
3
3
|
} from "@earendil-works/pi-durable";
|
|
4
4
|
import { scrubTexts } from "../secrets/scrub.js";
|
|
5
5
|
import {
|
|
6
|
+
clip,
|
|
6
7
|
formatLick,
|
|
7
8
|
lickId
|
|
8
9
|
} from "./lick.js";
|
|
9
10
|
import { entryLick, LICK_STATE_KIND, lickDecisions } from "./state.js";
|
|
10
11
|
import { skipEntry, skips } from "./triage.js";
|
|
12
|
+
function card(event) {
|
|
13
|
+
return {
|
|
14
|
+
...event.summary ? { summary: event.summary } : {},
|
|
15
|
+
...event.report ? { report: event.report } : {}
|
|
16
|
+
};
|
|
17
|
+
}
|
|
11
18
|
const LicksOutbox = defineDoc({
|
|
12
19
|
kind: "slicc.licks",
|
|
13
20
|
version: 1,
|
|
@@ -48,7 +55,7 @@ function strongest(a, b) {
|
|
|
48
55
|
function merge(pending, event, now) {
|
|
49
56
|
const items = [...pending.items];
|
|
50
57
|
for (const item of event.items ?? []) if (!items.includes(item)) items.push(item);
|
|
51
|
-
const { severity: _, ...lick } = pending.lick;
|
|
58
|
+
const { severity: _, summary: __, report: ___, ...lick } = pending.lick;
|
|
52
59
|
const severity = strongest(pending.lick.severity, event.severity);
|
|
53
60
|
return {
|
|
54
61
|
lick: {
|
|
@@ -57,7 +64,8 @@ function merge(pending, event, now) {
|
|
|
57
64
|
text: event.text,
|
|
58
65
|
count: pending.lick.count + (event.count ?? 1),
|
|
59
66
|
at: event.at ?? now,
|
|
60
|
-
...severity ? { severity } : {}
|
|
67
|
+
...severity ? { severity } : {},
|
|
68
|
+
...card(event)
|
|
61
69
|
},
|
|
62
70
|
items,
|
|
63
71
|
body: event.body ?? pending.body
|
|
@@ -74,7 +82,8 @@ function fresh(event, now, listed) {
|
|
|
74
82
|
count: event.count ?? 1,
|
|
75
83
|
at: now,
|
|
76
84
|
...listed ? { actions: listed } : {},
|
|
77
|
-
...event.severity ? { severity: event.severity } : {}
|
|
85
|
+
...event.severity ? { severity: event.severity } : {},
|
|
86
|
+
...card(event)
|
|
78
87
|
},
|
|
79
88
|
items: [...new Set(event.items ?? [])],
|
|
80
89
|
body: event.body ?? null
|
|
@@ -85,7 +94,7 @@ function keyOf(event) {
|
|
|
85
94
|
return event.coalesce === false ? `${base}|${event.eventId ?? lickId()}` : base;
|
|
86
95
|
}
|
|
87
96
|
async function scrubbedLick(event) {
|
|
88
|
-
const fields = ["title", "text", "body", "triage"].filter(
|
|
97
|
+
const fields = ["title", "text", "body", "triage", "summary", "report"].filter(
|
|
89
98
|
(key) => event[key] !== void 0
|
|
90
99
|
);
|
|
91
100
|
const items = event.items ?? [];
|
|
@@ -95,6 +104,7 @@ async function scrubbedLick(event) {
|
|
|
95
104
|
out[key] = scrubbed[at];
|
|
96
105
|
});
|
|
97
106
|
if (event.items) out.items = scrubbed.slice(fields.length);
|
|
107
|
+
if (out.summary !== void 0) out.summary = clip(out.summary);
|
|
98
108
|
return out;
|
|
99
109
|
}
|
|
100
110
|
function createLicks(host) {
|
package/dist/licks/licks.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/licks/licks.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n type Conversation,\n defineDoc,\n type Harness,\n type SubmissionId,\n} from '@earendil-works/pi-durable';\nimport { scrubTexts } from '../secrets/scrub.ts';\nimport {\n formatLick,\n type Lick,\n type LickAction,\n type LickChannel,\n type LickSeverity,\n type LickTarget,\n lickId,\n} from './lick.ts';\nimport { entryLick, LICK_STATE_KIND, type LickDecision, lickDecisions } from './state.ts';\nimport { skipEntry, skips, type Triage, type Verdict } from './triage.ts';\n\nexport interface LickEvent {\n channel: LickChannel;\n source: string;\n title: string;\n text: string;\n body?: string;\n items?: string[];\n count?: number;\n target: LickTarget;\n coalesce?: boolean;\n severity?: LickSeverity;\n eventId?: string;\n at?: number;\n triage?: string;\n}\n\ntype Pending = {\n lick: Lick;\n items: string[];\n body: string | null;\n};\n\ntype Queue = {\n submission: number | null;\n pending: Pending | null;\n};\n\ntype OutboxState = {\n queues: Record<string, Queue>;\n events: Record<string, string[]>;\n};\n\nexport const LicksOutbox = defineDoc<OutboxState>({\n kind: 'slicc.licks',\n version: 1,\n scope: 'session',\n initial: () => ({ queues: {}, events: {} }),\n});\n\nexport interface LickHandler {\n confirm?(lick: Lick, reason: string | undefined, context: Context): Promise<string | undefined>;\n dismiss?(lick: Lick, reason: string | undefined, context: Context): Promise<string | undefined>;\n}\n\nexport interface LickFound {\n lick: Lick;\n decision: LickDecision | undefined;\n}\n\nexport interface Licks {\n deliver(event: LickEvent, context: Context): Promise<void>;\n flush(context: Context): Promise<void>;\n find(conversation: Conversation, id: string, context: Context): Promise<LickFound | undefined>;\n decide(\n conversation: Conversation,\n id: string,\n action: LickAction,\n reason: string | undefined,\n context: Context\n ): Promise<string>;\n handle(channel: LickChannel, handler: LickHandler): void;\n pending(context: Context): Promise<number>;\n forget(target: LickTarget, channel: LickChannel, source: string, context: Context): Promise<void>;\n guard(check: (target: LickTarget) => Blocked | undefined): void;\n triage(judge: Triage): void;\n}\n\nexport type Blocked = { id: string; name: string; at: number };\n\nexport function frozenNotice(event: LickEvent, blocked: Blocked): LickEvent {\n return {\n channel: event.channel,\n source: `frozen:${event.source}`,\n title: `${blocked.name} is frozen`,\n text: `${event.title} was skipped`,\n body: `${event.target} belongs to ${blocked.name}, which is frozen (${blocked.id}). Thaw it in the Freezer to get its licks again.`,\n target: 'cone',\n severity: 'warn',\n eventId: `${blocked.id}|${event.target}|${event.channel}|${event.source}|${blocked.at}`,\n };\n}\n\nexport interface LicksHost {\n harness: Harness;\n resolve(target: LickTarget, channel: LickChannel, context: Context): Promise<Conversation>;\n}\n\nexport const MAX_ITEMS = 50;\nexport const MAX_EVENTS = 64;\n\nexport function render(pending: Pending): Lick {\n const shown = pending.items.slice(0, MAX_ITEMS);\n const more = pending.items.length - shown.length;\n const lines = [\n ...(pending.body ? [pending.body] : []),\n ...shown,\n ...(more > 0 ? [`\u2026 and ${more} more`] : []),\n ];\n const { body: _, ...lick } = pending.lick;\n return lines.length ? { ...lick, body: lines.join('\\n') } : lick;\n}\n\nconst RANK: Record<LickSeverity, number> = { warn: 1, error: 2 };\n\nexport function strongest(\n a: LickSeverity | undefined,\n b: LickSeverity | undefined\n): LickSeverity | undefined {\n if (!a) return b;\n if (!b) return a;\n return RANK[b] > RANK[a] ? b : a;\n}\n\nfunction merge(pending: Pending, event: LickEvent, now: number): Pending {\n const items = [...pending.items];\n for (const item of event.items ?? []) if (!items.includes(item)) items.push(item);\n const { severity: _, ...lick } = pending.lick;\n const severity = strongest(pending.lick.severity, event.severity);\n return {\n lick: {\n ...lick,\n title: event.title,\n text: event.text,\n count: pending.lick.count + (event.count ?? 1),\n at: event.at ?? now,\n ...(severity ? { severity } : {}),\n },\n items,\n body: event.body ?? pending.body,\n };\n}\n\nfunction fresh(event: LickEvent, now: number, listed: LickAction[] | undefined): Pending {\n return {\n lick: {\n id: lickId(),\n channel: event.channel,\n source: event.source,\n title: event.title,\n text: event.text,\n count: event.count ?? 1,\n at: now,\n ...(listed ? { actions: listed } : {}),\n ...(event.severity ? { severity: event.severity } : {}),\n },\n items: [...new Set(event.items ?? [])],\n body: event.body ?? null,\n };\n}\n\nfunction keyOf(event: LickEvent): string {\n const base = `${event.target}|${event.channel}|${event.source}`;\n return event.coalesce === false ? `${base}|${event.eventId ?? lickId()}` : base;\n}\n\nexport async function scrubbedLick(event: LickEvent): Promise<LickEvent> {\n const fields = (['title', 'text', 'body', 'triage'] as const).filter(\n (key) => event[key] !== undefined\n );\n const items = event.items ?? [];\n const scrubbed = await scrubTexts([...fields.map((key) => event[key] as string), ...items]);\n const out: LickEvent = { ...event };\n fields.forEach((key, at) => {\n out[key] = scrubbed[at] as string;\n });\n if (event.items) out.items = scrubbed.slice(fields.length);\n return out;\n}\n\nexport function createLicks(host: Promise<LicksHost>): Licks {\n const handlers = new Map<LickChannel, LickHandler>();\n let lock: Promise<unknown> = Promise.resolve();\n const serial = <T>(operation: () => Promise<T>): Promise<T> => {\n const run = lock.then(operation, operation);\n lock = run.catch(() => undefined);\n return run;\n };\n\n const actions = (channel: LickChannel): LickAction[] | undefined => {\n const handler = handlers.get(channel);\n const list: LickAction[] = [];\n if (handler?.confirm) list.push('confirm');\n if (handler?.dismiss) list.push('dismiss');\n return list.length ? list : undefined;\n };\n\n async function previous(\n harness: Harness,\n id: number | null,\n context: Context\n ): Promise<'queued' | 'withdrawn' | 'gone'> {\n if (id === null) return 'gone';\n const submission = await harness.submission(id as SubmissionId, context);\n const record = await submission?.status(context);\n if (record?.status === 'queued') return 'queued';\n return record?.status === 'unanswered' && record.reason === 'aborted' ? 'withdrawn' : 'gone';\n }\n\n async function settled(key: string, context: Context): Promise<Queue | undefined> {\n const { harness } = await host;\n const queue = (await harness.snapshot(LicksOutbox, context))?.queues[key];\n if (!queue) return undefined;\n const before = await previous(harness, queue.submission, context);\n if (before === 'queued') return undefined;\n if (queue.pending && before === 'gone') return queue;\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n if (doc.queues[key]?.submission === queue.submission) delete doc.queues[key];\n }, context);\n return undefined;\n }\n\n async function flushKey(key: string, context: Context): Promise<void> {\n const { harness, resolve } = await host;\n const queue = await settled(key, context);\n if (!queue?.pending) return;\n const lick = render(queue.pending);\n const target = key.split('|')[0] as LickTarget;\n const conversation = await resolve(target, queue.pending.lick.channel, context);\n const submission = await conversation.submit(\n {\n type: 'input',\n content: formatLick(lick),\n whenBusy: 'steer',\n requestId: `lick:${lick.id}`,\n },\n context\n );\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n doc.queues[key] = { submission: submission.id, pending: null };\n }, context);\n }\n\n async function flushAll(context: Context): Promise<void> {\n const { harness } = await host;\n const keys = Object.keys((await harness.snapshot(LicksOutbox, context))?.queues ?? {});\n for (const key of keys) await flushKey(key, context);\n }\n\n async function find(\n conversation: Conversation,\n id: string,\n context: Context\n ): Promise<LickFound | undefined> {\n const { entries } = await conversation.context(context);\n const lick = entries.map(entryLick).find((candidate) => candidate?.id === id);\n if (!lick) return undefined;\n return { lick, decision: lickDecisions(entries).get(id) };\n }\n\n async function judged(event: LickEvent, context: Context): Promise<Verdict | undefined> {\n if (!judge || !event.triage) return undefined;\n return judge(event, context).catch(() => undefined);\n }\n\n async function seenBefore(event: LickEvent, context: Context): Promise<boolean> {\n if (event.eventId === undefined) return false;\n const { harness } = await host;\n const events = (await harness.snapshot(LicksOutbox, context))?.events ?? {};\n return (events[`${event.channel}|${event.source}`] ?? []).includes(event.eventId);\n }\n\n async function record(event: LickEvent, verdict: Verdict, now: number, context: Context) {\n const conversation = await (await host).resolve(event.target, event.channel, context);\n await conversation.submit(skipEntry(event, verdict, now), context);\n }\n\n let check: ((target: LickTarget) => Blocked | undefined) | undefined;\n let judge: Triage | undefined;\n return {\n guard(next) {\n check = next;\n },\n triage(next) {\n judge = next;\n },\n async deliver(raw, context) {\n const incoming = await scrubbedLick(raw);\n const blocked = check?.(incoming.target);\n const event = blocked ? frozenNotice(incoming, blocked) : incoming;\n if (judge && event.triage && (await seenBefore(event, context))) return;\n const verdict = await judged(event, context);\n const skipped = skips(verdict);\n return serial(async () => {\n const { harness } = await host;\n const key = keyOf(event);\n await settled(key, context);\n const stream = `${event.channel}|${event.source}`;\n const now = event.at ?? Date.now();\n if (skipped) {\n if (await seenBefore(event, context)) return;\n await record(event, verdict, now, context);\n }\n const accepted = await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n const seen = doc.events[stream] ?? [];\n if (event.eventId !== undefined) {\n if (seen.includes(event.eventId)) return false;\n doc.events[stream] = [...seen, event.eventId].slice(-MAX_EVENTS);\n }\n if (skipped) return false;\n const queue = doc.queues[key] ?? { submission: null, pending: null };\n queue.pending = queue.pending\n ? merge(queue.pending, event, now)\n : fresh(event, now, actions(event.channel));\n doc.queues[key] = queue;\n return true;\n }, context);\n if (accepted) await flushKey(key, context);\n });\n },\n flush: (context) => serial(() => flushAll(context)),\n find,\n async decide(conversation, id, action, reason, context) {\n const found = await find(conversation, id, context);\n if (!found) throw new Error(`There is no lick ${id} in this conversation.`);\n if (found.decision) throw new Error(`Lick ${id} is already ${found.decision}.`);\n if (!found.lick.actions?.includes(action))\n throw new Error(`Lick ${id} (${found.lick.channel}) has no ${action} action.`);\n const run = handlers.get(found.lick.channel)?.[action];\n if (!run) throw new Error(`Lick ${id} (${found.lick.channel}) has no ${action} action.`);\n return (await run(found.lick, reason, context)) ?? `Lick ${id} ${action}ed.`;\n },\n handle(channel, handler) {\n handlers.set(channel, handler);\n },\n forget(target, channel, source, context) {\n return serial(async () => {\n const { harness } = await host;\n const key = `${target}|${channel}|${source}`;\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n const queue = doc.queues[key];\n if (queue?.pending) queue.pending = null;\n }, context);\n });\n },\n async pending(context) {\n const { harness } = await host;\n const queues = (await harness.snapshot(LicksOutbox, context))?.queues ?? {};\n return Object.values(queues).filter((queue) => queue.submission !== null || queue.pending)\n .length;\n },\n };\n}\n\nexport { LICK_STATE_KIND };\n"],
|
|
5
|
-
"mappings": "AACA;AAAA,EAEE;AAAA,OAGK;AACP,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EAMA;AAAA,OACK;AACP,SAAS,WAAW,iBAAoC,qBAAqB;AAC7E,SAAS,WAAW,aAAwC;
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n type Conversation,\n defineDoc,\n type Harness,\n type SubmissionId,\n} from '@earendil-works/pi-durable';\nimport { scrubTexts } from '../secrets/scrub.ts';\nimport {\n clip,\n formatLick,\n type Lick,\n type LickAction,\n type LickChannel,\n type LickSeverity,\n type LickTarget,\n lickId,\n} from './lick.ts';\nimport { entryLick, LICK_STATE_KIND, type LickDecision, lickDecisions } from './state.ts';\nimport { skipEntry, skips, type Triage, type Verdict } from './triage.ts';\n\nexport interface LickEvent {\n channel: LickChannel;\n source: string;\n title: string;\n text: string;\n body?: string;\n items?: string[];\n count?: number;\n target: LickTarget;\n coalesce?: boolean;\n severity?: LickSeverity;\n eventId?: string;\n at?: number;\n triage?: string;\n summary?: string;\n report?: string;\n}\n\nfunction card(event: LickEvent): Pick<Lick, 'summary' | 'report'> {\n return {\n ...(event.summary ? { summary: event.summary } : {}),\n ...(event.report ? { report: event.report } : {}),\n };\n}\n\ntype Pending = {\n lick: Lick;\n items: string[];\n body: string | null;\n};\n\ntype Queue = {\n submission: number | null;\n pending: Pending | null;\n};\n\ntype OutboxState = {\n queues: Record<string, Queue>;\n events: Record<string, string[]>;\n};\n\nexport const LicksOutbox = defineDoc<OutboxState>({\n kind: 'slicc.licks',\n version: 1,\n scope: 'session',\n initial: () => ({ queues: {}, events: {} }),\n});\n\nexport interface LickHandler {\n confirm?(lick: Lick, reason: string | undefined, context: Context): Promise<string | undefined>;\n dismiss?(lick: Lick, reason: string | undefined, context: Context): Promise<string | undefined>;\n}\n\nexport interface LickFound {\n lick: Lick;\n decision: LickDecision | undefined;\n}\n\nexport interface Licks {\n deliver(event: LickEvent, context: Context): Promise<void>;\n flush(context: Context): Promise<void>;\n find(conversation: Conversation, id: string, context: Context): Promise<LickFound | undefined>;\n decide(\n conversation: Conversation,\n id: string,\n action: LickAction,\n reason: string | undefined,\n context: Context\n ): Promise<string>;\n handle(channel: LickChannel, handler: LickHandler): void;\n pending(context: Context): Promise<number>;\n forget(target: LickTarget, channel: LickChannel, source: string, context: Context): Promise<void>;\n guard(check: (target: LickTarget) => Blocked | undefined): void;\n triage(judge: Triage): void;\n}\n\nexport type Blocked = { id: string; name: string; at: number };\n\nexport function frozenNotice(event: LickEvent, blocked: Blocked): LickEvent {\n return {\n channel: event.channel,\n source: `frozen:${event.source}`,\n title: `${blocked.name} is frozen`,\n text: `${event.title} was skipped`,\n body: `${event.target} belongs to ${blocked.name}, which is frozen (${blocked.id}). Thaw it in the Freezer to get its licks again.`,\n target: 'cone',\n severity: 'warn',\n eventId: `${blocked.id}|${event.target}|${event.channel}|${event.source}|${blocked.at}`,\n };\n}\n\nexport interface LicksHost {\n harness: Harness;\n resolve(target: LickTarget, channel: LickChannel, context: Context): Promise<Conversation>;\n}\n\nexport const MAX_ITEMS = 50;\nexport const MAX_EVENTS = 64;\n\nexport function render(pending: Pending): Lick {\n const shown = pending.items.slice(0, MAX_ITEMS);\n const more = pending.items.length - shown.length;\n const lines = [\n ...(pending.body ? [pending.body] : []),\n ...shown,\n ...(more > 0 ? [`\u2026 and ${more} more`] : []),\n ];\n const { body: _, ...lick } = pending.lick;\n return lines.length ? { ...lick, body: lines.join('\\n') } : lick;\n}\n\nconst RANK: Record<LickSeverity, number> = { warn: 1, error: 2 };\n\nexport function strongest(\n a: LickSeverity | undefined,\n b: LickSeverity | undefined\n): LickSeverity | undefined {\n if (!a) return b;\n if (!b) return a;\n return RANK[b] > RANK[a] ? b : a;\n}\n\nfunction merge(pending: Pending, event: LickEvent, now: number): Pending {\n const items = [...pending.items];\n for (const item of event.items ?? []) if (!items.includes(item)) items.push(item);\n const { severity: _, summary: __, report: ___, ...lick } = pending.lick;\n const severity = strongest(pending.lick.severity, event.severity);\n return {\n lick: {\n ...lick,\n title: event.title,\n text: event.text,\n count: pending.lick.count + (event.count ?? 1),\n at: event.at ?? now,\n ...(severity ? { severity } : {}),\n ...card(event),\n },\n items,\n body: event.body ?? pending.body,\n };\n}\n\nfunction fresh(event: LickEvent, now: number, listed: LickAction[] | undefined): Pending {\n return {\n lick: {\n id: lickId(),\n channel: event.channel,\n source: event.source,\n title: event.title,\n text: event.text,\n count: event.count ?? 1,\n at: now,\n ...(listed ? { actions: listed } : {}),\n ...(event.severity ? { severity: event.severity } : {}),\n ...card(event),\n },\n items: [...new Set(event.items ?? [])],\n body: event.body ?? null,\n };\n}\n\nfunction keyOf(event: LickEvent): string {\n const base = `${event.target}|${event.channel}|${event.source}`;\n return event.coalesce === false ? `${base}|${event.eventId ?? lickId()}` : base;\n}\n\nexport async function scrubbedLick(event: LickEvent): Promise<LickEvent> {\n const fields = (['title', 'text', 'body', 'triage', 'summary', 'report'] as const).filter(\n (key) => event[key] !== undefined\n );\n const items = event.items ?? [];\n const scrubbed = await scrubTexts([...fields.map((key) => event[key] as string), ...items]);\n const out: LickEvent = { ...event };\n fields.forEach((key, at) => {\n out[key] = scrubbed[at] as string;\n });\n if (event.items) out.items = scrubbed.slice(fields.length);\n if (out.summary !== undefined) out.summary = clip(out.summary);\n return out;\n}\n\nexport function createLicks(host: Promise<LicksHost>): Licks {\n const handlers = new Map<LickChannel, LickHandler>();\n let lock: Promise<unknown> = Promise.resolve();\n const serial = <T>(operation: () => Promise<T>): Promise<T> => {\n const run = lock.then(operation, operation);\n lock = run.catch(() => undefined);\n return run;\n };\n\n const actions = (channel: LickChannel): LickAction[] | undefined => {\n const handler = handlers.get(channel);\n const list: LickAction[] = [];\n if (handler?.confirm) list.push('confirm');\n if (handler?.dismiss) list.push('dismiss');\n return list.length ? list : undefined;\n };\n\n async function previous(\n harness: Harness,\n id: number | null,\n context: Context\n ): Promise<'queued' | 'withdrawn' | 'gone'> {\n if (id === null) return 'gone';\n const submission = await harness.submission(id as SubmissionId, context);\n const record = await submission?.status(context);\n if (record?.status === 'queued') return 'queued';\n return record?.status === 'unanswered' && record.reason === 'aborted' ? 'withdrawn' : 'gone';\n }\n\n async function settled(key: string, context: Context): Promise<Queue | undefined> {\n const { harness } = await host;\n const queue = (await harness.snapshot(LicksOutbox, context))?.queues[key];\n if (!queue) return undefined;\n const before = await previous(harness, queue.submission, context);\n if (before === 'queued') return undefined;\n if (queue.pending && before === 'gone') return queue;\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n if (doc.queues[key]?.submission === queue.submission) delete doc.queues[key];\n }, context);\n return undefined;\n }\n\n async function flushKey(key: string, context: Context): Promise<void> {\n const { harness, resolve } = await host;\n const queue = await settled(key, context);\n if (!queue?.pending) return;\n const lick = render(queue.pending);\n const target = key.split('|')[0] as LickTarget;\n const conversation = await resolve(target, queue.pending.lick.channel, context);\n const submission = await conversation.submit(\n {\n type: 'input',\n content: formatLick(lick),\n whenBusy: 'steer',\n requestId: `lick:${lick.id}`,\n },\n context\n );\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n doc.queues[key] = { submission: submission.id, pending: null };\n }, context);\n }\n\n async function flushAll(context: Context): Promise<void> {\n const { harness } = await host;\n const keys = Object.keys((await harness.snapshot(LicksOutbox, context))?.queues ?? {});\n for (const key of keys) await flushKey(key, context);\n }\n\n async function find(\n conversation: Conversation,\n id: string,\n context: Context\n ): Promise<LickFound | undefined> {\n const { entries } = await conversation.context(context);\n const lick = entries.map(entryLick).find((candidate) => candidate?.id === id);\n if (!lick) return undefined;\n return { lick, decision: lickDecisions(entries).get(id) };\n }\n\n async function judged(event: LickEvent, context: Context): Promise<Verdict | undefined> {\n if (!judge || !event.triage) return undefined;\n return judge(event, context).catch(() => undefined);\n }\n\n async function seenBefore(event: LickEvent, context: Context): Promise<boolean> {\n if (event.eventId === undefined) return false;\n const { harness } = await host;\n const events = (await harness.snapshot(LicksOutbox, context))?.events ?? {};\n return (events[`${event.channel}|${event.source}`] ?? []).includes(event.eventId);\n }\n\n async function record(event: LickEvent, verdict: Verdict, now: number, context: Context) {\n const conversation = await (await host).resolve(event.target, event.channel, context);\n await conversation.submit(skipEntry(event, verdict, now), context);\n }\n\n let check: ((target: LickTarget) => Blocked | undefined) | undefined;\n let judge: Triage | undefined;\n return {\n guard(next) {\n check = next;\n },\n triage(next) {\n judge = next;\n },\n async deliver(raw, context) {\n const incoming = await scrubbedLick(raw);\n const blocked = check?.(incoming.target);\n const event = blocked ? frozenNotice(incoming, blocked) : incoming;\n if (judge && event.triage && (await seenBefore(event, context))) return;\n const verdict = await judged(event, context);\n const skipped = skips(verdict);\n return serial(async () => {\n const { harness } = await host;\n const key = keyOf(event);\n await settled(key, context);\n const stream = `${event.channel}|${event.source}`;\n const now = event.at ?? Date.now();\n if (skipped) {\n if (await seenBefore(event, context)) return;\n await record(event, verdict, now, context);\n }\n const accepted = await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n const seen = doc.events[stream] ?? [];\n if (event.eventId !== undefined) {\n if (seen.includes(event.eventId)) return false;\n doc.events[stream] = [...seen, event.eventId].slice(-MAX_EVENTS);\n }\n if (skipped) return false;\n const queue = doc.queues[key] ?? { submission: null, pending: null };\n queue.pending = queue.pending\n ? merge(queue.pending, event, now)\n : fresh(event, now, actions(event.channel));\n doc.queues[key] = queue;\n return true;\n }, context);\n if (accepted) await flushKey(key, context);\n });\n },\n flush: (context) => serial(() => flushAll(context)),\n find,\n async decide(conversation, id, action, reason, context) {\n const found = await find(conversation, id, context);\n if (!found) throw new Error(`There is no lick ${id} in this conversation.`);\n if (found.decision) throw new Error(`Lick ${id} is already ${found.decision}.`);\n if (!found.lick.actions?.includes(action))\n throw new Error(`Lick ${id} (${found.lick.channel}) has no ${action} action.`);\n const run = handlers.get(found.lick.channel)?.[action];\n if (!run) throw new Error(`Lick ${id} (${found.lick.channel}) has no ${action} action.`);\n return (await run(found.lick, reason, context)) ?? `Lick ${id} ${action}ed.`;\n },\n handle(channel, handler) {\n handlers.set(channel, handler);\n },\n forget(target, channel, source, context) {\n return serial(async () => {\n const { harness } = await host;\n const key = `${target}|${channel}|${source}`;\n await harness.commit(async (tx) => {\n const doc = await tx.doc(LicksOutbox);\n const queue = doc.queues[key];\n if (queue?.pending) queue.pending = null;\n }, context);\n });\n },\n async pending(context) {\n const { harness } = await host;\n const queues = (await harness.snapshot(LicksOutbox, context))?.queues ?? {};\n return Object.values(queues).filter((queue) => queue.submission !== null || queue.pending)\n .length;\n },\n };\n}\n\nexport { LICK_STATE_KIND };\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["record"]
|
|
7
7
|
}
|
package/dist/scoops/extension.js
CHANGED
|
@@ -13,6 +13,7 @@ import { normalize, resolve } from "../kernel/paths.js";
|
|
|
13
13
|
import { LEGACY } from "../layout.js";
|
|
14
14
|
import { FROM_KIND } from "./from.js";
|
|
15
15
|
import { ScoopWorkDoc } from "./service.js";
|
|
16
|
+
import { cardOf } from "./summary.js";
|
|
16
17
|
const PREVIEW = 1e3;
|
|
17
18
|
function scoopFacts(record, layout = LEGACY) {
|
|
18
19
|
const place = layout.workspace(record.folder);
|
|
@@ -39,6 +40,7 @@ function reportLick(record, id, report, route = {}) {
|
|
|
39
40
|
text: `[scoop ${record.name} (${record.role ?? "scoop"}) ${status}]`,
|
|
40
41
|
body: [where, reason, preview].filter(Boolean).join("\n\n"),
|
|
41
42
|
target: route.target ?? `cone:${record.cone}`,
|
|
43
|
+
...cardOf(record.name, report),
|
|
42
44
|
...report.failed && report.failed !== "aborted" ? { severity: "warn" } : {}
|
|
43
45
|
};
|
|
44
46
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/scoops/extension.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n AssistantEntry,\n type ConversationId,\n defineExtension,\n defineTask,\n type Extension,\n hook,\n type PromptInput,\n type Submission,\n section,\n ToolTask,\n type Tx,\n} from '@earendil-works/pi-durable';\nimport { answerText } from '../agent.ts';\nimport { AgentsDoc, live, type ScoopRecord } from '../agents.ts';\nimport { HOME } from '../kernel/env.ts';\nimport { normalize, resolve } from '../kernel/paths.ts';\nimport { type Layout, LEGACY } from '../layout.ts';\nimport type { LickEvent } from '../licks/licks.ts';\nimport { FROM_KIND } from './from.ts';\nimport { type Feed, type ScoopsHost, ScoopWorkDoc } from './service.ts';\n\nexport const PREVIEW = 1000;\n\ntype Lookup = () => Promise<ScoopsHost>;\n\nexport function scoopFacts(\n record: Pick<ScoopRecord, 'name' | 'folder' | 'role'> & { cone?: string },\n layout: Layout = LEGACY\n): string {\n const place = layout.workspace(record.folder);\n return [\n `You are the scoop \"${record.name}\"${record.role ? ` (role: ${record.role})` : ''}, a helper agent working for a cone. Messages come from the cone or from the user at a terminal; your final answer goes back to whoever asked, so end every task with a complete answer.`,\n layout.users\n ? `You run as the user ${layout.scoopRoot(record.folder).slice('/home/'.length)}, and your home is ${place}: work there. Your cone can read your home; you can't read your cone's home or another scoop's. /tmp is shared scratch.`\n : `Work in ${place}. Your read, write and edit tools only reach ${layout.scoopRoot(record.folder)} and /tmp, and read ${HOME}.`,\n layout.users\n ? 'Your bash runs as your user too, so the kernel decides what it may read and change. Stay in your home, and change nothing outside it unless the request says so.'\n : 'Your bash is not confined: it sees the whole file system and every process. Stay in your folder anyway, and change nothing outside it unless the request says so.',\n 'You may run `agent` synchronously for a bounded side task (calls nest at most three deep), but you cannot start asynchronous scoops (`agent --async`, `subagent spawn`).',\n ].join('\\n');\n}\n\nfunction lines(text: string): number {\n return text ? text.split('\\n').length : 0;\n}\n\nexport type ReportResult = { text: string; failed: string | null; path: string | null };\n\nexport function reportLick(\n record: ScoopRecord,\n id: string,\n report: ReportResult,\n route: { target?: string | null; channel?: 'scoop-notify' | 'bash' } = {}\n): LickEvent {\n const status = report.failed ? (report.failed === 'aborted' ? 'stopped' : 'failed') : 'completed';\n const label = `${record.name} (${record.role ?? 'scoop'})`;\n const preview = report.text.length > PREVIEW ? `${report.text.slice(0, PREVIEW)}\u2026` : report.text;\n const where = report.path ? `Output: ${report.path} (${lines(report.text)} lines)` : null;\n const reason = report.failed && report.failed !== 'aborted' ? `Reason: ${report.failed}` : null;\n return {\n channel: route.channel ?? 'scoop-notify',\n source: id,\n title: label,\n text: `[scoop ${record.name} (${record.role ?? 'scoop'}) ${status}]`,\n body: [where, reason, preview].filter(Boolean).join('\\n\\n'),\n target: (route.target ?? `cone:${record.cone}`) as LickEvent['target'],\n ...(report.failed && report.failed !== 'aborted' ? { severity: 'warn' as const } : {}),\n };\n}\n\ntype Noting = {\n commit: (\n write: (\n tx: Tx\n ) => Promise<{ status: 'running'; checkpoint: { phase: 'deliver'; noted: true } }>,\n context: Context\n ) => Promise<unknown>;\n};\n\nexport async function noteWhenPlaced(\n runtime: Noting,\n submission: Pick<Submission, 'status'>,\n conversation: ConversationId,\n note: { from: string | undefined; agent?: boolean; noted: boolean | undefined },\n context: Context\n): Promise<void> {\n const { from } = note;\n while (from && !note.noted && !context.abortSignal?.aborted) {\n const record = await submission.status(context);\n if (record.status !== 'queued') {\n if (!('entry' in record) || record.entry === undefined) return;\n const entry = record.entry;\n await runtime\n .commit(async (tx) => {\n await tx.appendEntry(conversation, {\n kind: FROM_KIND,\n data: { from, entry, ...(note.agent ? { agent: true } : {}) },\n });\n return { status: 'running', checkpoint: { phase: 'deliver', noted: true } };\n }, context)\n .catch(() => undefined);\n return;\n }\n await new Promise((done) => setTimeout(done, NOTE_POLL_MS));\n }\n}\n\nexport const NOTE_POLL_MS = 100;\n\nexport function waitTask(lookup: Lookup) {\n type WaitInput = { scoops: string[]; cone: string; deadline: number };\n\n return defineTask<WaitInput, { phase: 'start' } | { phase: 'watch' }, null>({\n name: 'slicc.scoop-wait',\n version: 1,\n initial: () => ({ phase: 'start' }),\n phases: {\n async start(task, runtime, context) {\n await runtime.commit(async (tx) => {\n (await tx.doc(ScoopWorkDoc)).waits[String(task.id)] = {\n scoops: task.input.scoops,\n done: {},\n cone: task.input.cone,\n };\n return { status: 'running', checkpoint: { phase: 'watch' } };\n }, context);\n },\n async watch(task, runtime, context) {\n const { agents, licks } = await lookup();\n const key = String(task.id);\n for (;;) {\n const work = await runtime.snapshot(ScoopWorkDoc, context);\n const state = work?.waits[key];\n if (!state) break;\n const feeding = new Set(Object.values(work?.feeds ?? {}).map((feed) => feed.scoop));\n const open = state.scoops.filter((id) => !(id in state.done) && feeding.has(id));\n const expired = runtime.now() >= task.input.deadline;\n if (open.length && !expired) {\n await runtime.sleep(Math.min(runtime.now() + 300, task.input.deadline), context);\n continue;\n }\n const names = (id: string) => agents.state().scoops[id]?.name ?? id;\n const body = state.scoops.map((id) =>\n open.includes(id)\n ? `## ${names(id)}\\n(still working when the wait timed out)`\n : `## ${names(id)}\\n${state.done[id] ?? '(nothing was running)'}`\n );\n await licks.deliver(\n {\n channel: 'scoop-wait',\n source: key,\n title: `Waited for ${state.scoops.map(names).join(', ')}`,\n text: open.length\n ? `${state.scoops.length - open.length} of ${state.scoops.length} done; timed out waiting for ${open.map(names).join(', ')}`\n : `All ${state.scoops.length} done`,\n body: body.join('\\n\\n'),\n target: `cone:${state.cone}`,\n coalesce: false,\n eventId: `wait:${key}`,\n },\n context\n );\n break;\n }\n await runtime.commit(async (tx) => {\n delete (await tx.doc(ScoopWorkDoc)).waits[key];\n return { status: 'terminal', outcome: { status: 'completed', result: null } };\n }, context);\n },\n },\n abort: (task, runtime, context) =>\n runtime.commit(async (tx) => {\n delete (await tx.doc(ScoopWorkDoc)).waits[String(task.id)];\n return { status: 'terminal', outcome: { status: 'aborted' } };\n }, context),\n });\n}\n\nexport function scoopTasks(lookup: Lookup) {\n const anchor = defineTask<null, { phase: 'done' }, null>({\n name: 'slicc.scoop-anchor',\n version: 1,\n initial: () => ({ phase: 'done' }),\n phases: {\n done: (_task, runtime, context) =>\n runtime.commit(\n () => ({ status: 'terminal', outcome: { status: 'completed', result: null } }),\n context\n ),\n },\n abort: (_task, runtime, context) =>\n runtime.commit(() => ({ status: 'terminal', outcome: { status: 'aborted' } }), context),\n });\n\n type ReporterInput = {\n scoop: string;\n prompt: string;\n followUp?: boolean;\n request?: string;\n };\n type ReporterState =\n | { phase: 'deliver'; noted?: boolean }\n | { phase: 'report'; text: string; failed: string | null };\n\n const reporter = defineTask<ReporterInput, ReporterState, ReportResult>({\n name: 'slicc.scoop-reporter',\n version: 1,\n initial: () => ({ phase: 'deliver' }),\n phases: {\n async deliver(task, runtime, context) {\n const { agents } = await lookup();\n const record = agents.state().scoops[task.input.scoop];\n const handle =\n record && live(record)\n ? await runtime.conversation(record.conversation as ConversationId, context)\n : undefined;\n if (!handle) {\n await runtime.commit(async (tx) => {\n delete (await tx.doc(ScoopWorkDoc)).feeds[String(task.id)];\n return {\n status: 'terminal',\n outcome: {\n status: 'completed',\n result: { text: '', failed: 'the scoop is gone', path: null },\n },\n };\n }, context);\n return;\n }\n const requestId = task.input.request ? `subagent:${task.input.request}` : `feed:${task.id}`;\n const feed = (await runtime.snapshot(ScoopWorkDoc, context))?.feeds[String(task.id)];\n const submission = await handle.submit(\n {\n type: 'input',\n content: task.input.prompt,\n whenBusy: task.input.followUp ? 'followUp' : 'steer',\n requestId,\n },\n context\n );\n const noting = noteWhenPlaced(\n runtime,\n submission,\n record?.conversation as ConversationId,\n { from: feed?.from, agent: feed?.agent === true, noted: task.state.checkpoint.noted },\n context\n );\n const settled = await submission.wait(context);\n await noting;\n await runtime.commit(async (tx) => {\n if (settled.status !== 'done' || settled.type !== 'input')\n return {\n status: 'running',\n checkpoint: {\n phase: 'report',\n text: '',\n failed: String(settled.reason),\n },\n };\n const answer = await tx.entry(AssistantEntry, settled.answer);\n return {\n status: 'running',\n checkpoint: {\n phase: 'report',\n text: answerText(answer?.model?.[0]?.content),\n failed: null,\n },\n };\n }, context);\n },\n async report(task, runtime, context) {\n const { agents, licks, layout = LEGACY } = await lookup();\n const id = task.input.scoop;\n const record = agents.state().scoops[id];\n const { text, failed } = task.state.checkpoint;\n const path =\n record && text ? `${layout.reports(record.folder, record.cone)}/${task.id}.md` : null;\n if (path)\n await (await runtime.env(context))?.writeFile(\n path,\n text.endsWith('\\n') ? text : `${text}\\n`,\n context\n );\n const work = await runtime.snapshot(ScoopWorkDoc, context);\n const entry = work?.feeds[String(task.id)] as Feed;\n const waiting = (wait: { scoops: string[]; done: Record<string, string> }) =>\n wait.scoops.includes(id) && !(id in wait.done);\n const folded = Object.values(work?.waits ?? {}).some(waiting);\n const route = { target: entry.target, channel: entry.channel };\n if (!folded && record && entry.report && failed !== 'aborted')\n await licks.deliver(\n {\n ...reportLick(record, id, { text, failed, path }, route),\n eventId: `report:${task.id}`,\n },\n context\n );\n await runtime.commit(async (tx) => {\n const work = await tx.doc(ScoopWorkDoc);\n for (const wait of Object.values(work.waits))\n if (waiting(wait))\n wait.done[id] = failed\n ? `stopped: ${failed}`\n : `${path ? `Output: ${path}\\n` : ''}${text || '(no text)'}`;\n delete work.feeds[String(task.id)];\n return {\n status: 'terminal',\n outcome: { status: 'completed', result: { text, failed, path } },\n };\n }, context);\n },\n },\n abort: (task, runtime, context) =>\n runtime.commit(async (tx) => {\n delete (await tx.doc(ScoopWorkDoc)).feeds[String(task.id)];\n return { status: 'terminal', outcome: { status: 'aborted' } };\n }, context),\n });\n\n return { anchor, reporter, wait: waitTask(lookup) };\n}\n\nasync function whose(input: PromptInput, context: Context) {\n const state = await input.read.snapshot(AgentsDoc, context);\n const scoop = Object.values(state?.scoops ?? {}).find(\n (record) => record.conversation === input.conversationId\n );\n return { scoop };\n}\n\nexport function scoopsExtension(\n tasks: ReturnType<typeof scoopTasks>,\n layout: () => Layout = () => LEGACY\n): Extension {\n return defineExtension({\n name: 'slicc-scoops',\n tasks: [tasks.anchor, tasks.reporter, tasks.wait],\n sections: [\n section('scoop', async (input, context) => {\n const { scoop } = await whose(input, context);\n return scoop ? scoopFacts(scoop, layout()) : undefined;\n }),\n ],\n });\n}\n\nconst WRITES = new Set(['write', 'edit']);\n\nexport function allowed(\n record: Pick<ScoopRecord, 'folder'> & { roots?: ScoopRecord['roots'] },\n coneCwd: string,\n path: string,\n writing: boolean,\n reads: readonly string[] = [],\n own = LEGACY.scoopRoot(record.folder)\n): boolean {\n const write = [own, '/tmp', ...(record.roots?.write ?? [])];\n const roots = writing ? write : [...write, coneCwd, ...(record.roots?.read ?? []), ...reads];\n return roots.some((root) => path === root || path.startsWith(`${root === '/' ? '' : root}/`));\n}\n\nexport function guardExtension(lookup: Lookup): Extension {\n return defineExtension({\n name: 'slicc-scoop-guard',\n hooks: [\n hook(ToolTask, {\n async beforeTool(call, api, context) {\n if (call.name !== 'read' && !WRITES.has(call.name)) return undefined;\n const { agents, reads, layout = LEGACY, enforced, envOf } = await lookup();\n const record = Object.values(agents.state().scoops).find(\n (scoop) => scoop.conversation === api.conversationId\n );\n if (!record || enforced) return undefined;\n const own = layout.scoopRoot(record.folder);\n const cone = layout.users ? own : HOME;\n const path = String(call.arguments.path);\n const asked = normalize(resolve(layout.workspace(record.folder), path));\n const env = await envOf?.(api.conversationId, context);\n const canonical = await env?.canonicalPath(asked, context);\n const target = canonical?.ok ? canonical.value : asked;\n const writing = WRITES.has(call.name);\n const skills = reads?.() ?? [];\n if (\n allowed(record, cone, target, writing, skills, own) ||\n allowed(record, cone, asked, writing, skills, own)\n )\n return undefined;\n const reach = layout.users || writing ? '' : `, and reads ${HOME}`;\n return {\n block: `${target} is outside this scoop's folders. ${call.name} reaches ${own} and /tmp${reach}.`,\n };\n },\n }),\n ],\n });\n}\n"],
|
|
5
|
-
"mappings": "AACA;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EAEA;AAAA,EAGA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,kBAAkB;AAC3B,SAAS,WAAW,YAA8B;AAClD,SAAS,YAAY;AACrB,SAAS,WAAW,eAAe;AACnC,SAAsB,cAAc;AAEpC,SAAS,iBAAiB;AAC1B,SAAqC,oBAAoB;
|
|
4
|
+
"sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport {\n AssistantEntry,\n type ConversationId,\n defineExtension,\n defineTask,\n type Extension,\n hook,\n type PromptInput,\n type Submission,\n section,\n ToolTask,\n type Tx,\n} from '@earendil-works/pi-durable';\nimport { answerText } from '../agent.ts';\nimport { AgentsDoc, live, type ScoopRecord } from '../agents.ts';\nimport { HOME } from '../kernel/env.ts';\nimport { normalize, resolve } from '../kernel/paths.ts';\nimport { type Layout, LEGACY } from '../layout.ts';\nimport type { LickEvent } from '../licks/licks.ts';\nimport { FROM_KIND } from './from.ts';\nimport { type Feed, type ScoopsHost, ScoopWorkDoc } from './service.ts';\nimport { cardOf } from './summary.ts';\n\nexport const PREVIEW = 1000;\n\ntype Lookup = () => Promise<ScoopsHost>;\n\nexport function scoopFacts(\n record: Pick<ScoopRecord, 'name' | 'folder' | 'role'> & { cone?: string },\n layout: Layout = LEGACY\n): string {\n const place = layout.workspace(record.folder);\n return [\n `You are the scoop \"${record.name}\"${record.role ? ` (role: ${record.role})` : ''}, a helper agent working for a cone. Messages come from the cone or from the user at a terminal; your final answer goes back to whoever asked, so end every task with a complete answer.`,\n layout.users\n ? `You run as the user ${layout.scoopRoot(record.folder).slice('/home/'.length)}, and your home is ${place}: work there. Your cone can read your home; you can't read your cone's home or another scoop's. /tmp is shared scratch.`\n : `Work in ${place}. Your read, write and edit tools only reach ${layout.scoopRoot(record.folder)} and /tmp, and read ${HOME}.`,\n layout.users\n ? 'Your bash runs as your user too, so the kernel decides what it may read and change. Stay in your home, and change nothing outside it unless the request says so.'\n : 'Your bash is not confined: it sees the whole file system and every process. Stay in your folder anyway, and change nothing outside it unless the request says so.',\n 'You may run `agent` synchronously for a bounded side task (calls nest at most three deep), but you cannot start asynchronous scoops (`agent --async`, `subagent spawn`).',\n ].join('\\n');\n}\n\nfunction lines(text: string): number {\n return text ? text.split('\\n').length : 0;\n}\n\nexport type ReportResult = { text: string; failed: string | null; path: string | null };\n\nexport function reportLick(\n record: ScoopRecord,\n id: string,\n report: ReportResult,\n route: { target?: string | null; channel?: 'scoop-notify' | 'bash' } = {}\n): LickEvent {\n const status = report.failed ? (report.failed === 'aborted' ? 'stopped' : 'failed') : 'completed';\n const label = `${record.name} (${record.role ?? 'scoop'})`;\n const preview = report.text.length > PREVIEW ? `${report.text.slice(0, PREVIEW)}\u2026` : report.text;\n const where = report.path ? `Output: ${report.path} (${lines(report.text)} lines)` : null;\n const reason = report.failed && report.failed !== 'aborted' ? `Reason: ${report.failed}` : null;\n return {\n channel: route.channel ?? 'scoop-notify',\n source: id,\n title: label,\n text: `[scoop ${record.name} (${record.role ?? 'scoop'}) ${status}]`,\n body: [where, reason, preview].filter(Boolean).join('\\n\\n'),\n target: (route.target ?? `cone:${record.cone}`) as LickEvent['target'],\n ...cardOf(record.name, report),\n ...(report.failed && report.failed !== 'aborted' ? { severity: 'warn' as const } : {}),\n };\n}\n\ntype Noting = {\n commit: (\n write: (\n tx: Tx\n ) => Promise<{ status: 'running'; checkpoint: { phase: 'deliver'; noted: true } }>,\n context: Context\n ) => Promise<unknown>;\n};\n\nexport async function noteWhenPlaced(\n runtime: Noting,\n submission: Pick<Submission, 'status'>,\n conversation: ConversationId,\n note: { from: string | undefined; agent?: boolean; noted: boolean | undefined },\n context: Context\n): Promise<void> {\n const { from } = note;\n while (from && !note.noted && !context.abortSignal?.aborted) {\n const record = await submission.status(context);\n if (record.status !== 'queued') {\n if (!('entry' in record) || record.entry === undefined) return;\n const entry = record.entry;\n await runtime\n .commit(async (tx) => {\n await tx.appendEntry(conversation, {\n kind: FROM_KIND,\n data: { from, entry, ...(note.agent ? { agent: true } : {}) },\n });\n return { status: 'running', checkpoint: { phase: 'deliver', noted: true } };\n }, context)\n .catch(() => undefined);\n return;\n }\n await new Promise((done) => setTimeout(done, NOTE_POLL_MS));\n }\n}\n\nexport const NOTE_POLL_MS = 100;\n\nexport function waitTask(lookup: Lookup) {\n type WaitInput = { scoops: string[]; cone: string; deadline: number };\n\n return defineTask<WaitInput, { phase: 'start' } | { phase: 'watch' }, null>({\n name: 'slicc.scoop-wait',\n version: 1,\n initial: () => ({ phase: 'start' }),\n phases: {\n async start(task, runtime, context) {\n await runtime.commit(async (tx) => {\n (await tx.doc(ScoopWorkDoc)).waits[String(task.id)] = {\n scoops: task.input.scoops,\n done: {},\n cone: task.input.cone,\n };\n return { status: 'running', checkpoint: { phase: 'watch' } };\n }, context);\n },\n async watch(task, runtime, context) {\n const { agents, licks } = await lookup();\n const key = String(task.id);\n for (;;) {\n const work = await runtime.snapshot(ScoopWorkDoc, context);\n const state = work?.waits[key];\n if (!state) break;\n const feeding = new Set(Object.values(work?.feeds ?? {}).map((feed) => feed.scoop));\n const open = state.scoops.filter((id) => !(id in state.done) && feeding.has(id));\n const expired = runtime.now() >= task.input.deadline;\n if (open.length && !expired) {\n await runtime.sleep(Math.min(runtime.now() + 300, task.input.deadline), context);\n continue;\n }\n const names = (id: string) => agents.state().scoops[id]?.name ?? id;\n const body = state.scoops.map((id) =>\n open.includes(id)\n ? `## ${names(id)}\\n(still working when the wait timed out)`\n : `## ${names(id)}\\n${state.done[id] ?? '(nothing was running)'}`\n );\n await licks.deliver(\n {\n channel: 'scoop-wait',\n source: key,\n title: `Waited for ${state.scoops.map(names).join(', ')}`,\n text: open.length\n ? `${state.scoops.length - open.length} of ${state.scoops.length} done; timed out waiting for ${open.map(names).join(', ')}`\n : `All ${state.scoops.length} done`,\n body: body.join('\\n\\n'),\n target: `cone:${state.cone}`,\n coalesce: false,\n eventId: `wait:${key}`,\n },\n context\n );\n break;\n }\n await runtime.commit(async (tx) => {\n delete (await tx.doc(ScoopWorkDoc)).waits[key];\n return { status: 'terminal', outcome: { status: 'completed', result: null } };\n }, context);\n },\n },\n abort: (task, runtime, context) =>\n runtime.commit(async (tx) => {\n delete (await tx.doc(ScoopWorkDoc)).waits[String(task.id)];\n return { status: 'terminal', outcome: { status: 'aborted' } };\n }, context),\n });\n}\n\nexport function scoopTasks(lookup: Lookup) {\n const anchor = defineTask<null, { phase: 'done' }, null>({\n name: 'slicc.scoop-anchor',\n version: 1,\n initial: () => ({ phase: 'done' }),\n phases: {\n done: (_task, runtime, context) =>\n runtime.commit(\n () => ({ status: 'terminal', outcome: { status: 'completed', result: null } }),\n context\n ),\n },\n abort: (_task, runtime, context) =>\n runtime.commit(() => ({ status: 'terminal', outcome: { status: 'aborted' } }), context),\n });\n\n type ReporterInput = {\n scoop: string;\n prompt: string;\n followUp?: boolean;\n request?: string;\n };\n type ReporterState =\n | { phase: 'deliver'; noted?: boolean }\n | { phase: 'report'; text: string; failed: string | null };\n\n const reporter = defineTask<ReporterInput, ReporterState, ReportResult>({\n name: 'slicc.scoop-reporter',\n version: 1,\n initial: () => ({ phase: 'deliver' }),\n phases: {\n async deliver(task, runtime, context) {\n const { agents } = await lookup();\n const record = agents.state().scoops[task.input.scoop];\n const handle =\n record && live(record)\n ? await runtime.conversation(record.conversation as ConversationId, context)\n : undefined;\n if (!handle) {\n await runtime.commit(async (tx) => {\n delete (await tx.doc(ScoopWorkDoc)).feeds[String(task.id)];\n return {\n status: 'terminal',\n outcome: {\n status: 'completed',\n result: { text: '', failed: 'the scoop is gone', path: null },\n },\n };\n }, context);\n return;\n }\n const requestId = task.input.request ? `subagent:${task.input.request}` : `feed:${task.id}`;\n const feed = (await runtime.snapshot(ScoopWorkDoc, context))?.feeds[String(task.id)];\n const submission = await handle.submit(\n {\n type: 'input',\n content: task.input.prompt,\n whenBusy: task.input.followUp ? 'followUp' : 'steer',\n requestId,\n },\n context\n );\n const noting = noteWhenPlaced(\n runtime,\n submission,\n record?.conversation as ConversationId,\n { from: feed?.from, agent: feed?.agent === true, noted: task.state.checkpoint.noted },\n context\n );\n const settled = await submission.wait(context);\n await noting;\n await runtime.commit(async (tx) => {\n if (settled.status !== 'done' || settled.type !== 'input')\n return {\n status: 'running',\n checkpoint: {\n phase: 'report',\n text: '',\n failed: String(settled.reason),\n },\n };\n const answer = await tx.entry(AssistantEntry, settled.answer);\n return {\n status: 'running',\n checkpoint: {\n phase: 'report',\n text: answerText(answer?.model?.[0]?.content),\n failed: null,\n },\n };\n }, context);\n },\n async report(task, runtime, context) {\n const { agents, licks, layout = LEGACY } = await lookup();\n const id = task.input.scoop;\n const record = agents.state().scoops[id];\n const { text, failed } = task.state.checkpoint;\n const path =\n record && text ? `${layout.reports(record.folder, record.cone)}/${task.id}.md` : null;\n if (path)\n await (await runtime.env(context))?.writeFile(\n path,\n text.endsWith('\\n') ? text : `${text}\\n`,\n context\n );\n const work = await runtime.snapshot(ScoopWorkDoc, context);\n const entry = work?.feeds[String(task.id)] as Feed;\n const waiting = (wait: { scoops: string[]; done: Record<string, string> }) =>\n wait.scoops.includes(id) && !(id in wait.done);\n const folded = Object.values(work?.waits ?? {}).some(waiting);\n const route = { target: entry.target, channel: entry.channel };\n if (!folded && record && entry.report && failed !== 'aborted')\n await licks.deliver(\n {\n ...reportLick(record, id, { text, failed, path }, route),\n eventId: `report:${task.id}`,\n },\n context\n );\n await runtime.commit(async (tx) => {\n const work = await tx.doc(ScoopWorkDoc);\n for (const wait of Object.values(work.waits))\n if (waiting(wait))\n wait.done[id] = failed\n ? `stopped: ${failed}`\n : `${path ? `Output: ${path}\\n` : ''}${text || '(no text)'}`;\n delete work.feeds[String(task.id)];\n return {\n status: 'terminal',\n outcome: { status: 'completed', result: { text, failed, path } },\n };\n }, context);\n },\n },\n abort: (task, runtime, context) =>\n runtime.commit(async (tx) => {\n delete (await tx.doc(ScoopWorkDoc)).feeds[String(task.id)];\n return { status: 'terminal', outcome: { status: 'aborted' } };\n }, context),\n });\n\n return { anchor, reporter, wait: waitTask(lookup) };\n}\n\nasync function whose(input: PromptInput, context: Context) {\n const state = await input.read.snapshot(AgentsDoc, context);\n const scoop = Object.values(state?.scoops ?? {}).find(\n (record) => record.conversation === input.conversationId\n );\n return { scoop };\n}\n\nexport function scoopsExtension(\n tasks: ReturnType<typeof scoopTasks>,\n layout: () => Layout = () => LEGACY\n): Extension {\n return defineExtension({\n name: 'slicc-scoops',\n tasks: [tasks.anchor, tasks.reporter, tasks.wait],\n sections: [\n section('scoop', async (input, context) => {\n const { scoop } = await whose(input, context);\n return scoop ? scoopFacts(scoop, layout()) : undefined;\n }),\n ],\n });\n}\n\nconst WRITES = new Set(['write', 'edit']);\n\nexport function allowed(\n record: Pick<ScoopRecord, 'folder'> & { roots?: ScoopRecord['roots'] },\n coneCwd: string,\n path: string,\n writing: boolean,\n reads: readonly string[] = [],\n own = LEGACY.scoopRoot(record.folder)\n): boolean {\n const write = [own, '/tmp', ...(record.roots?.write ?? [])];\n const roots = writing ? write : [...write, coneCwd, ...(record.roots?.read ?? []), ...reads];\n return roots.some((root) => path === root || path.startsWith(`${root === '/' ? '' : root}/`));\n}\n\nexport function guardExtension(lookup: Lookup): Extension {\n return defineExtension({\n name: 'slicc-scoop-guard',\n hooks: [\n hook(ToolTask, {\n async beforeTool(call, api, context) {\n if (call.name !== 'read' && !WRITES.has(call.name)) return undefined;\n const { agents, reads, layout = LEGACY, enforced, envOf } = await lookup();\n const record = Object.values(agents.state().scoops).find(\n (scoop) => scoop.conversation === api.conversationId\n );\n if (!record || enforced) return undefined;\n const own = layout.scoopRoot(record.folder);\n const cone = layout.users ? own : HOME;\n const path = String(call.arguments.path);\n const asked = normalize(resolve(layout.workspace(record.folder), path));\n const env = await envOf?.(api.conversationId, context);\n const canonical = await env?.canonicalPath(asked, context);\n const target = canonical?.ok ? canonical.value : asked;\n const writing = WRITES.has(call.name);\n const skills = reads?.() ?? [];\n if (\n allowed(record, cone, target, writing, skills, own) ||\n allowed(record, cone, asked, writing, skills, own)\n )\n return undefined;\n const reach = layout.users || writing ? '' : `, and reads ${HOME}`;\n return {\n block: `${target} is outside this scoop's folders. ${call.name} reaches ${own} and /tmp${reach}.`,\n };\n },\n }),\n ],\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["work"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const FENCE = /^(`{3,}|~{3,})/;
|
|
2
|
+
const RULE = /^([-*_])(\s*\1){2,}$/;
|
|
3
|
+
const HEADING = /^\s{0,3}#{1,6}\s/;
|
|
4
|
+
const SUMMARY = /^\s{0,3}#{1,6}\s+summary\s*#*\s*$/i;
|
|
5
|
+
const ITEM = /^\s*([-*+]|\d+[.)])\s+/;
|
|
6
|
+
function plain(line) {
|
|
7
|
+
return line.replace(/^\s{0,3}#{1,6}\s+/, "").replace(/^\s*(>\s*)+/, "").replace(/^\s*([-*+]|\d+[.)])\s+(\[[ xX]\]\s+)?/, "").replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/\[([^\]]+)\]\([^)]*\)/g, "$1").replace(/<\/?[a-zA-Z][^>]*>/g, "").replace(/(\*\*|__|~~)(.+?)\1/g, "$2").replace(/(^|[^\w*])\*(?!\s)([^*]+?)\*(?!\w)/g, "$1$2").replace(/`+([^`]*)`+/g, "$1").replace(/\s+/g, " ").replace(/[\s#]+$/, "").trim();
|
|
8
|
+
}
|
|
9
|
+
function scan(text) {
|
|
10
|
+
const out = [];
|
|
11
|
+
let fenced = false;
|
|
12
|
+
for (const raw of text.split("\n")) {
|
|
13
|
+
const trimmed = raw.trim();
|
|
14
|
+
if (FENCE.test(trimmed)) {
|
|
15
|
+
fenced = !fenced;
|
|
16
|
+
out.push({ text: "", kind: "blank" });
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (fenced || RULE.test(trimmed) || trimmed.startsWith("|") || !trimmed) {
|
|
20
|
+
out.push({ text: "", kind: "blank" });
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const kind = HEADING.test(raw) ? "heading" : ITEM.test(raw) ? "item" : "text";
|
|
24
|
+
out.push({ text: raw, kind });
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
function paragraph(lines, stopAtHeading) {
|
|
29
|
+
const taken = [];
|
|
30
|
+
for (const line of lines) {
|
|
31
|
+
if (line.kind === "heading") {
|
|
32
|
+
if (taken.length || stopAtHeading) break;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (line.kind === "blank") {
|
|
36
|
+
if (taken.length) break;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (line.kind === "item") {
|
|
40
|
+
if (taken.length) break;
|
|
41
|
+
return plain(line.text);
|
|
42
|
+
}
|
|
43
|
+
taken.push(plain(line.text));
|
|
44
|
+
}
|
|
45
|
+
return taken.filter(Boolean).join(" ");
|
|
46
|
+
}
|
|
47
|
+
function sentence(text) {
|
|
48
|
+
const end = /[.!?](?=\s+[A-Z0-9"'(])/.exec(text);
|
|
49
|
+
return end ? text.slice(0, end.index + 1) : text;
|
|
50
|
+
}
|
|
51
|
+
function summarize(text) {
|
|
52
|
+
const lines = scan(text);
|
|
53
|
+
const at = lines.findIndex((line) => line.kind === "heading" && SUMMARY.test(line.text));
|
|
54
|
+
const found = (at >= 0 ? paragraph(lines.slice(at + 1), true) : "") || paragraph(lines, false) || lines.map((line) => plain(line.text)).find(Boolean) || "";
|
|
55
|
+
return sentence(found);
|
|
56
|
+
}
|
|
57
|
+
function cardOf(name, report) {
|
|
58
|
+
const said = report.failed ? "" : summarize(report.text);
|
|
59
|
+
const ended = report.failed ? report.failed === "aborted" ? "stopped" : "failed" : "finished";
|
|
60
|
+
return {
|
|
61
|
+
summary: said || `Scoop ${name} ${ended}`,
|
|
62
|
+
...report.path ? { report: report.path } : {}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export {
|
|
66
|
+
cardOf,
|
|
67
|
+
summarize
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=summary.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/scoops/summary.ts"],
|
|
4
|
+
"sourcesContent": ["const FENCE = /^(`{3,}|~{3,})/;\nconst RULE = /^([-*_])(\\s*\\1){2,}$/;\nconst HEADING = /^\\s{0,3}#{1,6}\\s/;\nconst SUMMARY = /^\\s{0,3}#{1,6}\\s+summary\\s*#*\\s*$/i;\nconst ITEM = /^\\s*([-*+]|\\d+[.)])\\s+/;\n\ntype Line = { text: string; kind: 'blank' | 'heading' | 'item' | 'text' };\n\nfunction plain(line: string): string {\n return line\n .replace(/^\\s{0,3}#{1,6}\\s+/, '')\n .replace(/^\\s*(>\\s*)+/, '')\n .replace(/^\\s*([-*+]|\\d+[.)])\\s+(\\[[ xX]\\]\\s+)?/, '')\n .replace(/!\\[([^\\]]*)\\]\\([^)]*\\)/g, '$1')\n .replace(/\\[([^\\]]+)\\]\\([^)]*\\)/g, '$1')\n .replace(/<\\/?[a-zA-Z][^>]*>/g, '')\n .replace(/(\\*\\*|__|~~)(.+?)\\1/g, '$2')\n .replace(/(^|[^\\w*])\\*(?!\\s)([^*]+?)\\*(?!\\w)/g, '$1$2')\n .replace(/`+([^`]*)`+/g, '$1')\n .replace(/\\s+/g, ' ')\n .replace(/[\\s#]+$/, '')\n .trim();\n}\n\nfunction scan(text: string): Line[] {\n const out: Line[] = [];\n let fenced = false;\n for (const raw of text.split('\\n')) {\n const trimmed = raw.trim();\n if (FENCE.test(trimmed)) {\n fenced = !fenced;\n out.push({ text: '', kind: 'blank' });\n continue;\n }\n if (fenced || RULE.test(trimmed) || trimmed.startsWith('|') || !trimmed) {\n out.push({ text: '', kind: 'blank' });\n continue;\n }\n const kind = HEADING.test(raw) ? 'heading' : ITEM.test(raw) ? 'item' : 'text';\n out.push({ text: raw, kind });\n }\n return out;\n}\n\nfunction paragraph(lines: readonly Line[], stopAtHeading: boolean): string {\n const taken: string[] = [];\n for (const line of lines) {\n if (line.kind === 'heading') {\n if (taken.length || stopAtHeading) break;\n continue;\n }\n if (line.kind === 'blank') {\n if (taken.length) break;\n continue;\n }\n if (line.kind === 'item') {\n if (taken.length) break;\n return plain(line.text);\n }\n taken.push(plain(line.text));\n }\n return taken.filter(Boolean).join(' ');\n}\n\nfunction sentence(text: string): string {\n const end = /[.!?](?=\\s+[A-Z0-9\"'(])/.exec(text);\n return end ? text.slice(0, end.index + 1) : text;\n}\n\nexport function summarize(text: string): string {\n const lines = scan(text);\n const at = lines.findIndex((line) => line.kind === 'heading' && SUMMARY.test(line.text));\n const found =\n (at >= 0 ? paragraph(lines.slice(at + 1), true) : '') ||\n paragraph(lines, false) ||\n lines.map((line) => plain(line.text)).find(Boolean) ||\n '';\n return sentence(found);\n}\n\nexport function cardOf(\n name: string,\n report: { text: string; failed: string | null; path: string | null }\n): { summary: string; report?: string } {\n const said = report.failed ? '' : summarize(report.text);\n const ended = report.failed ? (report.failed === 'aborted' ? 'stopped' : 'failed') : 'finished';\n return {\n summary: said || `Scoop ${name} ${ended}`,\n ...(report.path ? { report: report.path } : {}),\n };\n}\n"],
|
|
5
|
+
"mappings": "AAAA,MAAM,QAAQ;AACd,MAAM,OAAO;AACb,MAAM,UAAU;AAChB,MAAM,UAAU;AAChB,MAAM,OAAO;AAIb,SAAS,MAAM,MAAsB;AACnC,SAAO,KACJ,QAAQ,qBAAqB,EAAE,EAC/B,QAAQ,eAAe,EAAE,EACzB,QAAQ,yCAAyC,EAAE,EACnD,QAAQ,2BAA2B,IAAI,EACvC,QAAQ,0BAA0B,IAAI,EACtC,QAAQ,uBAAuB,EAAE,EACjC,QAAQ,wBAAwB,IAAI,EACpC,QAAQ,uCAAuC,MAAM,EACrD,QAAQ,gBAAgB,IAAI,EAC5B,QAAQ,QAAQ,GAAG,EACnB,QAAQ,WAAW,EAAE,EACrB,KAAK;AACV;AAEA,SAAS,KAAK,MAAsB;AAClC,QAAM,MAAc,CAAC;AACrB,MAAI,SAAS;AACb,aAAW,OAAO,KAAK,MAAM,IAAI,GAAG;AAClC,UAAM,UAAU,IAAI,KAAK;AACzB,QAAI,MAAM,KAAK,OAAO,GAAG;AACvB,eAAS,CAAC;AACV,UAAI,KAAK,EAAE,MAAM,IAAI,MAAM,QAAQ,CAAC;AACpC;AAAA,IACF;AACA,QAAI,UAAU,KAAK,KAAK,OAAO,KAAK,QAAQ,WAAW,GAAG,KAAK,CAAC,SAAS;AACvE,UAAI,KAAK,EAAE,MAAM,IAAI,MAAM,QAAQ,CAAC;AACpC;AAAA,IACF;AACA,UAAM,OAAO,QAAQ,KAAK,GAAG,IAAI,YAAY,KAAK,KAAK,GAAG,IAAI,SAAS;AACvE,QAAI,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC;AAAA,EAC9B;AACA,SAAO;AACT;AAEA,SAAS,UAAU,OAAwB,eAAgC;AACzE,QAAM,QAAkB,CAAC;AACzB,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,SAAS,WAAW;AAC3B,UAAI,MAAM,UAAU,cAAe;AACnC;AAAA,IACF;AACA,QAAI,KAAK,SAAS,SAAS;AACzB,UAAI,MAAM,OAAQ;AAClB;AAAA,IACF;AACA,QAAI,KAAK,SAAS,QAAQ;AACxB,UAAI,MAAM,OAAQ;AAClB,aAAO,MAAM,KAAK,IAAI;AAAA,IACxB;AACA,UAAM,KAAK,MAAM,KAAK,IAAI,CAAC;AAAA,EAC7B;AACA,SAAO,MAAM,OAAO,OAAO,EAAE,KAAK,GAAG;AACvC;AAEA,SAAS,SAAS,MAAsB;AACtC,QAAM,MAAM,0BAA0B,KAAK,IAAI;AAC/C,SAAO,MAAM,KAAK,MAAM,GAAG,IAAI,QAAQ,CAAC,IAAI;AAC9C;AAEO,SAAS,UAAU,MAAsB;AAC9C,QAAM,QAAQ,KAAK,IAAI;AACvB,QAAM,KAAK,MAAM,UAAU,CAAC,SAAS,KAAK,SAAS,aAAa,QAAQ,KAAK,KAAK,IAAI,CAAC;AACvF,QAAM,SACH,MAAM,IAAI,UAAU,MAAM,MAAM,KAAK,CAAC,GAAG,IAAI,IAAI,OAClD,UAAU,OAAO,KAAK,KACtB,MAAM,IAAI,CAAC,SAAS,MAAM,KAAK,IAAI,CAAC,EAAE,KAAK,OAAO,KAClD;AACF,SAAO,SAAS,KAAK;AACvB;AAEO,SAAS,OACd,MACA,QACsC;AACtC,QAAM,OAAO,OAAO,SAAS,KAAK,UAAU,OAAO,IAAI;AACvD,QAAM,QAAQ,OAAO,SAAU,OAAO,WAAW,YAAY,YAAY,WAAY;AACrF,SAAO;AAAA,IACL,SAAS,QAAQ,SAAS,IAAI,IAAI,KAAK;AAAA,IACvC,GAAI,OAAO,OAAO,EAAE,QAAQ,OAAO,KAAK,IAAI,CAAC;AAAA,EAC/C;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -266,7 +266,9 @@ function lickMessage(id, entry, decisions) {
|
|
|
266
266
|
...lick.body ? { body: lick.body } : {},
|
|
267
267
|
...lick.count > 1 ? { count: lick.count } : {},
|
|
268
268
|
...state ? { state } : {},
|
|
269
|
-
...lick.severity ? { severity: lick.severity } : {}
|
|
269
|
+
...lick.severity ? { severity: lick.severity } : {},
|
|
270
|
+
...lick.summary ? { summary: lick.summary } : {},
|
|
271
|
+
...lick.report ? { report: lick.report } : {}
|
|
270
272
|
};
|
|
271
273
|
}
|
|
272
274
|
function withoutLickTools(message) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/spectrum/messages.ts"],
|
|
4
|
-
"sourcesContent": ["import type {\n AssistantMessage,\n AssistantStatus,\n ErrorAction,\n LickMessage,\n Message,\n MessagePart,\n SystemMessage,\n ToolCall,\n ToolStatus,\n Usage,\n UserMessage,\n} from '@ai-ecoverse/slicc-spectrum/ui';\nimport type { ConversationView, EntryRecord } from '@earendil-works/pi-durable';\nimport { splitUser } from '../attachments.ts';\nimport {\n ASKED_KIND,\n askedLine,\n GUEST_INPUT_KIND,\n type GuestAsked,\n type GuestInput,\n type GuestOutcome,\n type GuestReviewed,\n guestText,\n REVIEWED_KIND,\n} from '../guests/attribution.ts';\nimport { parseLick } from '../licks/lick.ts';\nimport { entryLick, LICK_TOOLS, type LickDecision, lickDecisions } from '../licks/state.ts';\nimport { mcpLabel } from '../mcp/label.ts';\nimport { FROM_KIND, type From } from '../scoops/from.ts';\nimport type { SecretName } from '../secrets/names.ts';\nimport { listedLine, SECRETS_KIND } from '../secrets/view.ts';\nimport type { Delivered } from '../services.ts';\nimport { SPRINKLE_KIND } from '../sprinkles/kind.ts';\nimport { warn } from './attempt.ts';\n\ntype Json = Record<string, unknown>;\n\ninterface Block {\n type: string;\n text?: string;\n thinking?: string;\n id?: string;\n name?: string;\n arguments?: Json;\n}\n\ninterface ModelMessage {\n role: string;\n content?: string | Block[];\n timestamp?: number;\n stopReason?: string;\n errorMessage?: string;\n provider?: string;\n model?: string;\n usage?: {\n input?: number;\n output?: number;\n cacheRead?: number;\n cacheWrite?: number;\n cost?: { total?: number };\n };\n toolCallId?: string;\n isError?: boolean;\n details?: { patch?: unknown };\n}\n\ninterface Slot {\n callId: string;\n status: string;\n output?: string;\n}\n\ninterface Live {\n run?: unknown;\n generation?: { message?: ModelMessage; retry?: { at: number; error: string } };\n tools?: Slot[];\n compactions?: { taskId: number; reason: string }[];\n}\n\nexport function textOf(content: ModelMessage['content']): string {\n if (typeof content === 'string') return content;\n return (content ?? []).map((block) => (block.type === 'text' ? (block.text ?? '') : '')).join('');\n}\n\nconst paths = (args: Json | undefined): string[] =>\n typeof args?.path === 'string' ? [args.path] : [];\n\nfunction title(args: Json | undefined): string {\n const subject = args?.command ?? args?.path;\n return typeof subject === 'string' ? subject : '';\n}\n\ninterface Replacement {\n oldText?: string;\n newText?: string;\n}\n\nconst GAP = '\u2026';\n\nfunction seconds(value: unknown): string | undefined {\n return typeof value === 'number' ? `timeout ${value}s` : undefined;\n}\n\nfunction lines(args: Json): string | undefined {\n const { offset, limit } = args as { offset?: number; limit?: number };\n if (typeof offset === 'number' && typeof limit === 'number')\n return `lines ${offset}\u2013${offset + limit - 1}`;\n if (typeof offset === 'number') return `from line ${offset}`;\n return typeof limit === 'number' ? `first ${limit} lines` : undefined;\n}\n\nfunction edits(args: Json): ToolCall['diff'] {\n const list = Array.isArray(args.edits) ? (args.edits as Replacement[]) : [];\n if (list.length === 0) return undefined;\n return {\n before: list.map((edit) => edit.oldText ?? '').join(`\\n${GAP}\\n`),\n after: list.map((edit) => edit.newText ?? '').join(`\\n${GAP}\\n`),\n };\n}\n\nexport function fromPatch(patch: string): ToolCall['diff'] {\n const before: string[] = [];\n const after: string[] = [];\n let hunks = 0;\n for (const line of patch.split('\\n')) {\n if (line.startsWith('@@')) {\n if (hunks++ > 0) {\n before.push(GAP);\n after.push(GAP);\n }\n continue;\n }\n if (hunks === 0 || line === '' || line.startsWith('\\\\')) continue;\n const text = line.slice(1);\n if (line.startsWith('-')) before.push(text);\n else if (line.startsWith('+')) after.push(text);\n else {\n before.push(text);\n after.push(text);\n }\n }\n return hunks ? { before: before.join('\\n'), after: after.join('\\n') } : undefined;\n}\n\nfunction shape(name: string, args: Json): Pick<ToolCall, 'input' | 'meta' | 'diff'> {\n const path = typeof args.path === 'string' ? args.path : '';\n const meta = name === 'bash' ? seconds(args.timeout) : name === 'read' ? lines(args) : undefined;\n const base = meta ? { meta } : {};\n if (name === 'bash' && typeof args.command === 'string') return { input: args.command, ...base };\n if (name === 'read' || name === 'write') return { input: path, ...base };\n if (name === 'edit') {\n const diff = edits(args);\n return { input: path, ...(diff ? { diff } : {}) };\n }\n return { input: JSON.stringify(args, null, 2) };\n}\n\nfunction toolCall(block: Block): ToolCall {\n const args = block.arguments ?? {};\n return {\n id: block.id ?? '',\n name: block.name ?? 'tool',\n title: mcpLabel(block.name ?? '') ?? title(block.arguments),\n ...shape(block.name ?? 'tool', args),\n output: '',\n status: 'running',\n paths: paths(block.arguments),\n };\n}\n\nfunction part(block: Block): MessagePart | undefined {\n if (block.type === 'text') return { type: 'text', text: block.text ?? '' };\n if (block.type === 'thinking') return { type: 'thinking', text: block.thinking ?? '' };\n if (block.type === 'toolCall') return { type: 'tool', tool: toolCall(block) };\n return undefined;\n}\n\nfunction usage(message: ModelMessage): Usage | undefined {\n const used = message.usage;\n if (!used) return undefined;\n return {\n input: used.input ?? 0,\n output: used.output ?? 0,\n cost: used.cost?.total ?? 0,\n cacheRead: used.cacheRead ?? 0,\n cacheWrite: used.cacheWrite ?? 0,\n };\n}\n\nconst statuses: Record<string, AssistantStatus> = { error: 'error', aborted: 'stopped' };\n\nconst credentials =\n /security token|unrecognizedclient|not authori[sz]ed|unauthori[sz]ed|forbidden|\\b40[13]\\b|api key|credential|bearer/i;\nconst models = /model/i;\nconst unavailable =\n /access|invalid|not found|identifier|unsupported|not supported|isn['\u2019]t supported|not available|isn['\u2019]t available/i;\n\nconst providerNames: Record<string, string> = { 'amazon-bedrock': 'Bedrock', adobe: 'Adobe' };\nconst filtered =\n /Provider (?:stopped with|finish_reason): (?:content_filter(?:ed)?|guardrail_intervened|sensitive)|content[_ ]filter(?:ed|ing)?\\b|guardrail[_ ]intervened/i;\nconst refused =\n /refused to complete the request|Provider (?:stopped with|finish_reason): refusal|\\bstop_?reason\\W+refusal\\b/i;\nconst unconnected = /no api key|not configured|no credentials/i;\nconst missing = /missing/i;\nconst adobeAuth =\n /\\b401\\b|authenticat|unauthori[sz]ed|expired|invalid[_ ]?(?:token|credentials?|api[_ ]key)|token verification/i;\n\nfunction proxyRefusal(error: string): { message: string; detail: string } {\n try {\n const body = JSON.parse(error.match(/\\{.*\\}/s)?.[0] ?? '') as {\n error?: { type?: unknown; message?: unknown };\n };\n const { type, message } = body.error ?? {};\n const status = error.match(/^\\s*(\\d{3})\\b/)?.[1];\n return {\n message: typeof message === 'string' ? message : error,\n detail: typeof type === 'string' ? [status, type].filter(Boolean).join(' ') : error,\n };\n } catch {\n return { message: error, detail: error };\n }\n}\n\nexport const CONNECT_IT = 'Connect it in Settings \u203A Accounts.';\nexport const LOG_IN_AGAIN = 'Log in again to continue.';\n\nconst logged = new Set<string>();\n\nfunction logOnce(error: string): void {\n if (logged.has(error)) return;\n logged.add(error);\n warn(`slicc-agent: the Adobe session expired: ${error}`);\n}\n\nexport function providerName(provider: string | undefined, fallback = 'The provider'): string {\n return providerNames[provider ?? ''] ?? fallback;\n}\n\nexport function errorPart(error: string, provider?: string): MessagePart {\n const action = errorAction(error);\n if (action === 'drop-turn') {\n return {\n type: 'error',\n message: filtered.test(error)\n ? \"The model's content filter stopped this reply.\"\n : 'The model refused to answer this turn.',\n detail: error,\n action,\n };\n }\n const name = providerName(provider);\n const account = provider ? { account: provider } : {};\n if (unconnected.test(error))\n return {\n type: 'error',\n message: `${name} isn't connected.`,\n detail: CONNECT_IT,\n action: 'settings',\n ...account,\n };\n if (action === 'settings' && provider === 'adobe') {\n if (!adobeAuth.test(error)) return { type: 'error', ...proxyRefusal(error), action: 'retry' };\n logOnce(error);\n return {\n type: 'error',\n message: 'Your Adobe session has expired.',\n detail: LOG_IN_AGAIN,\n action: 'login',\n ...account,\n };\n }\n if (action === 'settings') {\n const lead = missing.test(error)\n ? `${name} needs an API key.`\n : `${name} rejected the API key.`;\n return { type: 'error', message: lead, detail: error, action, ...account };\n }\n return action === 'change-model'\n ? {\n type: 'error',\n message: \"This model isn't available with the current account.\",\n detail: error,\n action,\n }\n : { type: 'error', message: error, action };\n}\n\nexport function errorAction(message: string): ErrorAction {\n if (filtered.test(message) || refused.test(message)) return 'drop-turn';\n if (credentials.test(message)) return 'settings';\n if (models.test(message) && unavailable.test(message)) return 'change-model';\n return 'retry';\n}\n\nfunction sprinkleMessage(id: string, entry: EntryRecord): AssistantMessage {\n const data = (entry.data ?? {}) as { sprinkle?: unknown; at?: unknown };\n return {\n id,\n role: 'assistant',\n parts: [{ type: 'sprinkle', sprinkle: String(data.sprinkle ?? '') }],\n status: 'done',\n createdAt: typeof data.at === 'number' ? data.at : 0,\n };\n}\n\nexport function assistant(\n id: string,\n message: ModelMessage,\n status?: AssistantStatus\n): AssistantMessage {\n const blocks = Array.isArray(message.content) ? message.content : [];\n const parts = blocks.map(part).filter((item): item is MessagePart => item !== undefined);\n if (message.errorMessage) parts.push(errorPart(message.errorMessage, message.provider));\n const used = usage(message);\n return {\n id,\n role: 'assistant',\n parts,\n status: status ?? statuses[message.stopReason ?? ''] ?? 'done',\n createdAt: message.timestamp ?? 0,\n ...(message.provider && message.model ? { model: `${message.provider}/${message.model}` } : {}),\n ...(used ? { usage: used } : {}),\n };\n}\n\nfunction user(\n id: string,\n message: ModelMessage,\n delivered: Delivered | undefined,\n from: From | undefined\n): UserMessage {\n return {\n id,\n role: 'user',\n ...shownUser(message.content, `ref:${id.slice(1)}:0`),\n createdAt: message.timestamp ?? 0,\n ...(delivered ? { delivered } : {}),\n ...(from ? { from: from.from } : {}),\n ...(from?.agent ? { origin: 'agent' as const } : {}),\n };\n}\n\nfunction guestUser(\n id: string,\n message: ModelMessage,\n delivered: Delivered | undefined,\n data: unknown\n): UserMessage {\n const input = data as Partial<GuestInput> | undefined;\n const name = String(input?.name ?? '');\n const shown = shownUser(message.content, `ref:${id.slice(1)}:0`);\n return {\n id,\n role: 'user',\n ...shown,\n text: guestText(shown.text, name),\n createdAt: message.timestamp ?? 0,\n ...(delivered ? { delivered } : {}),\n from: name,\n origin: 'guest',\n ...(typeof input?.seat === 'string' ? { seat: input.seat } : {}),\n };\n}\n\nexport function origins(entries: readonly EntryRecord[]): Map<number, From> {\n const out = new Map<number, From>();\n for (const entry of entries)\n if (entry.kind === FROM_KIND) {\n const note = entry.data as From;\n out.set(note.entry, note);\n }\n return out;\n}\n\nfunction lickMessage(\n id: string,\n entry: EntryRecord,\n decisions: ReadonlyMap<string, LickDecision>\n): LickMessage | undefined {\n const lick = entryLick(entry);\n if (!lick) return undefined;\n const state = lick.actions?.length ? (decisions.get(lick.id) ?? 'pending') : undefined;\n return {\n id,\n role: 'lick',\n channel: lick.channel,\n title: lick.title,\n text: lick.text,\n createdAt: lick.at,\n ...(lick.body ? { body: lick.body } : {}),\n ...(lick.count > 1 ? { count: lick.count } : {}),\n ...(state ? { state } : {}),\n ...(lick.severity ? { severity: lick.severity } : {}),\n };\n}\n\nfunction withoutLickTools(message: AssistantMessage): AssistantMessage {\n const parts = message.parts.filter((item) => item.type !== 'tool' || !LICK_TOOLS[item.tool.name]);\n return parts.length === message.parts.length ? message : { ...message, parts };\n}\n\nfunction system(id: string, entry: EntryRecord, text: string): SystemMessage {\n const reason = (entry.data as Json | undefined)?.reason as SystemMessage['trigger'] | undefined;\n return {\n id,\n role: 'system',\n kind: 'compaction',\n text,\n createdAt: 0,\n state: 'summarized',\n ...(reason ? { trigger: reason } : {}),\n };\n}\n\nfunction tools(messages: Message[]): Map<string, ToolCall> {\n const found = new Map<string, ToolCall>();\n for (const message of messages) {\n if (message.role !== 'assistant') continue;\n for (const item of message.parts) if (item.type === 'tool') found.set(item.tool.id, item.tool);\n }\n return found;\n}\n\ninterface Diagnostic {\n severity: string;\n message: string;\n}\n\ninterface Note {\n severity: 'error' | 'warn' | 'info';\n text: string;\n}\n\ntype Card = ToolCall & { notes?: Note[] };\n\nconst RANK: Note['severity'][] = ['error', 'warn', 'info'];\n\nconst LINE = /^\\[(error|warn|info)\\] (.*)$/;\n\nfunction rendered(diagnostics: readonly Diagnostic[]): string {\n return `<harness>\\n${diagnostics.map((item) => `[${item.severity}] ${item.message}`).join('\\n')}\\n</harness>`;\n}\n\nfunction parsed(text: string): Diagnostic[] | undefined {\n const body = /^<harness>\\n([\\s\\S]*)\\n<\\/harness>$/.exec(text)?.[1];\n if (body === undefined) return undefined;\n const found = body.split('\\n').map((line) => LINE.exec(line));\n if (!found.every((match): match is RegExpExecArray => match !== null)) return undefined;\n return found.map(([, severity, message]) => ({ severity, message }) as Diagnostic);\n}\n\nfunction harness(\n content: ModelMessage['content'],\n data: unknown\n): { content: ModelMessage['content']; diagnostics: Diagnostic[] } {\n const last = Array.isArray(content) ? content.at(-1) : undefined;\n const stored = (data as { diagnostics?: unknown } | undefined)?.diagnostics;\n const listed = Array.isArray(stored) ? (stored as Diagnostic[]) : parsed(last?.text ?? '');\n if (!listed?.length || last?.type !== 'text' || last.text !== rendered(listed))\n return { content, diagnostics: [] };\n return { content: (content as Block[]).slice(0, -1), diagnostics: listed };\n}\n\nconst count = (value: string | undefined): string => Number(value).toLocaleString('en');\n\nconst ends: Record<string, string> = { end: ' from the start', beginning: ' from the end' };\n\nconst phrasings: [RegExp, (match: RegExpExecArray) => string][] = [\n [/^Command exited with code (-?\\d+)$/, (match) => `Exited with code ${match[1]}`],\n [/^Command timed out after (.+)$/, (match) => `Timed out after ${match[1]}`],\n [/^Command aborted$/, () => 'Aborted'],\n [/^Full output: (.+)$/, (match) => `Full output in ${match[1]}`],\n [\n /^Output truncated(?: to its (beginning|end))?: (\\d+) lines, (\\d+) bytes dropped$/,\n (match) =>\n `Output truncated: ${count(match[2])} lines and ${count(match[3])} bytes dropped${ends[match[1] ?? ''] ?? ''}`,\n ],\n];\n\nfunction phrased(message: string): string {\n const line = message\n .replace(/\\n[\\s\\S]*$/, '')\n .trim()\n .replace(/\\.$/, '');\n for (const [pattern, phrase] of phrasings) {\n const match = pattern.exec(line);\n if (match) return phrase(match);\n }\n return line;\n}\n\nfunction note(diagnostic: Diagnostic): Note {\n return { severity: diagnostic.severity as Note['severity'], text: phrased(diagnostic.message) };\n}\n\nfunction settle(calls: Map<string, ToolCall>, result: ModelMessage, data?: unknown): void {\n const call = calls.get(result.toolCallId ?? '') as Card | undefined;\n if (!call) return;\n const split = harness(result.content, data);\n call.output = textOf(split.content);\n if (split.diagnostics.length)\n call.notes = split.diagnostics\n .map(note)\n .sort((a, b) => RANK.indexOf(a.severity) - RANK.indexOf(b.severity));\n call.status = result.isError ? 'error' : 'done';\n const image = Array.isArray(result.content)\n ? (result.content as { type: string; data?: string; mimeType?: string }[]).find(\n (part) => part.type === 'image' && part.data && part.mimeType\n )\n : undefined;\n if (image) call.image = `data:${image.mimeType};base64,${image.data}`;\n const patch = result.details?.patch;\n const diff = typeof patch === 'string' ? fromPatch(patch) : undefined;\n if (diff) call.diff = diff;\n else if (result.isError) delete call.diff;\n}\n\nfunction rewound(id: string, entry: EntryRecord): SystemMessage {\n const data = (entry.data ?? {}) as { stopped?: string[]; restored?: string[]; files?: boolean };\n const notes = [\n 'Its prompt is back in the composer.',\n ...(data.stopped?.length\n ? [`Stopped ${data.stopped.map((name) => `scoop ${name}`).join(', ')}.`]\n : []),\n ...(data.restored?.length\n ? [`Restored ${data.restored.map((name) => `scoop ${name}`).join(', ')}.`]\n : []),\n ...(data.files ? ['Changed files stay; use Changes or git to revert them.'] : []),\n ];\n return {\n id,\n role: 'system',\n kind: 'notice',\n title: 'Rewound 1 turn',\n text: notes.join(' '),\n createdAt: 0,\n };\n}\n\nfunction thawed(id: string, entry: EntryRecord): SystemMessage {\n const data = (entry.data ?? {}) as { name?: string; kind?: string; v6?: boolean };\n const what = data.v6 ? 'SLICC 6' : data.kind === 'agent' ? 'agent run' : 'scoop';\n return {\n id,\n role: 'system',\n kind: 'notice',\n title: `Thawed from ${what} ${data.name ?? ''}`.trim(),\n text: textOf(entry.model?.[0]?.content ?? ''),\n createdAt: entry.model?.[0]?.timestamp ?? 0,\n };\n}\n\nexport function reviewed(entries: readonly EntryRecord[]): Map<string, GuestOutcome> {\n const out = new Map<string, GuestOutcome>();\n for (const entry of entries) {\n if (entry.kind !== REVIEWED_KIND) continue;\n const data = (entry.data ?? {}) as Partial<GuestReviewed>;\n if (typeof data.review === 'string' && data.outcome) out.set(data.review, data.outcome);\n }\n return out;\n}\n\nfunction asked(\n id: string,\n entry: EntryRecord,\n outcomes: ReadonlyMap<string, GuestOutcome>\n): SystemMessage {\n const data = (entry.data ?? {}) as Partial<GuestAsked>;\n return {\n id,\n role: 'system',\n kind: 'notice',\n text: askedLine(\n String(data.name ?? ''),\n String(data.text ?? ''),\n outcomes.get(String(data.review))\n ),\n createdAt: typeof data.at === 'number' ? data.at : 0,\n };\n}\n\nfunction secrets(id: string, entry: EntryRecord): SystemMessage {\n const data = (entry.data ?? {}) as {\n names?: SecretName[];\n error?: string | null;\n exposed?: string[];\n };\n return {\n id,\n role: 'system',\n kind: 'notice',\n title: data.error ? 'Secrets unavailable' : 'Secrets listed',\n text: listedLine({\n names: data.names ?? [],\n error: data.error ?? null,\n exposed: data.exposed ?? [],\n }),\n createdAt: entry.model?.[0]?.timestamp ?? 0,\n };\n}\n\nconst V6_KINDS: Record<string, string> = {\n user: 'pi.user',\n assistant: 'pi.assistant',\n toolResult: 'pi.tool-result',\n};\n\nfunction entryMessage(\n entry: EntryRecord,\n out: Message[],\n calls: Map<string, ToolCall>,\n delivered: Delivered | undefined,\n notes: {\n decisions: ReadonlyMap<string, LickDecision>;\n origins: ReadonlyMap<number, From>;\n outcomes: ReadonlyMap<string, GuestOutcome>;\n }\n): void {\n const id = `e${entry.id}`;\n const model = entry.model?.[0] as ModelMessage | undefined;\n if (entry.kind === 'slicc.v6' && model) {\n const kind = V6_KINDS[model.role as string];\n if (kind) entryMessage({ ...entry, kind }, out, calls, delivered, notes);\n return;\n }\n if (entry.kind === 'slicc.rewound') out.push(rewound(id, entry));\n if (entry.kind === 'slicc.thawed') out.push(thawed(id, entry));\n else if (entry.kind === SECRETS_KIND) out.push(secrets(id, entry));\n else if (entry.kind === ASKED_KIND) out.push(asked(id, entry, notes.outcomes));\n else if (entry.kind === SPRINKLE_KIND) out.push(sprinkleMessage(id, entry));\n else if (entry.kind === 'pi.user' && model)\n out.push(\n lickMessage(id, entry, notes.decisions) ??\n user(id, model, delivered, notes.origins.get(entry.id))\n );\n else if (entry.kind === GUEST_INPUT_KIND && model)\n out.push(guestUser(id, model, delivered, entry.data));\n else if (entry.kind === 'pi.assistant' && model) {\n const message = assistant(id, model);\n out.push(withoutLickTools(message));\n for (const [key, value] of tools([message])) calls.set(key, value);\n } else if (entry.kind === 'pi.tool-result' && model) settle(calls, model, entry.data);\n else if (entry.kind === 'pi.compaction') out.push(system(id, entry, textOf(model?.content)));\n}\n\nconst slotStatus: Record<string, ToolStatus> = {\n pending: 'running',\n running: 'running',\n done: 'done',\n};\n\nfunction live(view: ConversationView, out: Message[], calls: Map<string, ToolCall>): void {\n const state = view.docs['pi.live'] as Live | undefined;\n for (const slot of state?.tools ?? []) {\n const call = calls.get(slot.callId);\n if (call && call.status === 'running') {\n call.status = slotStatus[slot.status] ?? 'running';\n call.output = slot.output ?? call.output;\n }\n }\n const partial = state?.generation?.message;\n if (partial) out.push(assistant('live', partial, 'streaming'));\n for (const compaction of state?.compactions ?? []) {\n out.push({\n id: `compaction-${compaction.taskId}`,\n role: 'system',\n kind: 'compaction',\n text: '',\n createdAt: 0,\n state: 'summarizing',\n trigger: compaction.reason as SystemMessage['trigger'],\n });\n }\n const retry = state?.generation?.retry;\n if (retry)\n out.push({\n id: 'retry',\n role: 'system',\n kind: 'notice',\n text: retry.error,\n createdAt: retry.at,\n });\n}\n\nexport function toMessages(\n view: ConversationView | undefined,\n deliveries: Readonly<Record<string, Delivered>> = {}\n): Message[] {\n const out: Message[] = [];\n if (!view) return out;\n const calls = new Map<string, ToolCall>();\n const notes = {\n decisions: lickDecisions(view.entries),\n origins: origins(view.entries),\n outcomes: reviewed(view.entries),\n };\n let previous: EntryRecord | undefined;\n for (const entry of view.entries) {\n const steered = previous?.kind === 'pi.tool-result' ? 'steer' : undefined;\n entryMessage(entry, out, calls, deliveries[String(entry.id)] ?? steered, notes);\n previous = entry;\n }\n live(view, out, calls);\n return out;\n}\n\nfunction shownUser(\n content: ModelMessage['content'],\n prefix?: string\n): Pick<UserMessage, 'text' | 'attachments'> {\n const { text, attachments } = splitUser(content, prefix);\n return attachments.length ? { text, attachments } : { text };\n}\n\nexport function isBusy(view: ConversationView | undefined): boolean {\n return Boolean((view?.docs['pi.live'] as Live | undefined)?.run);\n}\n\nexport function queued(\n view: ConversationView | undefined,\n at: (id: string) => number = () => 0\n): UserMessage[] {\n const items = ((view?.docs['pi.inbox'] as { items?: Json[] } | undefined)?.items ?? []) as {\n id: number;\n mode: string;\n content?: ModelMessage['content'];\n entry?: { kind?: string; data?: Partial<GuestInput> };\n }[];\n return items\n .filter((item) => item.mode !== 'write' && !parseLick(textOf(item.content)))\n .map((item) => {\n const shown = shownUser(item.content);\n const data = item.entry?.data;\n const guest = item.entry?.kind === GUEST_INPUT_KIND ? String(data?.name ?? '') : null;\n const seat = typeof data?.seat === 'string' ? { seat: data.seat } : {};\n return {\n id: `q${item.id}`,\n role: 'user' as const,\n ...shown,\n ...(guest === null\n ? {}\n : {\n text: guestText(shown.text, guest),\n from: guest,\n origin: 'guest' as const,\n ...seat,\n }),\n createdAt: at(`q${item.id}`),\n queued: true,\n mode: item.mode === 'steer' ? 'steer' : 'queue',\n };\n });\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type {\n AssistantMessage,\n AssistantStatus,\n ErrorAction,\n LickMessage,\n Message,\n MessagePart,\n SystemMessage,\n ToolCall,\n ToolStatus,\n Usage,\n UserMessage,\n} from '@ai-ecoverse/slicc-spectrum/ui';\nimport type { ConversationView, EntryRecord } from '@earendil-works/pi-durable';\nimport { splitUser } from '../attachments.ts';\nimport {\n ASKED_KIND,\n askedLine,\n GUEST_INPUT_KIND,\n type GuestAsked,\n type GuestInput,\n type GuestOutcome,\n type GuestReviewed,\n guestText,\n REVIEWED_KIND,\n} from '../guests/attribution.ts';\nimport { parseLick } from '../licks/lick.ts';\nimport { entryLick, LICK_TOOLS, type LickDecision, lickDecisions } from '../licks/state.ts';\nimport { mcpLabel } from '../mcp/label.ts';\nimport { FROM_KIND, type From } from '../scoops/from.ts';\nimport type { SecretName } from '../secrets/names.ts';\nimport { listedLine, SECRETS_KIND } from '../secrets/view.ts';\nimport type { Delivered } from '../services.ts';\nimport { SPRINKLE_KIND } from '../sprinkles/kind.ts';\nimport { warn } from './attempt.ts';\n\ntype Json = Record<string, unknown>;\n\ninterface Block {\n type: string;\n text?: string;\n thinking?: string;\n id?: string;\n name?: string;\n arguments?: Json;\n}\n\ninterface ModelMessage {\n role: string;\n content?: string | Block[];\n timestamp?: number;\n stopReason?: string;\n errorMessage?: string;\n provider?: string;\n model?: string;\n usage?: {\n input?: number;\n output?: number;\n cacheRead?: number;\n cacheWrite?: number;\n cost?: { total?: number };\n };\n toolCallId?: string;\n isError?: boolean;\n details?: { patch?: unknown };\n}\n\ninterface Slot {\n callId: string;\n status: string;\n output?: string;\n}\n\ninterface Live {\n run?: unknown;\n generation?: { message?: ModelMessage; retry?: { at: number; error: string } };\n tools?: Slot[];\n compactions?: { taskId: number; reason: string }[];\n}\n\nexport function textOf(content: ModelMessage['content']): string {\n if (typeof content === 'string') return content;\n return (content ?? []).map((block) => (block.type === 'text' ? (block.text ?? '') : '')).join('');\n}\n\nconst paths = (args: Json | undefined): string[] =>\n typeof args?.path === 'string' ? [args.path] : [];\n\nfunction title(args: Json | undefined): string {\n const subject = args?.command ?? args?.path;\n return typeof subject === 'string' ? subject : '';\n}\n\ninterface Replacement {\n oldText?: string;\n newText?: string;\n}\n\nconst GAP = '\u2026';\n\nfunction seconds(value: unknown): string | undefined {\n return typeof value === 'number' ? `timeout ${value}s` : undefined;\n}\n\nfunction lines(args: Json): string | undefined {\n const { offset, limit } = args as { offset?: number; limit?: number };\n if (typeof offset === 'number' && typeof limit === 'number')\n return `lines ${offset}\u2013${offset + limit - 1}`;\n if (typeof offset === 'number') return `from line ${offset}`;\n return typeof limit === 'number' ? `first ${limit} lines` : undefined;\n}\n\nfunction edits(args: Json): ToolCall['diff'] {\n const list = Array.isArray(args.edits) ? (args.edits as Replacement[]) : [];\n if (list.length === 0) return undefined;\n return {\n before: list.map((edit) => edit.oldText ?? '').join(`\\n${GAP}\\n`),\n after: list.map((edit) => edit.newText ?? '').join(`\\n${GAP}\\n`),\n };\n}\n\nexport function fromPatch(patch: string): ToolCall['diff'] {\n const before: string[] = [];\n const after: string[] = [];\n let hunks = 0;\n for (const line of patch.split('\\n')) {\n if (line.startsWith('@@')) {\n if (hunks++ > 0) {\n before.push(GAP);\n after.push(GAP);\n }\n continue;\n }\n if (hunks === 0 || line === '' || line.startsWith('\\\\')) continue;\n const text = line.slice(1);\n if (line.startsWith('-')) before.push(text);\n else if (line.startsWith('+')) after.push(text);\n else {\n before.push(text);\n after.push(text);\n }\n }\n return hunks ? { before: before.join('\\n'), after: after.join('\\n') } : undefined;\n}\n\nfunction shape(name: string, args: Json): Pick<ToolCall, 'input' | 'meta' | 'diff'> {\n const path = typeof args.path === 'string' ? args.path : '';\n const meta = name === 'bash' ? seconds(args.timeout) : name === 'read' ? lines(args) : undefined;\n const base = meta ? { meta } : {};\n if (name === 'bash' && typeof args.command === 'string') return { input: args.command, ...base };\n if (name === 'read' || name === 'write') return { input: path, ...base };\n if (name === 'edit') {\n const diff = edits(args);\n return { input: path, ...(diff ? { diff } : {}) };\n }\n return { input: JSON.stringify(args, null, 2) };\n}\n\nfunction toolCall(block: Block): ToolCall {\n const args = block.arguments ?? {};\n return {\n id: block.id ?? '',\n name: block.name ?? 'tool',\n title: mcpLabel(block.name ?? '') ?? title(block.arguments),\n ...shape(block.name ?? 'tool', args),\n output: '',\n status: 'running',\n paths: paths(block.arguments),\n };\n}\n\nfunction part(block: Block): MessagePart | undefined {\n if (block.type === 'text') return { type: 'text', text: block.text ?? '' };\n if (block.type === 'thinking') return { type: 'thinking', text: block.thinking ?? '' };\n if (block.type === 'toolCall') return { type: 'tool', tool: toolCall(block) };\n return undefined;\n}\n\nfunction usage(message: ModelMessage): Usage | undefined {\n const used = message.usage;\n if (!used) return undefined;\n return {\n input: used.input ?? 0,\n output: used.output ?? 0,\n cost: used.cost?.total ?? 0,\n cacheRead: used.cacheRead ?? 0,\n cacheWrite: used.cacheWrite ?? 0,\n };\n}\n\nconst statuses: Record<string, AssistantStatus> = { error: 'error', aborted: 'stopped' };\n\nconst credentials =\n /security token|unrecognizedclient|not authori[sz]ed|unauthori[sz]ed|forbidden|\\b40[13]\\b|api key|credential|bearer/i;\nconst models = /model/i;\nconst unavailable =\n /access|invalid|not found|identifier|unsupported|not supported|isn['\u2019]t supported|not available|isn['\u2019]t available/i;\n\nconst providerNames: Record<string, string> = { 'amazon-bedrock': 'Bedrock', adobe: 'Adobe' };\nconst filtered =\n /Provider (?:stopped with|finish_reason): (?:content_filter(?:ed)?|guardrail_intervened|sensitive)|content[_ ]filter(?:ed|ing)?\\b|guardrail[_ ]intervened/i;\nconst refused =\n /refused to complete the request|Provider (?:stopped with|finish_reason): refusal|\\bstop_?reason\\W+refusal\\b/i;\nconst unconnected = /no api key|not configured|no credentials/i;\nconst missing = /missing/i;\nconst adobeAuth =\n /\\b401\\b|authenticat|unauthori[sz]ed|expired|invalid[_ ]?(?:token|credentials?|api[_ ]key)|token verification/i;\n\nfunction proxyRefusal(error: string): { message: string; detail: string } {\n try {\n const body = JSON.parse(error.match(/\\{.*\\}/s)?.[0] ?? '') as {\n error?: { type?: unknown; message?: unknown };\n };\n const { type, message } = body.error ?? {};\n const status = error.match(/^\\s*(\\d{3})\\b/)?.[1];\n return {\n message: typeof message === 'string' ? message : error,\n detail: typeof type === 'string' ? [status, type].filter(Boolean).join(' ') : error,\n };\n } catch {\n return { message: error, detail: error };\n }\n}\n\nexport const CONNECT_IT = 'Connect it in Settings \u203A Accounts.';\nexport const LOG_IN_AGAIN = 'Log in again to continue.';\n\nconst logged = new Set<string>();\n\nfunction logOnce(error: string): void {\n if (logged.has(error)) return;\n logged.add(error);\n warn(`slicc-agent: the Adobe session expired: ${error}`);\n}\n\nexport function providerName(provider: string | undefined, fallback = 'The provider'): string {\n return providerNames[provider ?? ''] ?? fallback;\n}\n\nexport function errorPart(error: string, provider?: string): MessagePart {\n const action = errorAction(error);\n if (action === 'drop-turn') {\n return {\n type: 'error',\n message: filtered.test(error)\n ? \"The model's content filter stopped this reply.\"\n : 'The model refused to answer this turn.',\n detail: error,\n action,\n };\n }\n const name = providerName(provider);\n const account = provider ? { account: provider } : {};\n if (unconnected.test(error))\n return {\n type: 'error',\n message: `${name} isn't connected.`,\n detail: CONNECT_IT,\n action: 'settings',\n ...account,\n };\n if (action === 'settings' && provider === 'adobe') {\n if (!adobeAuth.test(error)) return { type: 'error', ...proxyRefusal(error), action: 'retry' };\n logOnce(error);\n return {\n type: 'error',\n message: 'Your Adobe session has expired.',\n detail: LOG_IN_AGAIN,\n action: 'login',\n ...account,\n };\n }\n if (action === 'settings') {\n const lead = missing.test(error)\n ? `${name} needs an API key.`\n : `${name} rejected the API key.`;\n return { type: 'error', message: lead, detail: error, action, ...account };\n }\n return action === 'change-model'\n ? {\n type: 'error',\n message: \"This model isn't available with the current account.\",\n detail: error,\n action,\n }\n : { type: 'error', message: error, action };\n}\n\nexport function errorAction(message: string): ErrorAction {\n if (filtered.test(message) || refused.test(message)) return 'drop-turn';\n if (credentials.test(message)) return 'settings';\n if (models.test(message) && unavailable.test(message)) return 'change-model';\n return 'retry';\n}\n\nfunction sprinkleMessage(id: string, entry: EntryRecord): AssistantMessage {\n const data = (entry.data ?? {}) as { sprinkle?: unknown; at?: unknown };\n return {\n id,\n role: 'assistant',\n parts: [{ type: 'sprinkle', sprinkle: String(data.sprinkle ?? '') }],\n status: 'done',\n createdAt: typeof data.at === 'number' ? data.at : 0,\n };\n}\n\nexport function assistant(\n id: string,\n message: ModelMessage,\n status?: AssistantStatus\n): AssistantMessage {\n const blocks = Array.isArray(message.content) ? message.content : [];\n const parts = blocks.map(part).filter((item): item is MessagePart => item !== undefined);\n if (message.errorMessage) parts.push(errorPart(message.errorMessage, message.provider));\n const used = usage(message);\n return {\n id,\n role: 'assistant',\n parts,\n status: status ?? statuses[message.stopReason ?? ''] ?? 'done',\n createdAt: message.timestamp ?? 0,\n ...(message.provider && message.model ? { model: `${message.provider}/${message.model}` } : {}),\n ...(used ? { usage: used } : {}),\n };\n}\n\nfunction user(\n id: string,\n message: ModelMessage,\n delivered: Delivered | undefined,\n from: From | undefined\n): UserMessage {\n return {\n id,\n role: 'user',\n ...shownUser(message.content, `ref:${id.slice(1)}:0`),\n createdAt: message.timestamp ?? 0,\n ...(delivered ? { delivered } : {}),\n ...(from ? { from: from.from } : {}),\n ...(from?.agent ? { origin: 'agent' as const } : {}),\n };\n}\n\nfunction guestUser(\n id: string,\n message: ModelMessage,\n delivered: Delivered | undefined,\n data: unknown\n): UserMessage {\n const input = data as Partial<GuestInput> | undefined;\n const name = String(input?.name ?? '');\n const shown = shownUser(message.content, `ref:${id.slice(1)}:0`);\n return {\n id,\n role: 'user',\n ...shown,\n text: guestText(shown.text, name),\n createdAt: message.timestamp ?? 0,\n ...(delivered ? { delivered } : {}),\n from: name,\n origin: 'guest',\n ...(typeof input?.seat === 'string' ? { seat: input.seat } : {}),\n };\n}\n\nexport function origins(entries: readonly EntryRecord[]): Map<number, From> {\n const out = new Map<number, From>();\n for (const entry of entries)\n if (entry.kind === FROM_KIND) {\n const note = entry.data as From;\n out.set(note.entry, note);\n }\n return out;\n}\n\ntype NotifyLick = LickMessage & { summary?: string; report?: string };\n\nfunction lickMessage(\n id: string,\n entry: EntryRecord,\n decisions: ReadonlyMap<string, LickDecision>\n): NotifyLick | undefined {\n const lick = entryLick(entry);\n if (!lick) return undefined;\n const state = lick.actions?.length ? (decisions.get(lick.id) ?? 'pending') : undefined;\n return {\n id,\n role: 'lick',\n channel: lick.channel,\n title: lick.title,\n text: lick.text,\n createdAt: lick.at,\n ...(lick.body ? { body: lick.body } : {}),\n ...(lick.count > 1 ? { count: lick.count } : {}),\n ...(state ? { state } : {}),\n ...(lick.severity ? { severity: lick.severity } : {}),\n ...(lick.summary ? { summary: lick.summary } : {}),\n ...(lick.report ? { report: lick.report } : {}),\n };\n}\n\nfunction withoutLickTools(message: AssistantMessage): AssistantMessage {\n const parts = message.parts.filter((item) => item.type !== 'tool' || !LICK_TOOLS[item.tool.name]);\n return parts.length === message.parts.length ? message : { ...message, parts };\n}\n\nfunction system(id: string, entry: EntryRecord, text: string): SystemMessage {\n const reason = (entry.data as Json | undefined)?.reason as SystemMessage['trigger'] | undefined;\n return {\n id,\n role: 'system',\n kind: 'compaction',\n text,\n createdAt: 0,\n state: 'summarized',\n ...(reason ? { trigger: reason } : {}),\n };\n}\n\nfunction tools(messages: Message[]): Map<string, ToolCall> {\n const found = new Map<string, ToolCall>();\n for (const message of messages) {\n if (message.role !== 'assistant') continue;\n for (const item of message.parts) if (item.type === 'tool') found.set(item.tool.id, item.tool);\n }\n return found;\n}\n\ninterface Diagnostic {\n severity: string;\n message: string;\n}\n\ninterface Note {\n severity: 'error' | 'warn' | 'info';\n text: string;\n}\n\ntype Card = ToolCall & { notes?: Note[] };\n\nconst RANK: Note['severity'][] = ['error', 'warn', 'info'];\n\nconst LINE = /^\\[(error|warn|info)\\] (.*)$/;\n\nfunction rendered(diagnostics: readonly Diagnostic[]): string {\n return `<harness>\\n${diagnostics.map((item) => `[${item.severity}] ${item.message}`).join('\\n')}\\n</harness>`;\n}\n\nfunction parsed(text: string): Diagnostic[] | undefined {\n const body = /^<harness>\\n([\\s\\S]*)\\n<\\/harness>$/.exec(text)?.[1];\n if (body === undefined) return undefined;\n const found = body.split('\\n').map((line) => LINE.exec(line));\n if (!found.every((match): match is RegExpExecArray => match !== null)) return undefined;\n return found.map(([, severity, message]) => ({ severity, message }) as Diagnostic);\n}\n\nfunction harness(\n content: ModelMessage['content'],\n data: unknown\n): { content: ModelMessage['content']; diagnostics: Diagnostic[] } {\n const last = Array.isArray(content) ? content.at(-1) : undefined;\n const stored = (data as { diagnostics?: unknown } | undefined)?.diagnostics;\n const listed = Array.isArray(stored) ? (stored as Diagnostic[]) : parsed(last?.text ?? '');\n if (!listed?.length || last?.type !== 'text' || last.text !== rendered(listed))\n return { content, diagnostics: [] };\n return { content: (content as Block[]).slice(0, -1), diagnostics: listed };\n}\n\nconst count = (value: string | undefined): string => Number(value).toLocaleString('en');\n\nconst ends: Record<string, string> = { end: ' from the start', beginning: ' from the end' };\n\nconst phrasings: [RegExp, (match: RegExpExecArray) => string][] = [\n [/^Command exited with code (-?\\d+)$/, (match) => `Exited with code ${match[1]}`],\n [/^Command timed out after (.+)$/, (match) => `Timed out after ${match[1]}`],\n [/^Command aborted$/, () => 'Aborted'],\n [/^Full output: (.+)$/, (match) => `Full output in ${match[1]}`],\n [\n /^Output truncated(?: to its (beginning|end))?: (\\d+) lines, (\\d+) bytes dropped$/,\n (match) =>\n `Output truncated: ${count(match[2])} lines and ${count(match[3])} bytes dropped${ends[match[1] ?? ''] ?? ''}`,\n ],\n];\n\nfunction phrased(message: string): string {\n const line = message\n .replace(/\\n[\\s\\S]*$/, '')\n .trim()\n .replace(/\\.$/, '');\n for (const [pattern, phrase] of phrasings) {\n const match = pattern.exec(line);\n if (match) return phrase(match);\n }\n return line;\n}\n\nfunction note(diagnostic: Diagnostic): Note {\n return { severity: diagnostic.severity as Note['severity'], text: phrased(diagnostic.message) };\n}\n\nfunction settle(calls: Map<string, ToolCall>, result: ModelMessage, data?: unknown): void {\n const call = calls.get(result.toolCallId ?? '') as Card | undefined;\n if (!call) return;\n const split = harness(result.content, data);\n call.output = textOf(split.content);\n if (split.diagnostics.length)\n call.notes = split.diagnostics\n .map(note)\n .sort((a, b) => RANK.indexOf(a.severity) - RANK.indexOf(b.severity));\n call.status = result.isError ? 'error' : 'done';\n const image = Array.isArray(result.content)\n ? (result.content as { type: string; data?: string; mimeType?: string }[]).find(\n (part) => part.type === 'image' && part.data && part.mimeType\n )\n : undefined;\n if (image) call.image = `data:${image.mimeType};base64,${image.data}`;\n const patch = result.details?.patch;\n const diff = typeof patch === 'string' ? fromPatch(patch) : undefined;\n if (diff) call.diff = diff;\n else if (result.isError) delete call.diff;\n}\n\nfunction rewound(id: string, entry: EntryRecord): SystemMessage {\n const data = (entry.data ?? {}) as { stopped?: string[]; restored?: string[]; files?: boolean };\n const notes = [\n 'Its prompt is back in the composer.',\n ...(data.stopped?.length\n ? [`Stopped ${data.stopped.map((name) => `scoop ${name}`).join(', ')}.`]\n : []),\n ...(data.restored?.length\n ? [`Restored ${data.restored.map((name) => `scoop ${name}`).join(', ')}.`]\n : []),\n ...(data.files ? ['Changed files stay; use Changes or git to revert them.'] : []),\n ];\n return {\n id,\n role: 'system',\n kind: 'notice',\n title: 'Rewound 1 turn',\n text: notes.join(' '),\n createdAt: 0,\n };\n}\n\nfunction thawed(id: string, entry: EntryRecord): SystemMessage {\n const data = (entry.data ?? {}) as { name?: string; kind?: string; v6?: boolean };\n const what = data.v6 ? 'SLICC 6' : data.kind === 'agent' ? 'agent run' : 'scoop';\n return {\n id,\n role: 'system',\n kind: 'notice',\n title: `Thawed from ${what} ${data.name ?? ''}`.trim(),\n text: textOf(entry.model?.[0]?.content ?? ''),\n createdAt: entry.model?.[0]?.timestamp ?? 0,\n };\n}\n\nexport function reviewed(entries: readonly EntryRecord[]): Map<string, GuestOutcome> {\n const out = new Map<string, GuestOutcome>();\n for (const entry of entries) {\n if (entry.kind !== REVIEWED_KIND) continue;\n const data = (entry.data ?? {}) as Partial<GuestReviewed>;\n if (typeof data.review === 'string' && data.outcome) out.set(data.review, data.outcome);\n }\n return out;\n}\n\nfunction asked(\n id: string,\n entry: EntryRecord,\n outcomes: ReadonlyMap<string, GuestOutcome>\n): SystemMessage {\n const data = (entry.data ?? {}) as Partial<GuestAsked>;\n return {\n id,\n role: 'system',\n kind: 'notice',\n text: askedLine(\n String(data.name ?? ''),\n String(data.text ?? ''),\n outcomes.get(String(data.review))\n ),\n createdAt: typeof data.at === 'number' ? data.at : 0,\n };\n}\n\nfunction secrets(id: string, entry: EntryRecord): SystemMessage {\n const data = (entry.data ?? {}) as {\n names?: SecretName[];\n error?: string | null;\n exposed?: string[];\n };\n return {\n id,\n role: 'system',\n kind: 'notice',\n title: data.error ? 'Secrets unavailable' : 'Secrets listed',\n text: listedLine({\n names: data.names ?? [],\n error: data.error ?? null,\n exposed: data.exposed ?? [],\n }),\n createdAt: entry.model?.[0]?.timestamp ?? 0,\n };\n}\n\nconst V6_KINDS: Record<string, string> = {\n user: 'pi.user',\n assistant: 'pi.assistant',\n toolResult: 'pi.tool-result',\n};\n\nfunction entryMessage(\n entry: EntryRecord,\n out: Message[],\n calls: Map<string, ToolCall>,\n delivered: Delivered | undefined,\n notes: {\n decisions: ReadonlyMap<string, LickDecision>;\n origins: ReadonlyMap<number, From>;\n outcomes: ReadonlyMap<string, GuestOutcome>;\n }\n): void {\n const id = `e${entry.id}`;\n const model = entry.model?.[0] as ModelMessage | undefined;\n if (entry.kind === 'slicc.v6' && model) {\n const kind = V6_KINDS[model.role as string];\n if (kind) entryMessage({ ...entry, kind }, out, calls, delivered, notes);\n return;\n }\n if (entry.kind === 'slicc.rewound') out.push(rewound(id, entry));\n if (entry.kind === 'slicc.thawed') out.push(thawed(id, entry));\n else if (entry.kind === SECRETS_KIND) out.push(secrets(id, entry));\n else if (entry.kind === ASKED_KIND) out.push(asked(id, entry, notes.outcomes));\n else if (entry.kind === SPRINKLE_KIND) out.push(sprinkleMessage(id, entry));\n else if (entry.kind === 'pi.user' && model)\n out.push(\n lickMessage(id, entry, notes.decisions) ??\n user(id, model, delivered, notes.origins.get(entry.id))\n );\n else if (entry.kind === GUEST_INPUT_KIND && model)\n out.push(guestUser(id, model, delivered, entry.data));\n else if (entry.kind === 'pi.assistant' && model) {\n const message = assistant(id, model);\n out.push(withoutLickTools(message));\n for (const [key, value] of tools([message])) calls.set(key, value);\n } else if (entry.kind === 'pi.tool-result' && model) settle(calls, model, entry.data);\n else if (entry.kind === 'pi.compaction') out.push(system(id, entry, textOf(model?.content)));\n}\n\nconst slotStatus: Record<string, ToolStatus> = {\n pending: 'running',\n running: 'running',\n done: 'done',\n};\n\nfunction live(view: ConversationView, out: Message[], calls: Map<string, ToolCall>): void {\n const state = view.docs['pi.live'] as Live | undefined;\n for (const slot of state?.tools ?? []) {\n const call = calls.get(slot.callId);\n if (call && call.status === 'running') {\n call.status = slotStatus[slot.status] ?? 'running';\n call.output = slot.output ?? call.output;\n }\n }\n const partial = state?.generation?.message;\n if (partial) out.push(assistant('live', partial, 'streaming'));\n for (const compaction of state?.compactions ?? []) {\n out.push({\n id: `compaction-${compaction.taskId}`,\n role: 'system',\n kind: 'compaction',\n text: '',\n createdAt: 0,\n state: 'summarizing',\n trigger: compaction.reason as SystemMessage['trigger'],\n });\n }\n const retry = state?.generation?.retry;\n if (retry)\n out.push({\n id: 'retry',\n role: 'system',\n kind: 'notice',\n text: retry.error,\n createdAt: retry.at,\n });\n}\n\nexport function toMessages(\n view: ConversationView | undefined,\n deliveries: Readonly<Record<string, Delivered>> = {}\n): Message[] {\n const out: Message[] = [];\n if (!view) return out;\n const calls = new Map<string, ToolCall>();\n const notes = {\n decisions: lickDecisions(view.entries),\n origins: origins(view.entries),\n outcomes: reviewed(view.entries),\n };\n let previous: EntryRecord | undefined;\n for (const entry of view.entries) {\n const steered = previous?.kind === 'pi.tool-result' ? 'steer' : undefined;\n entryMessage(entry, out, calls, deliveries[String(entry.id)] ?? steered, notes);\n previous = entry;\n }\n live(view, out, calls);\n return out;\n}\n\nfunction shownUser(\n content: ModelMessage['content'],\n prefix?: string\n): Pick<UserMessage, 'text' | 'attachments'> {\n const { text, attachments } = splitUser(content, prefix);\n return attachments.length ? { text, attachments } : { text };\n}\n\nexport function isBusy(view: ConversationView | undefined): boolean {\n return Boolean((view?.docs['pi.live'] as Live | undefined)?.run);\n}\n\nexport function queued(\n view: ConversationView | undefined,\n at: (id: string) => number = () => 0\n): UserMessage[] {\n const items = ((view?.docs['pi.inbox'] as { items?: Json[] } | undefined)?.items ?? []) as {\n id: number;\n mode: string;\n content?: ModelMessage['content'];\n entry?: { kind?: string; data?: Partial<GuestInput> };\n }[];\n return items\n .filter((item) => item.mode !== 'write' && !parseLick(textOf(item.content)))\n .map((item) => {\n const shown = shownUser(item.content);\n const data = item.entry?.data;\n const guest = item.entry?.kind === GUEST_INPUT_KIND ? String(data?.name ?? '') : null;\n const seat = typeof data?.seat === 'string' ? { seat: data.seat } : {};\n return {\n id: `q${item.id}`,\n role: 'user' as const,\n ...shown,\n ...(guest === null\n ? {}\n : {\n text: guestText(shown.text, guest),\n from: guest,\n origin: 'guest' as const,\n ...seat,\n }),\n createdAt: at(`q${item.id}`),\n queued: true,\n mode: item.mode === 'steer' ? 'steer' : 'queue',\n };\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["note", "part"]
|
|
7
7
|
}
|
|
@@ -2,6 +2,8 @@ export declare const LICK_CHANNELS: readonly ['webhook', 'cron', 'sprinkle', 'fs
|
|
|
2
2
|
export type LickChannel = (typeof LICK_CHANNELS)[number];
|
|
3
3
|
export type LickAction = 'confirm' | 'dismiss';
|
|
4
4
|
export type LickSeverity = 'warn' | 'error';
|
|
5
|
+
export declare const SUMMARY = 120;
|
|
6
|
+
export declare function clip(value: string, size?: number): string;
|
|
5
7
|
export type LickTarget = 'cone' | `cone:${string}` | `scoop:${string}`;
|
|
6
8
|
export type Lick = {
|
|
7
9
|
id: string;
|
|
@@ -14,6 +16,8 @@ export type Lick = {
|
|
|
14
16
|
at: number;
|
|
15
17
|
actions?: LickAction[];
|
|
16
18
|
severity?: LickSeverity;
|
|
19
|
+
summary?: string;
|
|
20
|
+
report?: string;
|
|
17
21
|
};
|
|
18
22
|
export declare function escapeText(value: string): string;
|
|
19
23
|
export declare function escapeAttribute(value: string): string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/slicc-agent",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.13.1",
|
|
4
4
|
"description": "SLICC's agent core on pi durable: an agent worker, session storage, remoting and pi extensions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"release": "semantic-release"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
|
-
"@ai-ecoverse/slicc-kernel": "1.
|
|
68
|
+
"@ai-ecoverse/slicc-kernel": "1.130.0",
|
|
69
69
|
"@ai-ecoverse/slicc-scrub": "1.0.0",
|
|
70
70
|
"@ai-ecoverse/slicc-shared-web": "1.24.2",
|
|
71
71
|
"@ai-ecoverse/slicc-spectrum": "1.94.2",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
},
|
|
87
87
|
"dependencies": {
|
|
88
88
|
"@ai-ecoverse/slicc-browsers": "1.8.5",
|
|
89
|
-
"@ai-ecoverse/slicc-vault": "1.
|
|
89
|
+
"@ai-ecoverse/slicc-vault": "1.13.0",
|
|
90
90
|
"@aws-sdk/middleware-websocket": "^3.972.54",
|
|
91
91
|
"@earendil-works/chord": "1.3.0",
|
|
92
92
|
"@earendil-works/pi-ai": "1.3.0",
|