@ai-ecoverse/slicc-agent 3.47.4 → 3.48.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.
Files changed (64) hide show
  1. package/README.md +18 -6
  2. package/dist/attachments.js +168 -0
  3. package/dist/attachments.js.map +7 -0
  4. package/dist/changes/git.js +4 -2
  5. package/dist/changes/git.js.map +2 -2
  6. package/dist/control.js +30 -10
  7. package/dist/control.js.map +2 -2
  8. package/dist/host.js +26 -9
  9. package/dist/host.js.map +3 -3
  10. package/dist/images/fit.js +50 -0
  11. package/dist/images/fit.js.map +7 -0
  12. package/dist/images/index.js +7 -47
  13. package/dist/images/index.js.map +3 -3
  14. package/dist/index.js +15 -0
  15. package/dist/index.js.map +2 -2
  16. package/dist/main.js +11 -2
  17. package/dist/main.js.map +2 -2
  18. package/dist/mcp/runtime.js +1 -1
  19. package/dist/mcp/runtime.js.map +2 -2
  20. package/dist/net.js +10 -1
  21. package/dist/net.js.map +2 -2
  22. package/dist/readonly.js +7 -1
  23. package/dist/readonly.js.map +2 -2
  24. package/dist/remote/client.js +9 -4
  25. package/dist/remote/client.js.map +2 -2
  26. package/dist/remote/host.js +1 -0
  27. package/dist/remote/host.js.map +2 -2
  28. package/dist/services.js.map +2 -2
  29. package/dist/spectrum/agent-port.js +17 -2
  30. package/dist/spectrum/agent-port.js.map +2 -2
  31. package/dist/spectrum/changes-port.js +4 -0
  32. package/dist/spectrum/changes-port.js.map +2 -2
  33. package/dist/spectrum/messages.js +7 -2
  34. package/dist/spectrum/messages.js.map +2 -2
  35. package/dist/spectrum/outgoing.js +73 -0
  36. package/dist/spectrum/outgoing.js.map +7 -0
  37. package/dist/system.js +11 -6
  38. package/dist/system.js.map +2 -2
  39. package/dist/thumbs.js +105 -0
  40. package/dist/thumbs.js.map +7 -0
  41. package/dist/types/attachments.d.ts +58 -0
  42. package/dist/types/changes/git.d.ts +3 -2
  43. package/dist/types/control.d.ts +2 -1
  44. package/dist/types/host.d.ts +4 -0
  45. package/dist/types/images/fit.d.ts +10 -0
  46. package/dist/types/images/index.d.ts +2 -10
  47. package/dist/types/index.d.ts +2 -0
  48. package/dist/types/main.d.ts +2 -0
  49. package/dist/types/net.d.ts +8 -0
  50. package/dist/types/remote/client.d.ts +2 -0
  51. package/dist/types/remote/host.d.ts +2 -0
  52. package/dist/types/services.d.ts +16 -0
  53. package/dist/types/spectrum/changes-port.d.ts +1 -0
  54. package/dist/types/spectrum/outgoing.d.ts +8 -0
  55. package/dist/types/system.d.ts +1 -1
  56. package/dist/types/thumbs.d.ts +19 -0
  57. package/dist/types/uploads.d.ts +14 -0
  58. package/dist/types/views.d.ts +2 -1
  59. package/dist/uploads.js +43 -0
  60. package/dist/uploads.js.map +7 -0
  61. package/dist/views.js +6 -2
  62. package/dist/views.js.map +2 -2
  63. package/package.json +3 -3
  64. package/packages/vfs-root/skills/browser/SKILL.md +2 -2
package/dist/thumbs.js ADDED
@@ -0,0 +1,105 @@
1
+ import { decodedSize, fromBase64 } from "./attachments.js";
2
+ import { base64 } from "./images/fit.js";
3
+ const KEEP = 48 * 1024;
4
+ const THUMB_EDGE = 256;
5
+ const thumbnail = async (image) => {
6
+ const scope = globalThis;
7
+ if (!scope.createImageBitmap || !scope.OffscreenCanvas) return void 0;
8
+ const bitmap = await scope.createImageBitmap(
9
+ new Blob([fromBase64(image.data)], { type: image.mimeType })
10
+ );
11
+ for (const edge of [THUMB_EDGE, THUMB_EDGE / 2]) {
12
+ const scale = Math.min(1, edge / Math.max(bitmap.width, bitmap.height));
13
+ const width = Math.max(1, Math.round(bitmap.width * scale));
14
+ const height = Math.max(1, Math.round(bitmap.height * scale));
15
+ const canvas = new scope.OffscreenCanvas(width, height);
16
+ canvas.getContext("2d")?.drawImage(bitmap, 0, 0, width, height);
17
+ const blob = await canvas.convertToBlob({ type: "image/jpeg", quality: 0.7 });
18
+ const data = base64(new Uint8Array(await blob.arrayBuffer()));
19
+ if (data.length <= KEEP) {
20
+ bitmap.close();
21
+ return { data, mimeType: "image/jpeg" };
22
+ }
23
+ }
24
+ bitmap.close();
25
+ return void 0;
26
+ };
27
+ function slimmer(make, ready) {
28
+ const made = /* @__PURE__ */ new Map();
29
+ const entries = /* @__PURE__ */ new Set();
30
+ let closed = false;
31
+ const thumb = (key, part) => {
32
+ const full = { mimeType: String(part.mimeType), size: decodedSize(part.data) };
33
+ if (!made.has(key)) {
34
+ made.set(key, null);
35
+ void make({ data: part.data, mimeType: full.mimeType }).catch(() => void 0).then((picture2) => {
36
+ if (!picture2 || closed) return;
37
+ made.set(key, picture2);
38
+ ready();
39
+ });
40
+ }
41
+ const picture = made.get(key);
42
+ return picture ? { type: "image", data: picture.data, mimeType: picture.mimeType, full } : { type: "image", data: "", mimeType: full.mimeType, full };
43
+ };
44
+ const content = (key, value) => {
45
+ if (!Array.isArray(value)) return value;
46
+ let changed = false;
47
+ const out = value.map((part, index) => {
48
+ if (part.type !== "image" || (part.data?.length ?? 0) <= KEEP) return part;
49
+ changed = true;
50
+ return thumb(`${key}:${index}`, part);
51
+ });
52
+ return changed ? out : value;
53
+ };
54
+ const entry = (record) => {
55
+ if (!record.model?.length) return record;
56
+ let changed = false;
57
+ const seen = () => entries.add(record.id);
58
+ const model = record.model.map((message, at) => {
59
+ const next = content(`${record.id}:${at}`, message.content);
60
+ if (next === message.content) return message;
61
+ changed = true;
62
+ seen();
63
+ return { ...message, content: next };
64
+ });
65
+ return changed ? { ...record, model } : record;
66
+ };
67
+ const inbox = (doc) => {
68
+ if (!doc?.items) return doc;
69
+ let changed = false;
70
+ const items = doc.items.map((item) => {
71
+ const next = content(`q${item.id}:0`, item.content);
72
+ if (next === item.content) return item;
73
+ changed = true;
74
+ return { ...item, content: next };
75
+ });
76
+ return changed ? { ...doc, items } : doc;
77
+ };
78
+ return {
79
+ slim(view) {
80
+ const entries2 = view.entries.map(entry);
81
+ const queue = view.docs["pi.inbox"];
82
+ const slimmed = inbox(queue);
83
+ const same = slimmed === queue && entries2.every((item, at) => item === view.entries[at]);
84
+ if (same) return view;
85
+ return {
86
+ ...view,
87
+ entries: entries2,
88
+ docs: slimmed === queue ? view.docs : { ...view.docs, "pi.inbox": slimmed }
89
+ };
90
+ },
91
+ shown: (entry2) => entries.has(entry2),
92
+ dispose() {
93
+ closed = true;
94
+ made.clear();
95
+ entries.clear();
96
+ }
97
+ };
98
+ }
99
+ export {
100
+ KEEP,
101
+ THUMB_EDGE,
102
+ slimmer,
103
+ thumbnail
104
+ };
105
+ //# sourceMappingURL=thumbs.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/thumbs.ts"],
4
+ "sourcesContent": ["import type { ConversationView, EntryRecord } from '@earendil-works/pi-durable';\nimport { decodedSize, fromBase64 } from './attachments.ts';\nimport { base64 } from './images/fit.ts';\n\nexport const KEEP = 48 * 1024;\nexport const THUMB_EDGE = 256;\n\nexport interface Picture {\n data: string;\n mimeType: string;\n}\n\nexport type Thumbnail = (image: Picture) => Promise<Picture | undefined>;\n\nexport interface FullImage {\n mimeType: string;\n size: number;\n}\n\ntype Part = { type?: string; data?: string; mimeType?: string; full?: FullImage };\ntype Message = { content?: unknown };\ntype Item = { id?: number; content?: unknown };\n\ntype Canvas = {\n getContext(kind: '2d'): { drawImage(...args: unknown[]): void } | null;\n convertToBlob(options: { type: string; quality?: number }): Promise<Blob>;\n};\n\ntype Scope = {\n createImageBitmap?: (blob: Blob) => Promise<{ width: number; height: number; close(): void }>;\n OffscreenCanvas?: new (width: number, height: number) => Canvas;\n};\n\nexport const thumbnail: Thumbnail = async (image) => {\n const scope = globalThis as unknown as Scope;\n if (!scope.createImageBitmap || !scope.OffscreenCanvas) return undefined;\n const bitmap = await scope.createImageBitmap(\n new Blob([fromBase64(image.data) as BlobPart], { type: image.mimeType })\n );\n for (const edge of [THUMB_EDGE, THUMB_EDGE / 2]) {\n const scale = Math.min(1, edge / Math.max(bitmap.width, bitmap.height));\n const width = Math.max(1, Math.round(bitmap.width * scale));\n const height = Math.max(1, Math.round(bitmap.height * scale));\n const canvas = new scope.OffscreenCanvas(width, height);\n canvas.getContext('2d')?.drawImage(bitmap, 0, 0, width, height);\n const blob = await canvas.convertToBlob({ type: 'image/jpeg', quality: 0.7 });\n const data = base64(new Uint8Array(await blob.arrayBuffer()));\n if (data.length <= KEEP) {\n bitmap.close();\n return { data, mimeType: 'image/jpeg' };\n }\n }\n bitmap.close();\n return undefined;\n};\n\nexport interface Slimmer {\n slim(view: ConversationView): ConversationView;\n shown(entry: number): boolean;\n dispose(): void;\n}\n\nexport function slimmer(make: Thumbnail, ready: () => void): Slimmer {\n const made = new Map<string, Picture | null>();\n const entries = new Set<number>();\n let closed = false;\n const thumb = (key: string, part: Part): Part => {\n const full = { mimeType: String(part.mimeType), size: decodedSize(part.data as string) };\n if (!made.has(key)) {\n made.set(key, null);\n void make({ data: part.data as string, mimeType: full.mimeType })\n .catch(() => undefined)\n .then((picture) => {\n if (!picture || closed) return;\n made.set(key, picture);\n ready();\n });\n }\n const picture = made.get(key);\n return picture\n ? { type: 'image', data: picture.data, mimeType: picture.mimeType, full }\n : { type: 'image', data: '', mimeType: full.mimeType, full };\n };\n const content = (key: string, value: unknown): unknown => {\n if (!Array.isArray(value)) return value;\n let changed = false;\n const out = (value as Part[]).map((part, index) => {\n if (part.type !== 'image' || (part.data?.length ?? 0) <= KEEP) return part;\n changed = true;\n return thumb(`${key}:${index}`, part);\n });\n return changed ? out : value;\n };\n const entry = (record: EntryRecord): EntryRecord => {\n if (!record.model?.length) return record;\n let changed = false;\n const seen = () => entries.add(record.id);\n const model = (record.model as readonly Message[]).map((message, at) => {\n const next = content(`${record.id}:${at}`, message.content);\n if (next === message.content) return message;\n changed = true;\n seen();\n return { ...message, content: next };\n });\n return changed ? ({ ...record, model } as unknown as EntryRecord) : record;\n };\n const inbox = (doc: { items?: Item[] } | undefined) => {\n if (!doc?.items) return doc;\n let changed = false;\n const items = doc.items.map((item) => {\n const next = content(`q${item.id}:0`, item.content);\n if (next === item.content) return item;\n changed = true;\n return { ...item, content: next };\n });\n return changed ? { ...doc, items } : doc;\n };\n return {\n slim(view) {\n const entries = view.entries.map(entry);\n const queue = view.docs['pi.inbox'] as { items?: Item[] } | undefined;\n const slimmed = inbox(queue);\n const same = slimmed === queue && entries.every((item, at) => item === view.entries[at]);\n if (same) return view;\n return {\n ...view,\n entries,\n docs: slimmed === queue ? view.docs : { ...view.docs, 'pi.inbox': slimmed as never },\n };\n },\n shown: (entry) => entries.has(entry),\n dispose() {\n closed = true;\n made.clear();\n entries.clear();\n },\n };\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["picture", "entries", "entry"]
7
+ }
@@ -0,0 +1,58 @@
1
+ export type AttachmentKind = 'image' | 'text' | 'file';
2
+ export interface SendAttachment {
3
+ name: string;
4
+ mimeType: string;
5
+ kind: AttachmentKind;
6
+ data?: string;
7
+ text?: string;
8
+ path?: string;
9
+ }
10
+ export interface ShownAttachment {
11
+ id: string;
12
+ name: string;
13
+ kind: AttachmentKind;
14
+ mimeType: string;
15
+ size: number;
16
+ url?: string;
17
+ text?: string;
18
+ path?: string;
19
+ }
20
+ export type Part = {
21
+ type: 'text';
22
+ text: string;
23
+ } | {
24
+ type: 'image';
25
+ data: string;
26
+ mimeType: string;
27
+ };
28
+ export interface Saver {
29
+ sees: boolean;
30
+ save?(name: string, bytes: Uint8Array): Promise<string>;
31
+ }
32
+ export declare const IMAGE_LIMIT: number;
33
+ export declare const FILE_LIMIT: number;
34
+ export declare const TOTAL_LIMIT: number;
35
+ export declare const SEEN: Set<string>;
36
+ export declare const BLIND = "This model can't see images, so the image was saved as a file instead.";
37
+ export declare const TOGETHER = "These attachments add up to more than 10 MiB; send them in separate messages.";
38
+ export declare function mib(bytes: number): string;
39
+ export declare function limitOf(kind: AttachmentKind): number;
40
+ export declare function tooLarge(name: string, kind: AttachmentKind): string;
41
+ export declare function textBytes(text: string): number;
42
+ export declare function decodedSize(data: string): number;
43
+ export declare function fromBase64(data: string): Uint8Array;
44
+ interface Header {
45
+ kind: AttachmentKind;
46
+ name: string;
47
+ type: string;
48
+ path?: string;
49
+ size?: number;
50
+ }
51
+ export declare function wrap(header: Header, body: string): string;
52
+ export declare const REF: RegExp;
53
+ export declare function splitUser(content: unknown, prefix?: string): {
54
+ text: string;
55
+ attachments: ShownAttachment[];
56
+ };
57
+ export declare function userParts(text: string, attachments: readonly SendAttachment[], saver: Saver): Promise<Part[]>;
58
+ export {};
@@ -4,8 +4,9 @@ export declare const MAX_TEXT: number;
4
4
  export declare const MAX_FILES = 200;
5
5
  export declare const SCAN_DEPTH = 3;
6
6
  export declare const SCAN_ENTRIES = 200;
7
- export declare const NO_GIT = "Changes needs git, and git isn't installed. Install it with `pnpm add -g @ai-ecoverse/wasm-git`, then run `git init` in a folder under /home, or clone with slicc-node or the extension connected.";
8
- export declare const NO_REPO = "Changes lists the changes in git repositories, and there is none here yet. Run `git init` in a folder under /home, or clone one with slicc-node or the extension connected.";
7
+ export declare const CLONE_COMMAND = "curl -fsSL https://get.sliccy.ai/slicc | sh";
8
+ export declare const NO_GIT = "Changes needs git, and git isn't installed. Install it with `pnpm add -g @ai-ecoverse/wasm-git`, then run `git init` in a folder under /home. To clone from the internet, link slicc or the SLICC extension.";
9
+ export declare const NO_REPO = "Changes lists the changes in git repositories, and there are none here yet. Run `git init` in a folder under /home. To clone from the internet, link slicc or the SLICC extension.";
9
10
  export type ChangeStatus = 'added' | 'modified' | 'deleted';
10
11
  export type ChangeKind = 'tracked' | 'untracked' | 'conflict';
11
12
  export interface FileChange {
@@ -11,6 +11,7 @@ import type { SecretName } from './secrets/names.ts';
11
11
  import type { AgentControl } from './services.ts';
12
12
  import type { SkillsRuntime } from './skills/index.ts';
13
13
  import type { SprinklesRuntime } from './sprinkles/index.ts';
14
+ import { type Uploads } from './uploads.ts';
14
15
  export interface HostLicks {
15
16
  licks: Licks;
16
17
  sources: LickSources;
@@ -21,4 +22,4 @@ export interface HostScoops {
21
22
  }
22
23
  export declare function readable(out: string): string;
23
24
  export declare function submissionId(value: string): SubmissionId | undefined;
24
- export declare function createAgentControl(harness: Harness, target: Conversation | Cones, deliveries?: Deliveries, licks?: HostLicks, scoops?: HostScoops, skills?: Pick<SkillsRuntime, 'expand'>, sprinkles?: Pick<SprinklesRuntime, 'send' | 'call'>, memory?: Pick<MemoryRuntime, 'save' | 'remove'>, freezer?: Pick<FreezerRuntime, 'freeze' | 'newChat' | 'thaw' | 'discard'>, changes?: Pick<ChangesRuntime, 'open' | 'accept' | 'revert'>, monitor?: Pick<Monitor, 'open' | 'close'>, secrets?: () => readonly SecretName[]): AgentControl;
25
+ export declare function createAgentControl(harness: Harness, target: Conversation | Cones, deliveries?: Deliveries, licks?: HostLicks, scoops?: HostScoops, skills?: Pick<SkillsRuntime, 'expand'>, sprinkles?: Pick<SprinklesRuntime, 'send' | 'call'>, memory?: Pick<MemoryRuntime, 'save' | 'remove'>, freezer?: Pick<FreezerRuntime, 'freeze' | 'newChat' | 'thaw' | 'discard'>, changes?: Pick<ChangesRuntime, 'open' | 'accept' | 'revert'>, monitor?: Pick<Monitor, 'open' | 'close'>, secrets?: () => readonly SecretName[], uploads?: Uploads): AgentControl;
@@ -13,6 +13,8 @@ import type { VaultState } from './secrets/vault.ts';
13
13
  import { AgentSettings } from './services.ts';
14
14
  import type { SkillsRuntime } from './skills/index.ts';
15
15
  import type { SprinklesRuntime } from './sprinkles/index.ts';
16
+ import { type Thumbnail } from './thumbs.ts';
17
+ import type { Uploads } from './uploads.ts';
16
18
  import type { PortEndpoint } from './wire.ts';
17
19
  export interface ConnectOptions {
18
20
  readOnly?: boolean;
@@ -47,4 +49,6 @@ export declare function hostAgent(agent: Agent, options?: {
47
49
  monitor?: Pick<MonitorOptions, 'kernel' | 'groups' | 'users' | 'secrets'>;
48
50
  pages?: Pages;
49
51
  secrets?: HostSecrets;
52
+ uploads?: Uploads;
53
+ thumbnail?: Thumbnail;
50
54
  }): Promise<AgentHost>;
@@ -0,0 +1,10 @@
1
+ export declare const MAX_WIDTH = 1600;
2
+ export declare const MAX_BASE64: number;
3
+ export type Image = {
4
+ bytes: Uint8Array;
5
+ mimeType: string;
6
+ };
7
+ export type Shrink = (image: Image) => Promise<Image>;
8
+ export declare function mimeOf(bytes: Uint8Array): string | undefined;
9
+ export declare function base64(bytes: Uint8Array): string;
10
+ export declare const shrink: Shrink;
@@ -1,12 +1,4 @@
1
1
  import { type Extension, type ToolRegistration } from '@earendil-works/pi-durable';
2
- export declare const MAX_WIDTH = 1600;
3
- export declare const MAX_BASE64: number;
4
- export type Image = {
5
- bytes: Uint8Array;
6
- mimeType: string;
7
- };
8
- export type Shrink = (image: Image) => Promise<Image>;
9
- export declare function mimeOf(bytes: Uint8Array): string | undefined;
10
- export declare function base64(bytes: Uint8Array): string;
11
- export declare const shrink: Shrink;
2
+ import { type Shrink } from './fit.ts';
3
+ export { base64, type Image, MAX_BASE64, MAX_WIDTH, mimeOf, type Shrink, shrink } from './fit.ts';
12
4
  export declare function imageReads(read: ToolRegistration, fit?: Shrink): Extension;
@@ -1,5 +1,6 @@
1
1
  export { ADOBE, ADOBE_PROXY, type AdobeConfig, type AdobeModelInfo, adobeClassifiers, adobeConfig, adobeModel, adobeProvider, adobeUsage, type Budget, } from './adobe.ts';
2
2
  export { type Agent, type AgentOptions, answerText, openAgent } from './agent.ts';
3
+ export { FILE_LIMIT, IMAGE_LIMIT, type SendAttachment, type ShownAttachment, splitUser, TOTAL_LIMIT, userParts, } from './attachments.ts';
3
4
  export { type AgentConnection, connectAgent } from './client.ts';
4
5
  export { createAgentControl } from './control.ts';
5
6
  export { CREDENTIALS_DATABASE, EncryptedCredentialStore } from './credentials.ts';
@@ -18,5 +19,6 @@ export { type AccountState, AGENT_SESSION, AgentControl, AgentPagesService, Agen
18
19
  export { BEDROCK, createAgentSettings, createSliccModels, DEFAULT_MODEL, DEFAULT_REGIONS, sliccProviders, withDefaultRegions, } from './settings.ts';
19
20
  export { AGENT_DIRECTORY, type OpfsSqliteOptions, openMemorySqliteStorage, openOpfsSqliteStorage, WasmSqliteDatabase, } from './sqlite.ts';
20
21
  export { type SystemFacts, systemSection } from './system.ts';
22
+ export { UPLOADS, type Uploads, upload } from './uploads.ts';
21
23
  export { PortChannel, type PortEndpoint, withUnrefTimers } from './wire.ts';
22
24
  export { type KernelPorts, kernelPort, kernelPorts, serveConnections, type WorkerScope, } from './worker.ts';
@@ -1,4 +1,5 @@
1
1
  import type { CredentialStore, Provider } from '@earendil-works/pi-ai';
2
+ import type { Models } from '@earendil-works/pi-ai/models';
2
3
  import { type ModelRef, type Storage } from '@earendil-works/pi-durable';
3
4
  import { type AgentHost } from './host.ts';
4
5
  import type { KernelClient } from './kernel/client.ts';
@@ -30,6 +31,7 @@ export interface AgentWorkerOptions {
30
31
  }
31
32
  export declare function usersOf(admin: MessagePort | undefined, wanted: boolean | undefined, attach: KernelAttach): UserClients | undefined;
32
33
  export declare const STORE_DIR = "/.slicc";
34
+ export declare function seesImages(models: Pick<Models, 'getModel'>, ref: ModelRef | undefined): boolean;
33
35
  export declare const MCP_CALLBACK = "/oauth/mcp-callback";
34
36
  export declare function alive(client: KernelClient): (pid: number) => Promise<boolean>;
35
37
  export declare function runAgentWorker(scope: AgentWorkerScope, options?: AgentWorkerOptions): Promise<AgentHost>;
@@ -8,6 +8,8 @@ export interface Transport {
8
8
  traits?: {
9
9
  crossOrigin?: 'cors' | 'any';
10
10
  manualRedirects?: boolean;
11
+ unavailable?: boolean;
12
+ exit?: TransportExit;
11
13
  };
12
14
  fetch(request: {
13
15
  url: string;
@@ -17,6 +19,12 @@ export interface Transport {
17
19
  signal?: AbortSignal;
18
20
  }): Promise<TransportResponse>;
19
21
  }
22
+ export interface TransportExit {
23
+ kind: string;
24
+ caps: string[];
25
+ }
26
+ export declare function linkedExit(transport: Transport | undefined): TransportExit | undefined;
27
+ export declare function pageOnly(transport: Transport | undefined): boolean;
20
28
  export type Loopback = (request: Request, options: {
21
29
  port: number;
22
30
  signal?: AbortSignal;
@@ -1,3 +1,4 @@
1
+ import type { SendAttachment } from '../attachments.ts';
1
2
  import type { AgentConnection } from '../client.ts';
2
3
  import type { Delivered, SendMode } from '../services.ts';
3
4
  import type { RemoteEvent } from './events.ts';
@@ -11,6 +12,7 @@ export interface RemoteControl {
11
12
  text: string;
12
13
  whenBusy?: SendMode;
13
14
  thinking?: boolean;
15
+ attachments?: SendAttachment[];
14
16
  }): Promise<{
15
17
  delivered: Delivered;
16
18
  events: AsyncIterable<RemoteEvent>;
@@ -1,5 +1,6 @@
1
1
  import type { Context, ReplicatedState } from '@earendil-works/chord';
2
2
  import { type AgentEvent, type Conversation, type EntryRecord, type Harness, type SnapshotEvent, type SubmissionId } from '@earendil-works/pi-durable';
3
+ import type { SendAttachment } from '../attachments.ts';
3
4
  import { type VaultState } from '../secrets/vault.ts';
4
5
  import type { Delivered, SendMode, SendRequest, SendResponse } from '../services.ts';
5
6
  import { Mapper, type RemoteEvent } from './events.ts';
@@ -28,6 +29,7 @@ export interface AgentRemote {
28
29
  text: string;
29
30
  whenBusy: SendMode;
30
31
  thinking: boolean;
32
+ attachments?: SendAttachment[];
31
33
  }, context: Context): Promise<RemoteOpened>;
32
34
  watch(request: {
33
35
  agentId: string | null;
@@ -1,5 +1,6 @@
1
1
  import { type Context, type JsonValue, type ReplicatedState } from '@earendil-works/chord';
2
2
  import type { ConversationView } from '@earendil-works/pi-durable';
3
+ import type { SendAttachment, ShownAttachment } from './attachments.ts';
3
4
  import type { ChangesView } from './changes/git.ts';
4
5
  import type { FrozenCone } from './freezer/index.ts';
5
6
  import type { MemoryEntry, MemoryScope, MemoryTag } from './memory/format.ts';
@@ -13,6 +14,7 @@ export interface SendRequest {
13
14
  whenBusy: SendMode;
14
15
  requestId: string | null;
15
16
  agentId?: string | null;
17
+ attachments?: SendAttachment[];
16
18
  }
17
19
  export interface OperationError {
18
20
  code: string;
@@ -83,7 +85,20 @@ export interface AgentControl {
83
85
  changeRevert(path: string, context: Context): Promise<Created>;
84
86
  monitorOpen(lease: string | null, resync: boolean, context: Context): Promise<MonitorLease>;
85
87
  monitorClose(lease: string, context: Context): Promise<void>;
88
+ image(ref: ImageRef, context: Context): Promise<StoredImage>;
86
89
  }
90
+ export interface ImageRef {
91
+ entry: number;
92
+ message: number;
93
+ part: number;
94
+ }
95
+ export type StoredImage = {
96
+ data: string;
97
+ mimeType: string;
98
+ } | {
99
+ data: null;
100
+ mimeType: null;
101
+ };
87
102
  export interface MonitorLease {
88
103
  lease: string;
89
104
  ttlMs: number;
@@ -201,6 +216,7 @@ export type Rewound = {
201
216
  done: true;
202
217
  text: string;
203
218
  reason: null;
219
+ attachments?: ShownAttachment[];
204
220
  } | {
205
221
  done: false;
206
222
  text: null;
@@ -10,6 +10,7 @@ export declare class ChangesAdapter extends Emitter<ChangesEvents> implements Ch
10
10
  constructor(connection: AgentConnection, report?: Report);
11
11
  changes(): readonly Change[];
12
12
  unavailable(): string | null;
13
+ commands(): readonly string[];
13
14
  accept(path: string): void;
14
15
  revert(path: string): Promise<void>;
15
16
  }
@@ -0,0 +1,8 @@
1
+ import type { Attachment } from '@ai-ecoverse/slicc-spectrum/ui';
2
+ import { type SendAttachment } from '../attachments.ts';
3
+ import { type Shrink } from '../images/fit.ts';
4
+ import type { ImageRef, StoredImage } from '../services.ts';
5
+ export type Read = (url: string) => Promise<Uint8Array>;
6
+ export type Stored = (ref: ImageRef) => Promise<StoredImage>;
7
+ export declare const readUrl: Read;
8
+ export declare function sendAttachments(attachments?: readonly Attachment[], read?: Read, fit?: Shrink, stored?: Stored): Promise<SendAttachment[]>;
@@ -1,5 +1,5 @@
1
1
  import type { KernelClient } from './kernel/client.ts';
2
- import type { Transport } from './net.ts';
2
+ import { type Transport } from './net.ts';
3
3
  export interface SystemFacts {
4
4
  version: string;
5
5
  commands: string[];
@@ -0,0 +1,19 @@
1
+ import type { ConversationView } from '@earendil-works/pi-durable';
2
+ export declare const KEEP: number;
3
+ export declare const THUMB_EDGE = 256;
4
+ export interface Picture {
5
+ data: string;
6
+ mimeType: string;
7
+ }
8
+ export type Thumbnail = (image: Picture) => Promise<Picture | undefined>;
9
+ export interface FullImage {
10
+ mimeType: string;
11
+ size: number;
12
+ }
13
+ export declare const thumbnail: Thumbnail;
14
+ export interface Slimmer {
15
+ slim(view: ConversationView): ConversationView;
16
+ shown(entry: number): boolean;
17
+ dispose(): void;
18
+ }
19
+ export declare function slimmer(make: Thumbnail, ready: () => void): Slimmer;
@@ -0,0 +1,14 @@
1
+ import type { Context } from '@earendil-works/chord';
2
+ import type { Conversation, ModelRef } from '@earendil-works/pi-durable';
3
+ import type { ExecutionEnv } from '@earendil-works/pi-durable/env';
4
+ import type { Saver } from './attachments.ts';
5
+ export interface Uploads {
6
+ env(target: {
7
+ conversationId: number;
8
+ cwd?: string;
9
+ }, context: Context): ExecutionEnv | undefined | Promise<ExecutionEnv | undefined>;
10
+ sees(model: ModelRef | undefined): boolean;
11
+ }
12
+ export declare const UPLOADS = "uploads";
13
+ export declare function upload(env: ExecutionEnv, name: string, bytes: Uint8Array, context: Context): Promise<string>;
14
+ export declare function saverFor(conversation: Conversation, uploads: Uploads | undefined, context: Context): Promise<Saver>;
@@ -4,9 +4,10 @@ import type { Agent } from './agent.ts';
4
4
  import { type AgentsState } from './agents.ts';
5
5
  import type { AgentsSummary } from './services.ts';
6
6
  export declare function summarize(state: Readonly<AgentsState>): AgentsSummary;
7
- export declare function agentViews(agent: Agent, context: Context): Promise<{
7
+ export declare function agentViews(agent: Agent, context: Context, slim?: (view: ConversationView) => ConversationView): Promise<{
8
8
  agents: import("@earendil-works/chord").MutableReplicatedState<AgentsSummary>;
9
9
  views: import("@earendil-works/chord").MutableReplicatedState<Record<string, ConversationView>>;
10
10
  sync: () => Promise<unknown>;
11
+ refresh(): void;
11
12
  dispose(): void;
12
13
  }>;
@@ -0,0 +1,43 @@
1
+ const UPLOADS = "uploads";
2
+ function cleaned(name) {
3
+ const clean = name.replace(/[/\\\0]/g, "_").replace(/^\.+/, "") || "attachment";
4
+ const dot = clean.lastIndexOf(".");
5
+ return dot > 0 ? { stem: clean.slice(0, dot), ext: clean.slice(dot) } : { stem: clean, ext: "" };
6
+ }
7
+ function must(result, name) {
8
+ if (!result.ok) throw new Error(`Couldn't save ${name}: ${result.error?.message}`);
9
+ }
10
+ async function upload(env, name, bytes, context) {
11
+ const dir = `${env.cwd.replace(/\/+$/, "")}/${UPLOADS}`;
12
+ must(await env.createDir(dir, { recursive: true }, context), name);
13
+ const { stem, ext } = cleaned(name);
14
+ for (let n = 0; n < 100; n++) {
15
+ const path = `${dir}/${n ? `${stem}-${n}${ext}` : `${stem}${ext}`}`;
16
+ if ((await env.fileInfo(path, context)).ok) continue;
17
+ must(await env.writeFile(path, bytes, context), name);
18
+ return path;
19
+ }
20
+ throw new Error(`Couldn't find a free name for ${name} in ${dir}.`);
21
+ }
22
+ async function saverFor(conversation, uploads, context) {
23
+ if (!uploads) return { sees: true };
24
+ const agent = await conversation.agent(context);
25
+ return {
26
+ sees: uploads.sees(agent.model),
27
+ async save(name, bytes) {
28
+ const target = {
29
+ conversationId: conversation.id,
30
+ ...agent.cwd ? { cwd: agent.cwd } : {}
31
+ };
32
+ const env = await uploads.env(target, context);
33
+ if (!env) throw new Error(`This agent has no folder to save ${name} in.`);
34
+ return upload(env, name, bytes, context);
35
+ }
36
+ };
37
+ }
38
+ export {
39
+ UPLOADS,
40
+ saverFor,
41
+ upload
42
+ };
43
+ //# sourceMappingURL=uploads.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/uploads.ts"],
4
+ "sourcesContent": ["import type { Context } from '@earendil-works/chord';\nimport type { Conversation, ModelRef } from '@earendil-works/pi-durable';\nimport type { ExecutionEnv } from '@earendil-works/pi-durable/env';\nimport type { Saver } from './attachments.ts';\n\nexport interface Uploads {\n env(\n target: { conversationId: number; cwd?: string },\n context: Context\n ): ExecutionEnv | undefined | Promise<ExecutionEnv | undefined>;\n sees(model: ModelRef | undefined): boolean;\n}\n\nexport const UPLOADS = 'uploads';\n\nfunction cleaned(name: string): { stem: string; ext: string } {\n const clean = name.replace(/[/\\\\\\0]/g, '_').replace(/^\\.+/, '') || 'attachment';\n const dot = clean.lastIndexOf('.');\n return dot > 0 ? { stem: clean.slice(0, dot), ext: clean.slice(dot) } : { stem: clean, ext: '' };\n}\n\nfunction must(result: { ok: boolean; error?: { message: string } }, name: string): void {\n if (!result.ok) throw new Error(`Couldn't save ${name}: ${result.error?.message}`);\n}\n\nexport async function upload(\n env: ExecutionEnv,\n name: string,\n bytes: Uint8Array,\n context: Context\n): Promise<string> {\n const dir = `${env.cwd.replace(/\\/+$/, '')}/${UPLOADS}`;\n must(await env.createDir(dir, { recursive: true }, context), name);\n const { stem, ext } = cleaned(name);\n for (let n = 0; n < 100; n++) {\n const path = `${dir}/${n ? `${stem}-${n}${ext}` : `${stem}${ext}`}`;\n if ((await env.fileInfo(path, context)).ok) continue;\n must(await env.writeFile(path, bytes, context), name);\n return path;\n }\n throw new Error(`Couldn't find a free name for ${name} in ${dir}.`);\n}\n\nexport async function saverFor(\n conversation: Conversation,\n uploads: Uploads | undefined,\n context: Context\n): Promise<Saver> {\n if (!uploads) return { sees: true };\n const agent = await conversation.agent(context);\n return {\n sees: uploads.sees(agent.model),\n async save(name, bytes) {\n const target = {\n conversationId: conversation.id,\n ...(agent.cwd ? { cwd: agent.cwd } : {}),\n };\n const env = await uploads.env(target, context);\n if (!env) throw new Error(`This agent has no folder to save ${name} in.`);\n return upload(env, name, bytes, context);\n },\n };\n}\n"],
5
+ "mappings": "AAaO,MAAM,UAAU;AAEvB,SAAS,QAAQ,MAA6C;AAC5D,QAAM,QAAQ,KAAK,QAAQ,YAAY,GAAG,EAAE,QAAQ,QAAQ,EAAE,KAAK;AACnE,QAAM,MAAM,MAAM,YAAY,GAAG;AACjC,SAAO,MAAM,IAAI,EAAE,MAAM,MAAM,MAAM,GAAG,GAAG,GAAG,KAAK,MAAM,MAAM,GAAG,EAAE,IAAI,EAAE,MAAM,OAAO,KAAK,GAAG;AACjG;AAEA,SAAS,KAAK,QAAsD,MAAoB;AACtF,MAAI,CAAC,OAAO,GAAI,OAAM,IAAI,MAAM,iBAAiB,IAAI,KAAK,OAAO,OAAO,OAAO,EAAE;AACnF;AAEA,eAAsB,OACpB,KACA,MACA,OACA,SACiB;AACjB,QAAM,MAAM,GAAG,IAAI,IAAI,QAAQ,QAAQ,EAAE,CAAC,IAAI,OAAO;AACrD,OAAK,MAAM,IAAI,UAAU,KAAK,EAAE,WAAW,KAAK,GAAG,OAAO,GAAG,IAAI;AACjE,QAAM,EAAE,MAAM,IAAI,IAAI,QAAQ,IAAI;AAClC,WAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,UAAM,OAAO,GAAG,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,GAAG,KAAK,GAAG,IAAI,GAAG,GAAG,EAAE;AACjE,SAAK,MAAM,IAAI,SAAS,MAAM,OAAO,GAAG,GAAI;AAC5C,SAAK,MAAM,IAAI,UAAU,MAAM,OAAO,OAAO,GAAG,IAAI;AACpD,WAAO;AAAA,EACT;AACA,QAAM,IAAI,MAAM,iCAAiC,IAAI,OAAO,GAAG,GAAG;AACpE;AAEA,eAAsB,SACpB,cACA,SACA,SACgB;AAChB,MAAI,CAAC,QAAS,QAAO,EAAE,MAAM,KAAK;AAClC,QAAM,QAAQ,MAAM,aAAa,MAAM,OAAO;AAC9C,SAAO;AAAA,IACL,MAAM,QAAQ,KAAK,MAAM,KAAK;AAAA,IAC9B,MAAM,KAAK,MAAM,OAAO;AACtB,YAAM,SAAS;AAAA,QACb,gBAAgB,aAAa;AAAA,QAC7B,GAAI,MAAM,MAAM,EAAE,KAAK,MAAM,IAAI,IAAI,CAAC;AAAA,MACxC;AACA,YAAM,MAAM,MAAM,QAAQ,IAAI,QAAQ,OAAO;AAC7C,UAAI,CAAC,IAAK,OAAM,IAAI,MAAM,oCAAoC,IAAI,MAAM;AACxE,aAAO,OAAO,KAAK,MAAM,OAAO,OAAO;AAAA,IACzC;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
package/dist/views.js CHANGED
@@ -24,7 +24,7 @@ function wanted(state) {
24
24
  if (live(scoop)) out.set(id, scoop.conversation);
25
25
  return out;
26
26
  }
27
- async function agentViews(agent, context) {
27
+ async function agentViews(agent, context, slim = (view) => view) {
28
28
  const agents = replicatedState(summarize(agent.agents.state()));
29
29
  const views = replicatedState({});
30
30
  const mounted = /* @__PURE__ */ new Map();
@@ -50,12 +50,13 @@ async function agentViews(agent, context) {
50
50
  if (!handle) continue;
51
51
  const attached = await handle.viewState(context);
52
52
  const put = (value) => views.change(context, (draft) => {
53
- draft[id] = value;
53
+ draft[id] = slim(value);
54
54
  });
55
55
  put(attached.value);
56
56
  mounted.set(id, {
57
57
  conversation,
58
58
  attached,
59
+ put,
59
60
  off: attached.subscribe((value) => put(value))
60
61
  });
61
62
  }
@@ -71,6 +72,9 @@ async function agentViews(agent, context) {
71
72
  agents,
72
73
  views,
73
74
  sync,
75
+ refresh() {
76
+ for (const entry of mounted.values()) entry.put(entry.attached.value);
77
+ },
74
78
  dispose() {
75
79
  offChange();
76
80
  offCone();
package/dist/views.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/views.ts"],
4
- "sourcesContent": ["import { type AttachedReplicatedState, type Context, replicatedState } from '@earendil-works/chord';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport type { Agent } from './agent.ts';\nimport { type AgentsState, live } from './agents.ts';\nimport type { AgentsSummary } from './services.ts';\n\nexport function summarize(state: Readonly<AgentsState>): AgentsSummary {\n const cones = Object.entries(state.cones).map(([id, cone]) => ({\n id,\n name: cone.name,\n kind: 'cone' as const,\n parentId: null,\n role: null,\n }));\n const scoops = Object.entries(state.scoops)\n .filter(([, scoop]) => live(scoop))\n .map(([id, scoop]) => ({\n id,\n name: scoop.name,\n kind: 'scoop' as const,\n parentId: scoop.parent ?? scoop.cone,\n role: scoop.role,\n }));\n return { active: state.active, agents: [...cones, ...scoops] };\n}\n\nfunction wanted(state: Readonly<AgentsState>): Map<string, number> {\n const out = new Map<string, number>();\n for (const [id, cone] of Object.entries(state.cones)) out.set(id, cone.conversation);\n for (const [id, scoop] of Object.entries(state.scoops))\n if (live(scoop)) out.set(id, scoop.conversation);\n return out;\n}\n\ninterface Mounted {\n conversation: number;\n attached: AttachedReplicatedState<ConversationView>;\n off: () => void;\n}\n\nexport async function agentViews(agent: Agent, context: Context) {\n const agents = replicatedState<AgentsSummary>(summarize(agent.agents.state()));\n const views = replicatedState<Record<string, ConversationView>>({});\n const mounted = new Map<string, Mounted>();\n let lock: Promise<unknown> = Promise.resolve();\n const sync = () => {\n lock = lock\n .then(async () => {\n const state = agent.agents.state();\n agents.replace(context, summarize(state));\n const target = wanted(state);\n for (const [id, entry] of [...mounted]) {\n if (target.get(id) === entry.conversation) continue;\n entry.off();\n entry.attached.dispose();\n mounted.delete(id);\n if (!target.has(id))\n views.change(context, (draft) => {\n delete draft[id];\n });\n }\n for (const [id, conversation] of target) {\n if (mounted.has(id)) continue;\n const handle = await agent.agents.conversation(id, context);\n if (!handle) continue;\n const attached = await handle.viewState(context);\n const put = (value: ConversationView) =>\n views.change(context, (draft) => {\n (draft as Record<string, unknown>)[id] = value;\n });\n put(attached.value);\n mounted.set(id, {\n conversation,\n attached,\n off: attached.subscribe((value) => put(value)),\n });\n }\n })\n .catch(() => undefined);\n return lock;\n };\n await sync();\n const offChange = agent.agents.onChange(() => void sync());\n const offCone = agent.agents.onCone(async () => {\n await sync();\n });\n return {\n agents,\n views,\n sync,\n dispose() {\n offChange();\n offCone();\n for (const entry of mounted.values()) {\n entry.off();\n entry.attached.dispose();\n }\n mounted.clear();\n },\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { type AttachedReplicatedState, type Context, replicatedState } from '@earendil-works/chord';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport type { Agent } from './agent.ts';\nimport { type AgentsState, live } from './agents.ts';\nimport type { AgentsSummary } from './services.ts';\n\nexport function summarize(state: Readonly<AgentsState>): AgentsSummary {\n const cones = Object.entries(state.cones).map(([id, cone]) => ({\n id,\n name: cone.name,\n kind: 'cone' as const,\n parentId: null,\n role: null,\n }));\n const scoops = Object.entries(state.scoops)\n .filter(([, scoop]) => live(scoop))\n .map(([id, scoop]) => ({\n id,\n name: scoop.name,\n kind: 'scoop' as const,\n parentId: scoop.parent ?? scoop.cone,\n role: scoop.role,\n }));\n return { active: state.active, agents: [...cones, ...scoops] };\n}\n\nfunction wanted(state: Readonly<AgentsState>): Map<string, number> {\n const out = new Map<string, number>();\n for (const [id, cone] of Object.entries(state.cones)) out.set(id, cone.conversation);\n for (const [id, scoop] of Object.entries(state.scoops))\n if (live(scoop)) out.set(id, scoop.conversation);\n return out;\n}\n\ninterface Mounted {\n conversation: number;\n attached: AttachedReplicatedState<ConversationView>;\n put: (value: ConversationView) => void;\n off: () => void;\n}\n\nexport async function agentViews(\n agent: Agent,\n context: Context,\n slim: (view: ConversationView) => ConversationView = (view) => view\n) {\n const agents = replicatedState<AgentsSummary>(summarize(agent.agents.state()));\n const views = replicatedState<Record<string, ConversationView>>({});\n const mounted = new Map<string, Mounted>();\n let lock: Promise<unknown> = Promise.resolve();\n const sync = () => {\n lock = lock\n .then(async () => {\n const state = agent.agents.state();\n agents.replace(context, summarize(state));\n const target = wanted(state);\n for (const [id, entry] of [...mounted]) {\n if (target.get(id) === entry.conversation) continue;\n entry.off();\n entry.attached.dispose();\n mounted.delete(id);\n if (!target.has(id))\n views.change(context, (draft) => {\n delete draft[id];\n });\n }\n for (const [id, conversation] of target) {\n if (mounted.has(id)) continue;\n const handle = await agent.agents.conversation(id, context);\n if (!handle) continue;\n const attached = await handle.viewState(context);\n const put = (value: ConversationView) =>\n views.change(context, (draft) => {\n (draft as Record<string, unknown>)[id] = slim(value);\n });\n put(attached.value);\n mounted.set(id, {\n conversation,\n attached,\n put,\n off: attached.subscribe((value) => put(value)),\n });\n }\n })\n .catch(() => undefined);\n return lock;\n };\n await sync();\n const offChange = agent.agents.onChange(() => void sync());\n const offCone = agent.agents.onCone(async () => {\n await sync();\n });\n return {\n agents,\n views,\n sync,\n refresh() {\n for (const entry of mounted.values()) entry.put(entry.attached.value);\n },\n dispose() {\n offChange();\n offCone();\n for (const entry of mounted.values()) {\n entry.off();\n entry.attached.dispose();\n }\n mounted.clear();\n },\n };\n}\n"],
5
+ "mappings": "AAAA,SAAqD,uBAAuB;AAG5E,SAA2B,YAAY;AAGhC,SAAS,UAAU,OAA6C;AACrE,QAAM,QAAQ,OAAO,QAAQ,MAAM,KAAK,EAAE,IAAI,CAAC,CAAC,IAAI,IAAI,OAAO;AAAA,IAC7D;AAAA,IACA,MAAM,KAAK;AAAA,IACX,MAAM;AAAA,IACN,UAAU;AAAA,IACV,MAAM;AAAA,EACR,EAAE;AACF,QAAM,SAAS,OAAO,QAAQ,MAAM,MAAM,EACvC,OAAO,CAAC,CAAC,EAAE,KAAK,MAAM,KAAK,KAAK,CAAC,EACjC,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO;AAAA,IACrB;AAAA,IACA,MAAM,MAAM;AAAA,IACZ,MAAM;AAAA,IACN,UAAU,MAAM,UAAU,MAAM;AAAA,IAChC,MAAM,MAAM;AAAA,EACd,EAAE;AACJ,SAAO,EAAE,QAAQ,MAAM,QAAQ,QAAQ,CAAC,GAAG,OAAO,GAAG,MAAM,EAAE;AAC/D;AAEA,SAAS,OAAO,OAAmD;AACjE,QAAM,MAAM,oBAAI,IAAoB;AACpC,aAAW,CAAC,IAAI,IAAI,KAAK,OAAO,QAAQ,MAAM,KAAK,EAAG,KAAI,IAAI,IAAI,KAAK,YAAY;AACnF,aAAW,CAAC,IAAI,KAAK,KAAK,OAAO,QAAQ,MAAM,MAAM;AACnD,QAAI,KAAK,KAAK,EAAG,KAAI,IAAI,IAAI,MAAM,YAAY;AACjD,SAAO;AACT;AASA,eAAsB,WACpB,OACA,SACA,OAAqD,CAAC,SAAS,MAC/D;AACA,QAAM,SAAS,gBAA+B,UAAU,MAAM,OAAO,MAAM,CAAC,CAAC;AAC7E,QAAM,QAAQ,gBAAkD,CAAC,CAAC;AAClE,QAAM,UAAU,oBAAI,IAAqB;AACzC,MAAI,OAAyB,QAAQ,QAAQ;AAC7C,QAAM,OAAO,MAAM;AACjB,WAAO,KACJ,KAAK,YAAY;AAChB,YAAM,QAAQ,MAAM,OAAO,MAAM;AACjC,aAAO,QAAQ,SAAS,UAAU,KAAK,CAAC;AACxC,YAAM,SAAS,OAAO,KAAK;AAC3B,iBAAW,CAAC,IAAI,KAAK,KAAK,CAAC,GAAG,OAAO,GAAG;AACtC,YAAI,OAAO,IAAI,EAAE,MAAM,MAAM,aAAc;AAC3C,cAAM,IAAI;AACV,cAAM,SAAS,QAAQ;AACvB,gBAAQ,OAAO,EAAE;AACjB,YAAI,CAAC,OAAO,IAAI,EAAE;AAChB,gBAAM,OAAO,SAAS,CAAC,UAAU;AAC/B,mBAAO,MAAM,EAAE;AAAA,UACjB,CAAC;AAAA,MACL;AACA,iBAAW,CAAC,IAAI,YAAY,KAAK,QAAQ;AACvC,YAAI,QAAQ,IAAI,EAAE,EAAG;AACrB,cAAM,SAAS,MAAM,MAAM,OAAO,aAAa,IAAI,OAAO;AAC1D,YAAI,CAAC,OAAQ;AACb,cAAM,WAAW,MAAM,OAAO,UAAU,OAAO;AAC/C,cAAM,MAAM,CAAC,UACX,MAAM,OAAO,SAAS,CAAC,UAAU;AAC/B,UAAC,MAAkC,EAAE,IAAI,KAAK,KAAK;AAAA,QACrD,CAAC;AACH,YAAI,SAAS,KAAK;AAClB,gBAAQ,IAAI,IAAI;AAAA,UACd;AAAA,UACA;AAAA,UACA;AAAA,UACA,KAAK,SAAS,UAAU,CAAC,UAAU,IAAI,KAAK,CAAC;AAAA,QAC/C,CAAC;AAAA,MACH;AAAA,IACF,CAAC,EACA,MAAM,MAAM,MAAS;AACxB,WAAO;AAAA,EACT;AACA,QAAM,KAAK;AACX,QAAM,YAAY,MAAM,OAAO,SAAS,MAAM,KAAK,KAAK,CAAC;AACzD,QAAM,UAAU,MAAM,OAAO,OAAO,YAAY;AAC9C,UAAM,KAAK;AAAA,EACb,CAAC;AACD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AACR,iBAAW,SAAS,QAAQ,OAAO,EAAG,OAAM,IAAI,MAAM,SAAS,KAAK;AAAA,IACtE;AAAA,IACA,UAAU;AACR,gBAAU;AACV,cAAQ;AACR,iBAAW,SAAS,QAAQ,OAAO,GAAG;AACpC,cAAM,IAAI;AACV,cAAM,SAAS,QAAQ;AAAA,MACzB;AACA,cAAQ,MAAM;AAAA,IAChB;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-agent",
3
- "version": "3.47.4",
3
+ "version": "3.48.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",
@@ -63,7 +63,7 @@
63
63
  "devDependencies": {
64
64
  "@ai-ecoverse/slicc-kernel": "1.47.1",
65
65
  "@ai-ecoverse/slicc-shared-web": "1.13.1",
66
- "@ai-ecoverse/slicc-spectrum": "1.66.0",
66
+ "@ai-ecoverse/slicc-spectrum": "1.67.0",
67
67
  "@ai-ecoverse/wasm-bash": "5.3.0-17",
68
68
  "@ai-ecoverse/wasm-coreutils": "9.12.0-5",
69
69
  "@ai-ecoverse/wasm-git": "2.55.0-15",
@@ -78,7 +78,7 @@
78
78
  "typescript": "7.0.2"
79
79
  },
80
80
  "dependencies": {
81
- "@ai-ecoverse/slicc-browsers": "1.4.0",
81
+ "@ai-ecoverse/slicc-browsers": "1.5.0",
82
82
  "@earendil-works/chord": "1.0.4",
83
83
  "@earendil-works/pi-ai": "1.0.4",
84
84
  "@earendil-works/pi-client": "1.0.4",
@@ -21,9 +21,9 @@ It only lists tabs: it opens and changes nothing. This may be when SLICC asks th
21
21
 
22
22
  - **exit 0:** the browser is connected.
23
23
  - **exit 124 or 137** (no answer in 5 seconds): SLICC is waiting for the user to allow browser control. Tell them so, and run the check again once they've answered.
24
- - **"no browser to drive":** the browser isn't connected. Tell the user, with the way to connect that the error names (install slicc-extension, or run `npx @ai-ecoverse/slicc-node`).
24
+ - **"no browser to drive":** the browser isn't connected. Tell the user, with the way to connect that the error names (install slicc-extension, or link the `slicc` CLI).
25
25
  - **"declined":** the user declined browser control. Tell them, and don't retry; reloading SLICC asks again.
26
- - **"no seven page connected", or the connection is refused:** the local link (slicc-node) isn't running or isn't connected to this page. Tell the user.
26
+ - **"no seven page connected", or the connection is refused:** the linked `slicc` CLI isn't running or isn't connected to this page. Tell the user.
27
27
  - **Anything else,** including an empty `SLICC_CDP_URL` or no `playwright-cli`: the browser isn't connected (the extension isn't installed or attached). Say so and stop.
28
28
 
29
29
  Never try `127.0.0.1:9222`: that is the sandbox's own loopback.