@ai-ecoverse/slicc-agent 3.3.0 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +37 -1
  2. package/dist/control.js +44 -1
  3. package/dist/control.js.map +2 -2
  4. package/dist/host.js +5 -1
  5. package/dist/host.js.map +2 -2
  6. package/dist/kernel/activity.js +39 -0
  7. package/dist/kernel/activity.js.map +7 -0
  8. package/dist/kernel/env.js +16 -8
  9. package/dist/kernel/env.js.map +2 -2
  10. package/dist/licks/boot.js +66 -0
  11. package/dist/licks/boot.js.map +7 -0
  12. package/dist/licks/config.js +126 -0
  13. package/dist/licks/config.js.map +7 -0
  14. package/dist/licks/cron.js +120 -0
  15. package/dist/licks/cron.js.map +7 -0
  16. package/dist/licks/extension.js +123 -0
  17. package/dist/licks/extension.js.map +7 -0
  18. package/dist/licks/fswatch.js +156 -0
  19. package/dist/licks/fswatch.js.map +7 -0
  20. package/dist/licks/glob.js +47 -0
  21. package/dist/licks/glob.js.map +7 -0
  22. package/dist/licks/index.js +36 -0
  23. package/dist/licks/index.js.map +7 -0
  24. package/dist/licks/lick.js +107 -0
  25. package/dist/licks/lick.js.map +7 -0
  26. package/dist/licks/licks.js +194 -0
  27. package/dist/licks/licks.js.map +7 -0
  28. package/dist/licks/schedules.js +50 -0
  29. package/dist/licks/schedules.js.map +7 -0
  30. package/dist/licks/sources.js +139 -0
  31. package/dist/licks/sources.js.map +7 -0
  32. package/dist/licks/state.js +56 -0
  33. package/dist/licks/state.js.map +7 -0
  34. package/dist/main.js +23 -4
  35. package/dist/main.js.map +2 -2
  36. package/dist/services.js.map +2 -2
  37. package/dist/spectrum/agent-port.js +6 -1
  38. package/dist/spectrum/agent-port.js.map +2 -2
  39. package/dist/spectrum/messages.js +29 -5
  40. package/dist/spectrum/messages.js.map +2 -2
  41. package/dist/system.js +16 -0
  42. package/dist/system.js.map +2 -2
  43. package/dist/types/control.d.ts +6 -1
  44. package/dist/types/host.d.ts +2 -0
  45. package/dist/types/kernel/activity.d.ts +10 -0
  46. package/dist/types/kernel/env.d.ts +2 -0
  47. package/dist/types/licks/boot.d.ts +9 -0
  48. package/dist/types/licks/config.d.ts +35 -0
  49. package/dist/types/licks/cron.d.ts +16 -0
  50. package/dist/types/licks/extension.d.ts +22 -0
  51. package/dist/types/licks/fswatch.d.ts +20 -0
  52. package/dist/types/licks/glob.d.ts +1 -0
  53. package/dist/types/licks/index.d.ts +24 -0
  54. package/dist/types/licks/lick.d.ts +23 -0
  55. package/dist/types/licks/licks.d.ts +58 -0
  56. package/dist/types/licks/schedules.d.ts +25 -0
  57. package/dist/types/licks/sources.d.ts +38 -0
  58. package/dist/types/licks/state.d.ts +14 -0
  59. package/dist/types/services.d.ts +18 -0
  60. package/dist/types/spectrum/agent-port.d.ts +1 -1
  61. package/dist/types/system.d.ts +1 -0
  62. package/package.json +2 -2
package/dist/system.js CHANGED
@@ -44,9 +44,25 @@ function systemSection(facts) {
44
44
  `Not here yet: ${[...missing, "a browser or CDP tool", "GitHub credentials"].join(", ")}.`
45
45
  ].join("\n");
46
46
  }
47
+ async function kernelBoot(client) {
48
+ const decoder = new TextDecoder();
49
+ let out = "";
50
+ try {
51
+ const process = await client.spawn(["cat", "/proc/stat"], {
52
+ onStdout: (bytes) => {
53
+ out += decoder.decode(bytes, { stream: true });
54
+ }
55
+ });
56
+ await process.exited;
57
+ } catch {
58
+ return null;
59
+ }
60
+ return /^btime (\d+)$/m.exec(out)?.[1] ?? null;
61
+ }
47
62
  export {
48
63
  agentVersion,
49
64
  commandsOnPath,
65
+ kernelBoot,
50
66
  systemSection,
51
67
  transportName
52
68
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/system.ts"],
4
- "sourcesContent": ["import type { KernelClient } from './kernel/client.ts';\nimport type { Transport } from './net.ts';\n\nexport interface SystemFacts {\n version: string;\n commands: string[];\n transport: string;\n}\n\nconst LIST_PATH = 'IFS=:; for d in $PATH; do [ -d \"$d\" ] && ls -1 \"$d\"; done';\nconst RUNTIMES = ['node', 'python3'];\n\nexport async function commandsOnPath(client: KernelClient): Promise<string[]> {\n const decoder = new TextDecoder();\n let out = '';\n try {\n const process = await client.spawn(['bash', '-c', LIST_PATH], {\n onStdout: (bytes) => {\n out += decoder.decode(bytes, { stream: true });\n },\n });\n await process.exited;\n } catch {\n return [];\n }\n const names = out.split('\\n').map((name) => name.trim());\n return [...new Set(names.filter(Boolean))].sort();\n}\n\nexport function transportName(transport: Transport | undefined): string {\n const traits = transport?.traits;\n if (traits?.crossOrigin === 'cors') {\n return \"the page's own fetch, so only servers that allow CORS answer\";\n }\n if (traits?.manualRedirects) return 'a local proxy (slicc-node) that fetches without CORS';\n return 'a relay that fetches without CORS';\n}\n\nexport async function agentVersion(\n fetcher: (url: URL) => Promise<Response>,\n url = new URL('../package.json', import.meta.url)\n): Promise<string> {\n try {\n const response = await fetcher(url);\n const manifest = response.ok ? ((await response.json()) as { version?: string }) : {};\n return manifest.version ?? 'unknown';\n } catch {\n return 'unknown';\n }\n}\n\nexport function systemSection(facts: SystemFacts): string {\n const missing = RUNTIMES.filter((name) => !facts.commands.includes(name));\n return [\n `You are SLICC's agent in seven: slicc-agent ${facts.version}, installed in /opt/agent. Your bash runs on the page's shared slicc-kernel, a WebAssembly sandbox in the user's browser, not on a server or the user's machine.`,\n \"Files: / is the browser's private file system (OPFS). Work in /home and /tmp. /os and /opt are the system; change them only when asked.\",\n `Commands on PATH: ${facts.commands.join(' ') || 'none found'}.`,\n 'Install command-line tools with `pnpm add -g <package>`. There is no ipk.',\n `Network: requests go through ${facts.transport}. localhost and 127.0.0.1 are this sandbox's own loopback, not the user's computer.`,\n `Not here yet: ${[...missing, 'a browser or CDP tool', 'GitHub credentials'].join(', ')}.`,\n ].join('\\n');\n}\n"],
5
- "mappings": "AASA,MAAM,YAAY;AAClB,MAAM,WAAW,CAAC,QAAQ,SAAS;AAEnC,eAAsB,eAAe,QAAyC;AAC5E,QAAM,UAAU,IAAI,YAAY;AAChC,MAAI,MAAM;AACV,MAAI;AACF,UAAM,UAAU,MAAM,OAAO,MAAM,CAAC,QAAQ,MAAM,SAAS,GAAG;AAAA,MAC5D,UAAU,CAAC,UAAU;AACnB,eAAO,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAAA,MAC/C;AAAA,IACF,CAAC;AACD,UAAM,QAAQ;AAAA,EAChB,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACA,QAAM,QAAQ,IAAI,MAAM,IAAI,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC;AACvD,SAAO,CAAC,GAAG,IAAI,IAAI,MAAM,OAAO,OAAO,CAAC,CAAC,EAAE,KAAK;AAClD;AAEO,SAAS,cAAc,WAA0C;AACtE,QAAM,SAAS,WAAW;AAC1B,MAAI,QAAQ,gBAAgB,QAAQ;AAClC,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,gBAAiB,QAAO;AACpC,SAAO;AACT;AAEA,eAAsB,aACpB,SACA,MAAM,IAAI,IAAI,mBAAmB,YAAY,GAAG,GAC/B;AACjB,MAAI;AACF,UAAM,WAAW,MAAM,QAAQ,GAAG;AAClC,UAAM,WAAW,SAAS,KAAO,MAAM,SAAS,KAAK,IAA8B,CAAC;AACpF,WAAO,SAAS,WAAW;AAAA,EAC7B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,cAAc,OAA4B;AACxD,QAAM,UAAU,SAAS,OAAO,CAAC,SAAS,CAAC,MAAM,SAAS,SAAS,IAAI,CAAC;AACxE,SAAO;AAAA,IACL,+CAA+C,MAAM,OAAO;AAAA,IAC5D;AAAA,IACA,qBAAqB,MAAM,SAAS,KAAK,GAAG,KAAK,YAAY;AAAA,IAC7D;AAAA,IACA,gCAAgC,MAAM,SAAS;AAAA,IAC/C,iBAAiB,CAAC,GAAG,SAAS,yBAAyB,oBAAoB,EAAE,KAAK,IAAI,CAAC;AAAA,EACzF,EAAE,KAAK,IAAI;AACb;",
4
+ "sourcesContent": ["import type { KernelClient } from './kernel/client.ts';\nimport type { Transport } from './net.ts';\n\nexport interface SystemFacts {\n version: string;\n commands: string[];\n transport: string;\n}\n\nconst LIST_PATH = 'IFS=:; for d in $PATH; do [ -d \"$d\" ] && ls -1 \"$d\"; done';\nconst RUNTIMES = ['node', 'python3'];\n\nexport async function commandsOnPath(client: KernelClient): Promise<string[]> {\n const decoder = new TextDecoder();\n let out = '';\n try {\n const process = await client.spawn(['bash', '-c', LIST_PATH], {\n onStdout: (bytes) => {\n out += decoder.decode(bytes, { stream: true });\n },\n });\n await process.exited;\n } catch {\n return [];\n }\n const names = out.split('\\n').map((name) => name.trim());\n return [...new Set(names.filter(Boolean))].sort();\n}\n\nexport function transportName(transport: Transport | undefined): string {\n const traits = transport?.traits;\n if (traits?.crossOrigin === 'cors') {\n return \"the page's own fetch, so only servers that allow CORS answer\";\n }\n if (traits?.manualRedirects) return 'a local proxy (slicc-node) that fetches without CORS';\n return 'a relay that fetches without CORS';\n}\n\nexport async function agentVersion(\n fetcher: (url: URL) => Promise<Response>,\n url = new URL('../package.json', import.meta.url)\n): Promise<string> {\n try {\n const response = await fetcher(url);\n const manifest = response.ok ? ((await response.json()) as { version?: string }) : {};\n return manifest.version ?? 'unknown';\n } catch {\n return 'unknown';\n }\n}\n\nexport function systemSection(facts: SystemFacts): string {\n const missing = RUNTIMES.filter((name) => !facts.commands.includes(name));\n return [\n `You are SLICC's agent in seven: slicc-agent ${facts.version}, installed in /opt/agent. Your bash runs on the page's shared slicc-kernel, a WebAssembly sandbox in the user's browser, not on a server or the user's machine.`,\n \"Files: / is the browser's private file system (OPFS). Work in /home and /tmp. /os and /opt are the system; change them only when asked.\",\n `Commands on PATH: ${facts.commands.join(' ') || 'none found'}.`,\n 'Install command-line tools with `pnpm add -g <package>`. There is no ipk.',\n `Network: requests go through ${facts.transport}. localhost and 127.0.0.1 are this sandbox's own loopback, not the user's computer.`,\n `Not here yet: ${[...missing, 'a browser or CDP tool', 'GitHub credentials'].join(', ')}.`,\n ].join('\\n');\n}\n\nexport async function kernelBoot(client: KernelClient): Promise<string | null> {\n const decoder = new TextDecoder();\n let out = '';\n try {\n const process = await client.spawn(['cat', '/proc/stat'], {\n onStdout: (bytes) => {\n out += decoder.decode(bytes, { stream: true });\n },\n });\n await process.exited;\n } catch {\n return null;\n }\n return /^btime (\\d+)$/m.exec(out)?.[1] ?? null;\n}\n"],
5
+ "mappings": "AASA,MAAM,YAAY;AAClB,MAAM,WAAW,CAAC,QAAQ,SAAS;AAEnC,eAAsB,eAAe,QAAyC;AAC5E,QAAM,UAAU,IAAI,YAAY;AAChC,MAAI,MAAM;AACV,MAAI;AACF,UAAM,UAAU,MAAM,OAAO,MAAM,CAAC,QAAQ,MAAM,SAAS,GAAG;AAAA,MAC5D,UAAU,CAAC,UAAU;AACnB,eAAO,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAAA,MAC/C;AAAA,IACF,CAAC;AACD,UAAM,QAAQ;AAAA,EAChB,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACA,QAAM,QAAQ,IAAI,MAAM,IAAI,EAAE,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC;AACvD,SAAO,CAAC,GAAG,IAAI,IAAI,MAAM,OAAO,OAAO,CAAC,CAAC,EAAE,KAAK;AAClD;AAEO,SAAS,cAAc,WAA0C;AACtE,QAAM,SAAS,WAAW;AAC1B,MAAI,QAAQ,gBAAgB,QAAQ;AAClC,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,gBAAiB,QAAO;AACpC,SAAO;AACT;AAEA,eAAsB,aACpB,SACA,MAAM,IAAI,IAAI,mBAAmB,YAAY,GAAG,GAC/B;AACjB,MAAI;AACF,UAAM,WAAW,MAAM,QAAQ,GAAG;AAClC,UAAM,WAAW,SAAS,KAAO,MAAM,SAAS,KAAK,IAA8B,CAAC;AACpF,WAAO,SAAS,WAAW;AAAA,EAC7B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,cAAc,OAA4B;AACxD,QAAM,UAAU,SAAS,OAAO,CAAC,SAAS,CAAC,MAAM,SAAS,SAAS,IAAI,CAAC;AACxE,SAAO;AAAA,IACL,+CAA+C,MAAM,OAAO;AAAA,IAC5D;AAAA,IACA,qBAAqB,MAAM,SAAS,KAAK,GAAG,KAAK,YAAY;AAAA,IAC7D;AAAA,IACA,gCAAgC,MAAM,SAAS;AAAA,IAC/C,iBAAiB,CAAC,GAAG,SAAS,yBAAyB,oBAAoB,EAAE,KAAK,IAAI,CAAC;AAAA,EACzF,EAAE,KAAK,IAAI;AACb;AAEA,eAAsB,WAAW,QAA8C;AAC7E,QAAM,UAAU,IAAI,YAAY;AAChC,MAAI,MAAM;AACV,MAAI;AACF,UAAM,UAAU,MAAM,OAAO,MAAM,CAAC,OAAO,YAAY,GAAG;AAAA,MACxD,UAAU,CAAC,UAAU;AACnB,eAAO,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAAA,MAC/C;AAAA,IACF,CAAC;AACD,UAAM,QAAQ;AAAA,EAChB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO,iBAAiB,KAAK,GAAG,IAAI,CAAC,KAAK;AAC5C;",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,11 @@
1
1
  import { type Conversation, type Harness, type SubmissionId } from '@earendil-works/pi-durable';
2
2
  import type { Cones } from './cone.ts';
3
3
  import type { Deliveries } from './deliveries.ts';
4
+ import type { LickSources, Licks } from './licks/index.ts';
4
5
  import type { AgentControl } from './services.ts';
6
+ export interface HostLicks {
7
+ licks: Licks;
8
+ sources: LickSources;
9
+ }
5
10
  export declare function submissionId(value: string): SubmissionId | undefined;
6
- export declare function createAgentControl(harness: Harness, target: Conversation | Cones, deliveries?: Deliveries): AgentControl;
11
+ export declare function createAgentControl(harness: Harness, target: Conversation | Cones, deliveries?: Deliveries, licks?: HostLicks): AgentControl;
@@ -1,5 +1,6 @@
1
1
  import { type Context } from '@earendil-works/chord';
2
2
  import type { Agent } from './agent.ts';
3
+ import { type HostLicks } from './control.ts';
3
4
  import { AgentSettings } from './services.ts';
4
5
  import type { PortEndpoint } from './wire.ts';
5
6
  export interface AgentHost {
@@ -12,4 +13,5 @@ export declare function hostAgent(agent: Agent, options?: {
12
13
  serverId?: string;
13
14
  context?: Context;
14
15
  settings?: AgentSettings;
16
+ licks?: HostLicks;
15
17
  }): Promise<AgentHost>;
@@ -0,0 +1,10 @@
1
+ export interface Activity {
2
+ wrote(path: string): void;
3
+ began(): () => void;
4
+ agentMade(path: string): boolean;
5
+ }
6
+ export interface ActivityOptions {
7
+ windowMs?: number;
8
+ now?: () => number;
9
+ }
10
+ export declare function createActivity(options?: ActivityOptions): Activity;
@@ -1,11 +1,13 @@
1
1
  import type { Context } from '@earendil-works/chord';
2
2
  import { type BinaryReader, type DirReader, type ExecutionEnv, type ExecutionError, FileError, type FileInfo, type FileWatcher, type Result, type ShellExecOptions, type ShellExecResult, type TextLineReader, type WatchChange, type WatchTarget } from '@earendil-works/pi-durable/env';
3
+ import type { Activity } from './activity.ts';
3
4
  import type { KernelClient } from './client.ts';
4
5
  import { type PollOptions } from './watch.ts';
5
6
  export interface KernelEnvOptions {
6
7
  cwd: string;
7
8
  id?: string;
8
9
  watch?: PollOptions;
10
+ activity?: Activity;
9
11
  }
10
12
  type FileResult<T> = Promise<Result<T, FileError>>;
11
13
  export declare class SliccKernelEnv implements ExecutionEnv {
@@ -0,0 +1,9 @@
1
+ import type { Context } from '@earendil-works/chord';
2
+ import type { Conversation, EntryRecord, Harness } from '@earendil-works/pi-durable';
3
+ import type { Deliver } from './fswatch.ts';
4
+ export interface BootFacts {
5
+ version: string;
6
+ boot: string | null;
7
+ }
8
+ export declare function bashCommands(entries: readonly EntryRecord[], after: number): string[];
9
+ export declare function bootLicks(harness: Harness, cone: Conversation, deliver: Deliver, facts: BootFacts, context: Context): Promise<void>;
@@ -0,0 +1,35 @@
1
+ import type { LickTarget } from './lick.ts';
2
+ export interface CronEntry {
3
+ name: string;
4
+ schedule: string;
5
+ target: LickTarget;
6
+ message: string;
7
+ }
8
+ export interface WatchEntry {
9
+ name: string;
10
+ path: string;
11
+ glob: string;
12
+ target: LickTarget;
13
+ message: string;
14
+ debounce: number;
15
+ }
16
+ export interface WebhookEntry {
17
+ name: string;
18
+ target: LickTarget;
19
+ message: string;
20
+ }
21
+ export interface ConfigError {
22
+ file: string;
23
+ line?: number;
24
+ error: string;
25
+ }
26
+ export interface Parsed<T> {
27
+ entries: T[];
28
+ errors: ConfigError[];
29
+ }
30
+ export declare function isName(value: string): boolean;
31
+ export declare function isTarget(value: string): value is LickTarget;
32
+ export declare function parseCrontab(file: string, text: string): Parsed<CronEntry>;
33
+ export declare function nameOf(file: string): string;
34
+ export declare function parseWatch(file: string, source: string, home: string): WatchEntry;
35
+ export declare function parseWebhook(file: string, source: string): WebhookEntry;
@@ -0,0 +1,16 @@
1
+ export interface Schedule {
2
+ minutes: ReadonlySet<number>;
3
+ hours: ReadonlySet<number>;
4
+ days: ReadonlySet<number>;
5
+ months: ReadonlySet<number>;
6
+ weekdays: ReadonlySet<number>;
7
+ anyDay: boolean;
8
+ anyWeekday: boolean;
9
+ }
10
+ export declare function parseSchedule(expression: string): Schedule;
11
+ export declare function nextFire(schedule: Schedule, after: number): number | null;
12
+ export interface Missed {
13
+ count: number;
14
+ more: boolean;
15
+ }
16
+ export declare function missedFires(schedule: Schedule, from: number, until: number, cap?: number): Missed;
@@ -0,0 +1,22 @@
1
+ import type { Context } from '@earendil-works/chord';
2
+ import { type Conversation, type ConversationId } from '@earendil-works/pi-durable';
3
+ import { type Missed } from './cron.ts';
4
+ import type { LickTarget } from './lick.ts';
5
+ import type { Licks } from './licks.ts';
6
+ export declare const LICKS_SECTION: string;
7
+ export type ConversationLookup = (id: ConversationId, context: Context) => Promise<Conversation>;
8
+ export interface CronInput {
9
+ name: string;
10
+ schedule: string;
11
+ target: LickTarget;
12
+ message: string;
13
+ from: number;
14
+ }
15
+ type CronState = {
16
+ phase: 'wait';
17
+ at: number | null;
18
+ };
19
+ export declare function cronText(schedule: string, at: number, missed: Missed): string;
20
+ export declare function cronTask(licks: Licks): import("@earendil-works/pi-durable").Task<CronInput, CronState, null, object>;
21
+ export declare function licksExtension(licks: Licks, conversation: ConversationLookup, cron?: ReturnType<typeof cronTask>): import("@earendil-works/pi-durable").Extension<import("@earendil-works/pi-durable").ToolRegistration>;
22
+ export {};
@@ -0,0 +1,20 @@
1
+ import type { Context } from '@earendil-works/chord';
2
+ import type { ExecutionEnv } from '@earendil-works/pi-durable/env';
3
+ import type { Activity } from '../kernel/activity.ts';
4
+ import { type ConfigError } from './config.ts';
5
+ import type { LickEvent } from './licks.ts';
6
+ export interface ConfigFiles {
7
+ read(path: string, context: Context): Promise<string | undefined>;
8
+ list(path: string, context: Context): Promise<string[]>;
9
+ }
10
+ export type Deliver = (event: LickEvent, context: Context) => Promise<void>;
11
+ export declare function relativeTo(base: string, path: string): string | undefined;
12
+ export declare function configFiles(env: ExecutionEnv): ConfigFiles;
13
+ export declare class Watches {
14
+ #private;
15
+ constructor(env: ExecutionEnv, deliver: Deliver, home: string, activity?: Activity);
16
+ get names(): string[];
17
+ stop(name: string, context: Context): Promise<void>;
18
+ reconcile(dir: string, context: Context): Promise<ConfigError[]>;
19
+ close(context: Context): Promise<void>;
20
+ }
@@ -0,0 +1 @@
1
+ export declare function glob(pattern: string): (path: string) => boolean;
@@ -0,0 +1,24 @@
1
+ import type { Conversation, Harness, Registry } from '@earendil-works/pi-durable';
2
+ import type { ExecutionEnv } from '@earendil-works/pi-durable/env';
3
+ import type { Activity } from '../kernel/activity.ts';
4
+ import { type Licks } from './licks.ts';
5
+ import { type LickSources } from './sources.ts';
6
+ export interface LicksAgent {
7
+ harness: Harness;
8
+ root: Conversation;
9
+ cone(): Conversation;
10
+ }
11
+ export interface LicksSetup {
12
+ licks: Licks;
13
+ attach(agent: LicksAgent, options: {
14
+ env: ExecutionEnv;
15
+ home: string;
16
+ now?: () => number;
17
+ flushEvery?: number;
18
+ activity?: Activity;
19
+ }): LickSources;
20
+ }
21
+ export declare function setupLicks(registry: Registry): LicksSetup;
22
+ export type { Lick, LickChannel } from './lick.ts';
23
+ export type { LickEvent, Licks } from './licks.ts';
24
+ export type { LickSources, WebhookDelivery } from './sources.ts';
@@ -0,0 +1,23 @@
1
+ export declare const LICK_CHANNELS: readonly ['webhook', 'cron', 'sprinkle', 'fswatch', 'session-reload', 'navigate', 'discovery', 'upgrade', 'workflow', 'bash', 'jshd', 'preview', 'cherry', 'scoop-notify', 'scoop-idle', 'scoop-wait', 'sudo-request'];
2
+ export type LickChannel = (typeof LICK_CHANNELS)[number];
3
+ export type LickAction = 'confirm' | 'dismiss';
4
+ export type LickSeverity = 'warn' | 'error';
5
+ export type LickTarget = 'cone' | `scoop:${string}`;
6
+ export type Lick = {
7
+ id: string;
8
+ channel: LickChannel;
9
+ source: string;
10
+ title: string;
11
+ text: string;
12
+ body?: string;
13
+ count: number;
14
+ at: number;
15
+ actions?: LickAction[];
16
+ severity?: LickSeverity;
17
+ };
18
+ export declare function escapeText(value: string): string;
19
+ export declare function escapeAttribute(value: string): string;
20
+ export declare function unescape(value: string): string;
21
+ export declare function lickId(): string;
22
+ export declare function formatLick(lick: Lick): string;
23
+ export declare function parseLick(text: string): Lick | undefined;
@@ -0,0 +1,58 @@
1
+ import type { Context } from '@earendil-works/chord';
2
+ import { type Conversation, type Harness } from '@earendil-works/pi-durable';
3
+ import { type Lick, type LickAction, type LickChannel, type LickSeverity, type LickTarget } from './lick.ts';
4
+ import { LICK_STATE_KIND, type LickDecision } from './state.ts';
5
+ export interface LickEvent {
6
+ channel: LickChannel;
7
+ source: string;
8
+ title: string;
9
+ text: string;
10
+ body?: string;
11
+ items?: string[];
12
+ count?: number;
13
+ target: LickTarget;
14
+ coalesce?: boolean;
15
+ severity?: LickSeverity;
16
+ eventId?: string;
17
+ at?: number;
18
+ }
19
+ type Pending = {
20
+ lick: Lick;
21
+ items: string[];
22
+ body: string | null;
23
+ };
24
+ type Queue = {
25
+ submission: number | null;
26
+ pending: Pending | null;
27
+ };
28
+ type OutboxState = {
29
+ queues: Record<string, Queue>;
30
+ events: Record<string, string[]>;
31
+ };
32
+ export declare const LicksOutbox: import("@earendil-works/pi-durable").SessionDocToken<OutboxState>;
33
+ export interface LickHandler {
34
+ confirm?(lick: Lick, reason: string | undefined, context: Context): Promise<string | undefined>;
35
+ dismiss?(lick: Lick, reason: string | undefined, context: Context): Promise<string | undefined>;
36
+ }
37
+ export interface LickFound {
38
+ lick: Lick;
39
+ decision: LickDecision | undefined;
40
+ }
41
+ export interface Licks {
42
+ deliver(event: LickEvent, context: Context): Promise<void>;
43
+ flush(context: Context): Promise<void>;
44
+ find(conversation: Conversation, id: string, context: Context): Promise<LickFound | undefined>;
45
+ decide(conversation: Conversation, id: string, action: LickAction, reason: string | undefined, context: Context): Promise<string>;
46
+ handle(channel: LickChannel, handler: LickHandler): void;
47
+ pending(context: Context): Promise<number>;
48
+ }
49
+ export interface LicksHost {
50
+ harness: Harness;
51
+ resolve(target: LickTarget, context: Context): Promise<Conversation>;
52
+ }
53
+ export declare const MAX_ITEMS = 50;
54
+ export declare const MAX_EVENTS = 64;
55
+ export declare function render(pending: Pending): Lick;
56
+ export declare function strongest(a: LickSeverity | undefined, b: LickSeverity | undefined): LickSeverity | undefined;
57
+ export declare function createLicks(host: Promise<LicksHost>): Licks;
58
+ export { LICK_STATE_KIND };
@@ -0,0 +1,25 @@
1
+ import type { Context } from '@earendil-works/chord';
2
+ import { type Conversation, type Harness } from '@earendil-works/pi-durable';
3
+ import type { CronEntry } from './config.ts';
4
+ import type { cronTask } from './extension.ts';
5
+ type CronRecord = {
6
+ schedule: string;
7
+ target: string;
8
+ message: string;
9
+ task: number;
10
+ };
11
+ type HostState = {
12
+ cron: Record<string, CronRecord>;
13
+ agent: string | null;
14
+ boot: string | null;
15
+ mark: number;
16
+ upgrades: number;
17
+ };
18
+ export declare const LicksHostDoc: import("@earendil-works/pi-durable").SessionDocToken<HostState>;
19
+ export declare function reconcileCron(options: {
20
+ harness: Harness;
21
+ root: Conversation;
22
+ cron: ReturnType<typeof cronTask>;
23
+ now: () => number;
24
+ }, entries: readonly CronEntry[], context: Context): Promise<void>;
25
+ export {};
@@ -0,0 +1,38 @@
1
+ import type { Context } from '@earendil-works/chord';
2
+ import type { Conversation, Harness } from '@earendil-works/pi-durable';
3
+ import type { ExecutionEnv } from '@earendil-works/pi-durable/env';
4
+ import type { Activity } from '../kernel/activity.ts';
5
+ import { type BootFacts } from './boot.ts';
6
+ import { type ConfigError, type WebhookEntry } from './config.ts';
7
+ import type { cronTask } from './extension.ts';
8
+ import type { LickEvent, Licks } from './licks.ts';
9
+ export declare const SLICC_DIR = ".slicc";
10
+ export interface LickSourcesOptions {
11
+ harness: Harness;
12
+ root: Conversation;
13
+ cone: () => Conversation;
14
+ env: ExecutionEnv;
15
+ home: string;
16
+ licks: Licks;
17
+ cron: ReturnType<typeof cronTask>;
18
+ now?: () => number;
19
+ flushEvery?: number;
20
+ activity?: Activity;
21
+ }
22
+ export interface WebhookDelivery {
23
+ id?: string;
24
+ headers?: Record<string, string>;
25
+ body: unknown;
26
+ }
27
+ export interface LickSources {
28
+ start(context: Context): Promise<void>;
29
+ reconcile(context: Context): Promise<void>;
30
+ boot(facts: BootFacts, context: Context): Promise<void>;
31
+ webhook(name: string, delivery: WebhookDelivery, context: Context): Promise<boolean>;
32
+ close(context: Context): Promise<void>;
33
+ }
34
+ export declare function hash(text: string): string;
35
+ export declare function shortPath(path: string, home: string): string;
36
+ export declare function configLick(error: ConfigError, home: string): LickEvent;
37
+ export declare function webhookLick(name: string, entry: WebhookEntry, delivery: WebhookDelivery): LickEvent;
38
+ export declare function createLickSources(options: LickSourcesOptions): LickSources;
@@ -0,0 +1,14 @@
1
+ import { type Lick } from './lick.ts';
2
+ export type LickDecision = 'confirmed' | 'dismissed';
3
+ export declare const LICK_TOOLS: Record<string, LickDecision>;
4
+ export declare const LICK_STATE_KIND = "slicc.lick-state";
5
+ interface EntryLike {
6
+ id: number;
7
+ kind: string;
8
+ model?: readonly unknown[];
9
+ data?: unknown;
10
+ }
11
+ export declare function userText(entry: EntryLike): string | undefined;
12
+ export declare function entryLick(entry: EntryLike): Lick | undefined;
13
+ export declare function lickDecisions(entries: readonly EntryLike[]): Map<string, LickDecision>;
14
+ export {};
@@ -39,6 +39,24 @@ export interface AgentControl {
39
39
  reset(handoff: string | null, context: Context): Promise<void>;
40
40
  configure(change: AgentSettingsChange, context: Context): Promise<void>;
41
41
  rewind(messageId: string | null, context: Context): Promise<Rewound>;
42
+ resolveLick(lickId: string, state: 'confirmed' | 'dismissed', context: Context): Promise<LickResolved>;
43
+ webhook(name: string, delivery: WebhookRequest, context: Context): Promise<{
44
+ delivered: boolean;
45
+ }>;
46
+ }
47
+ export type LickResolved = {
48
+ done: true;
49
+ text: string;
50
+ error: null;
51
+ } | {
52
+ done: false;
53
+ text: null;
54
+ error: string;
55
+ };
56
+ export interface WebhookRequest {
57
+ id: string | null;
58
+ headers: Record<string, string>;
59
+ body: string;
42
60
  }
43
61
  export type Rewound = {
44
62
  done: true;
@@ -18,7 +18,7 @@ export declare class AgentAdapter extends Emitter<AgentEvents> implements AgentP
18
18
  unqueue(_agentId: string, messageId: string): void;
19
19
  suggestion(): string | null;
20
20
  answer(): void;
21
- resolveLick(): void;
21
+ resolveLick(_agentId: string, messageId: string, state: 'confirmed' | 'dismissed'): void;
22
22
  compact(): void;
23
23
  clear(): void;
24
24
  setModel(_agentId: string, model: string): void;
@@ -9,3 +9,4 @@ export declare function commandsOnPath(client: KernelClient): Promise<string[]>;
9
9
  export declare function transportName(transport: Transport | undefined): string;
10
10
  export declare function agentVersion(fetcher: (url: URL) => Promise<Response>, url?: URL): Promise<string>;
11
11
  export declare function systemSection(facts: SystemFacts): string;
12
+ export declare function kernelBoot(client: KernelClient): Promise<string | null>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-agent",
3
- "version": "3.3.0",
3
+ "version": "3.5.0",
4
4
  "description": "SLICC's agent core on pi durable: an agent worker, session storage, remoting and pi extensions",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -74,7 +74,7 @@
74
74
  "typescript": "7.0.2"
75
75
  },
76
76
  "dependencies": {
77
- "@ai-ecoverse/slicc-kernel": "1.14.0",
77
+ "@ai-ecoverse/slicc-kernel": "1.16.0",
78
78
  "@earendil-works/chord": "1.0.4",
79
79
  "@earendil-works/pi-ai": "1.0.4",
80
80
  "@earendil-works/pi-client": "1.0.4",