@ai-ecoverse/slicc-agent 5.5.1 → 5.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -36,6 +36,7 @@ await agent.prompt('What is in /home?');
36
36
  - **What it can't:** every other `AgentControl` method, settings action and remote-control `prompt` or `abort` is refused with `EACCES` ("this follower can watch, but not send, steer or abort"). Send and compact answer `accepted: false` with that code. The `view` seat's variants don't have those methods at all (`view` for control and settings, `watch` for remote control, `none` for Panels); the follower's client answers the missing ones with the refusals ([`src/readonly.ts`](src/readonly.ts)), so the worker never runs them. A `watch` stream is readable only over the link that opened it.
37
37
  - **Carrying the connection over a byte stream:** `linkEndpoint({ write, close })` is a `PortEndpoint` that `connectAgent` accepts. Feed it the stream's bytes with `receive(bytes)` and its end with `end(error?)`. On the owner's side, `bridgePort(port, { write, close })` does the same for the `MessagePort` from `port()`.
38
38
  - **The bytes:** the owner first posts a hello, its server id, then each Chord frame travels as its JSON text in UTF-8 (`PortLink` in [`src/wire.ts`](src/wire.ts)), and `{ close: true }` ends the port. On a byte stream each message becomes a frame: a `u32` big-endian length, then a tag (0 data, 1 hello, 2 close), then the payload. A frame is at most 16 MiB, and a malformed one ends the link.
39
+ - **Another agent version:** when Chord refuses the handshake because the owner's contracts don't match (`upgrade_required` or `contract_mismatch`), `connectAgent` and `connectGuest` close the link and reject once with `code: 'EVERSION'` and the sentence "That SLICC runs another version of the agent, so this tab can't follow it. Update both SLICCs, then reload this page." (Chord's error is its `cause`). It is final: a follower should show the sentence and not retry.
39
40
 
40
41
  ### Bash and files on slicc-kernel
41
42
 
package/dist/client.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import {
2
- createHost
2
+ createHost,
3
+ isChordError
3
4
  } from "@earendil-works/chord";
4
5
  import { BACKGROUND_CONTEXT } from "@earendil-works/chord/context";
5
6
  import { controlAnswer, pageAnswer, remoteAnswer, settingsAnswer } from "./readonly.js";
@@ -50,6 +51,9 @@ function lastingSettings(settings, hold) {
50
51
  dismissMoved: (context) => settings.dismissMoved(context)
51
52
  };
52
53
  }
54
+ const VERSIONS = /* @__PURE__ */ new Set(["upgrade_required", "contract_mismatch"]);
55
+ const OTHER_VERSION = "That SLICC runs another version of the agent, so this tab can't follow it. Update both SLICCs, then reload this page.";
56
+ const final = (error) => isChordError(error) && VERSIONS.has(error.code) ? Object.assign(new Error(OTHER_VERSION, { cause: error }), { code: "EVERSION" }) : error;
53
57
  const ended = (link) => link.ended.then((reason) => {
54
58
  throw reason ?? new Error("the agent closed the link");
55
59
  });
@@ -76,7 +80,7 @@ async function connectAgent(endpoint, options = {}) {
76
80
  } catch (error) {
77
81
  release();
78
82
  link.close("handshake failed");
79
- throw error;
83
+ throw final(error);
80
84
  }
81
85
  const root = (run, rootOptions) => host.root(run, rootOptions);
82
86
  const control = bound(controlHandle, root, CONTROL_METHODS, controlAnswer);
@@ -161,7 +165,9 @@ async function connectAgent(endpoint, options = {}) {
161
165
  };
162
166
  }
163
167
  export {
168
+ OTHER_VERSION,
164
169
  connectAgent,
170
+ final,
165
171
  lasting
166
172
  };
167
173
  //# sourceMappingURL=client.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/client.ts"],
4
- "sourcesContent": ["import {\n type Context,\n createHost,\n type Readable,\n type ReplicatedState,\n} from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport type { ApprovalPolicy } from './approvals/policy.ts';\nimport type { Approval } from './approvals/record.ts';\nimport type { ChangesView } from './changes/git.ts';\nimport type { FrozenCone } from './freezer/index.ts';\nimport type { GuestSeat } from './guests/seats.ts';\nimport type { MemoryEntry, MemoryScope } from './memory/format.ts';\nimport { controlAnswer, pageAnswer, remoteAnswer, settingsAnswer } from './readonly.ts';\nimport type { AgentRemote } from './remote/host.ts';\nimport type { AgentPages } from './remote/pages.ts';\nimport type { VaultState } from './secrets/vault.ts';\nimport {\n AgentControl,\n AgentPagesService,\n AgentRemoteService,\n AgentSettings,\n type AgentsSummary,\n AgentTranscript,\n CONTROL_METHODS,\n type Command,\n type Delivered,\n type GuestProposal,\n type GuestShown,\n type MonitorFeed,\n PAGE_METHODS,\n type PrivateRevision,\n REMOTE_METHODS,\n SETTINGS_METHODS,\n type SendMode,\n type SettingsState,\n TRANSCRIPT_STATES,\n} from './services.ts';\nimport { AGENT_HOST, bound, kept, type Remote, type Root, replicated } from './serving.ts';\nimport type { Sprinkle } from './sprinkles/kind.ts';\nimport { type PortEndpoint, PortLink } from './wire.ts';\n\nexport interface AgentConnection {\n readonly serverId: string;\n readonly control: AgentControl;\n readonly transcript: ReplicatedState<ConversationView>;\n readonly deliveries: ReplicatedState<Record<string, Delivered>>;\n readonly agents?: ReplicatedState<AgentsSummary>;\n readonly views?: ReplicatedState<Record<string, ConversationView>>;\n readonly commands?: ReplicatedState<Command[]>;\n readonly privateRevision?: ReplicatedState<PrivateRevision>;\n readonly sprinkles?: ReplicatedState<Sprinkle[]>;\n readonly memories?: ReplicatedState<MemoryEntry[]>;\n readonly memoryScopes?: ReplicatedState<MemoryScope[]>;\n readonly frozen?: ReplicatedState<FrozenCone[]>;\n readonly changes?: ReplicatedState<ChangesView>;\n readonly monitor?: ReplicatedState<MonitorFeed>;\n readonly vault?: ReplicatedState<VaultState>;\n readonly labels?: ReplicatedState<Record<string, string>>;\n readonly guests?: ReplicatedState<GuestSeat[]>;\n readonly viewOnly?: boolean;\n readonly guest?: GuestShown;\n readonly proposals?: ReplicatedState<GuestProposal[]>;\n readonly owner?: boolean;\n readonly approvals?: ReplicatedState<Approval[]>;\n readonly approvalPolicy?: ReplicatedState<ApprovalPolicy>;\n readonly settings: AgentSettings;\n readonly remote?: AgentRemote;\n readonly pages?: AgentPages;\n readonly closed: Promise<Error | undefined>;\n readonly readOnly?: boolean;\n prompt(text: string, whenBusy?: SendMode): Promise<string>;\n webhook(\n name: string,\n delivery: { id?: string | null; headers?: Record<string, string>; body: string }\n ): Promise<{ delivered: boolean }>;\n close(): Promise<void>;\n}\n\nexport interface Lasting<T> {\n readonly state: ReplicatedState<T>;\n freeze(): void;\n}\n\nexport function lasting<T>(state: ReplicatedState<T>): Lasting<T> {\n let last: T | undefined;\n let frozen = false;\n const read = () => {\n if (frozen) return last;\n try {\n last = state.value;\n } catch {}\n return last;\n };\n return {\n state: {\n get value() {\n return read();\n },\n subscribe: (listener) => (frozen ? () => undefined : state.subscribe(listener)),\n },\n freeze() {\n read();\n frozen = true;\n },\n };\n}\n\nfunction lastingSettings(\n settings: AgentSettings,\n hold: <T>(state: ReplicatedState<T>) => ReplicatedState<T>\n): AgentSettings {\n return {\n state: hold(settings.state),\n connect: (providerId, secret, fields, context) =>\n settings.connect(providerId, secret, fields, context),\n disconnect: (providerId, context) => settings.disconnect(providerId, context),\n signIn: (providerId, context) => settings.signIn(providerId, context),\n dismissMoved: (context) => settings.dismissMoved(context),\n };\n}\n\nconst ended = (link: PortLink): Promise<never> =>\n link.ended.then((reason) => {\n throw reason ?? new Error('the agent closed the link');\n });\n\nexport async function connectAgent(\n endpoint: PortEndpoint,\n options: { context?: Context } = {}\n): Promise<AgentConnection> {\n const context = options.context ?? BACKGROUND_CONTEXT;\n const link = new PortLink(endpoint);\n const serverId = await link.hello;\n const host = createHost({ name: `follower:${crypto.randomUUID()}`, kind: 'client' });\n host.connect(link);\n const get = (contract: Parameters<typeof host.get>[0]) =>\n host.get(contract, {}, { at: AGENT_HOST }) as unknown as Remote;\n const handles = [\n AgentControl,\n AgentTranscript,\n AgentSettings,\n AgentRemoteService,\n AgentPagesService,\n ].map(get);\n const [controlHandle, transcriptHandle, settingsHandle, remoteHandle, pagesHandle] = handles as [\n Remote,\n Remote,\n Remote,\n Remote,\n Remote,\n ];\n const release = () => {\n for (const handle of handles) handle.release();\n };\n try {\n await Promise.race([Promise.all(handles.map((handle) => handle.ready)), ended(link)]);\n } catch (error) {\n release();\n link.close('handshake failed');\n throw error;\n }\n const root: Root = (run, rootOptions) => host.root(run, rootOptions);\n const control = bound<AgentControl>(controlHandle, root, CONTROL_METHODS, controlAnswer);\n const members = Object.fromEntries(\n TRANSCRIPT_STATES.map((name) => [name, transcriptHandle[name] as Readable<unknown>])\n ) as Record<(typeof TRANSCRIPT_STATES)[number], Readable<unknown>>;\n const settingsState = settingsHandle.state as Readable<unknown>;\n const keeping = [...Object.values(members), settingsState].map(kept);\n await Promise.race([Promise.all(keeping.map((keep) => keep.ready)), ended(link)]);\n const settings: AgentSettings = {\n ...bound<Omit<AgentSettings, 'state'>>(settingsHandle, root, SETTINGS_METHODS, settingsAnswer),\n state: replicated(settingsState as Readable<SettingsState | undefined>),\n };\n const remote = bound<AgentRemote>(remoteHandle, root, REMOTE_METHODS, remoteAnswer);\n const pages = bound<AgentPages>(pagesHandle, root, PAGE_METHODS, pageAnswer);\n const state = <K extends keyof typeof members>(name: K) =>\n replicated(members[name] as Readable<never>);\n const held: Lasting<unknown>[] = [];\n const hold = <T>(state: ReplicatedState<T>): ReplicatedState<T> => {\n const kept = lasting(state);\n held.push(kept as Lasting<unknown>);\n return kept.state;\n };\n let open = true;\n const finish = () => {\n if (!open) return;\n open = false;\n for (const kept of held) kept.freeze();\n for (const keep of keeping) keep.stop();\n release();\n };\n const closed = link.ended.then((reason) => {\n finish();\n return reason;\n });\n return {\n serverId,\n readOnly: controlHandle.kind === 'view',\n control,\n transcript: hold<ConversationView>(state('state')),\n deliveries: hold<Record<string, Delivered>>(state('deliveries')),\n agents: hold<AgentsSummary>(state('agents')),\n views: hold<Record<string, ConversationView>>(state('views')),\n commands: hold<Command[]>(state('commands')),\n privateRevision: hold<PrivateRevision>(state('privateRevision')),\n sprinkles: hold<Sprinkle[]>(state('sprinkles')),\n memories: hold<MemoryEntry[]>(state('memories')),\n memoryScopes: hold<MemoryScope[]>(state('memoryScopes')),\n frozen: hold<FrozenCone[]>(state('frozen')),\n changes: hold<ChangesView>(state('changes')),\n monitor: hold<MonitorFeed>(state('monitor')),\n vault: hold<VaultState>(state('vault')),\n labels: hold<Record<string, string>>(state('labels')),\n guests: hold<GuestSeat[]>(state('guests')),\n viewOnly: controlHandle.kind === 'view',\n owner: pagesHandle.kind === 'pages',\n approvals: hold<Approval[]>(state('approvals')),\n approvalPolicy: hold<ApprovalPolicy>(state('approvalPolicy')),\n settings: lastingSettings(settings, hold),\n remote,\n pages,\n closed,\n webhook(name, delivery) {\n return control.webhook(\n name,\n { id: delivery.id ?? null, headers: delivery.headers ?? {}, body: delivery.body },\n context\n );\n },\n async prompt(text, whenBusy = 'followUp') {\n const sent = await control.send({ text, whenBusy, requestId: null }, context);\n if (!sent.accepted) throw new Error(`${sent.error.code}: ${sent.error.message}`);\n const settled = await control.wait(sent.submissionId, context);\n if (settled.status !== 'done') throw new Error(`unanswered: ${settled.reason}`);\n return settled.text;\n },\n async close() {\n finish();\n link.close('closed');\n await closed;\n await host.close();\n },\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import {\n type Context,\n createHost,\n isChordError,\n type Readable,\n type ReplicatedState,\n} from '@earendil-works/chord';\nimport { BACKGROUND_CONTEXT } from '@earendil-works/chord/context';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport type { ApprovalPolicy } from './approvals/policy.ts';\nimport type { Approval } from './approvals/record.ts';\nimport type { ChangesView } from './changes/git.ts';\nimport type { FrozenCone } from './freezer/index.ts';\nimport type { GuestSeat } from './guests/seats.ts';\nimport type { MemoryEntry, MemoryScope } from './memory/format.ts';\nimport { controlAnswer, pageAnswer, remoteAnswer, settingsAnswer } from './readonly.ts';\nimport type { AgentRemote } from './remote/host.ts';\nimport type { AgentPages } from './remote/pages.ts';\nimport type { VaultState } from './secrets/vault.ts';\nimport {\n AgentControl,\n AgentPagesService,\n AgentRemoteService,\n AgentSettings,\n type AgentsSummary,\n AgentTranscript,\n CONTROL_METHODS,\n type Command,\n type Delivered,\n type GuestProposal,\n type GuestShown,\n type MonitorFeed,\n PAGE_METHODS,\n type PrivateRevision,\n REMOTE_METHODS,\n SETTINGS_METHODS,\n type SendMode,\n type SettingsState,\n TRANSCRIPT_STATES,\n} from './services.ts';\nimport { AGENT_HOST, bound, kept, type Remote, type Root, replicated } from './serving.ts';\nimport type { Sprinkle } from './sprinkles/kind.ts';\nimport { type PortEndpoint, PortLink } from './wire.ts';\n\nexport interface AgentConnection {\n readonly serverId: string;\n readonly control: AgentControl;\n readonly transcript: ReplicatedState<ConversationView>;\n readonly deliveries: ReplicatedState<Record<string, Delivered>>;\n readonly agents?: ReplicatedState<AgentsSummary>;\n readonly views?: ReplicatedState<Record<string, ConversationView>>;\n readonly commands?: ReplicatedState<Command[]>;\n readonly privateRevision?: ReplicatedState<PrivateRevision>;\n readonly sprinkles?: ReplicatedState<Sprinkle[]>;\n readonly memories?: ReplicatedState<MemoryEntry[]>;\n readonly memoryScopes?: ReplicatedState<MemoryScope[]>;\n readonly frozen?: ReplicatedState<FrozenCone[]>;\n readonly changes?: ReplicatedState<ChangesView>;\n readonly monitor?: ReplicatedState<MonitorFeed>;\n readonly vault?: ReplicatedState<VaultState>;\n readonly labels?: ReplicatedState<Record<string, string>>;\n readonly guests?: ReplicatedState<GuestSeat[]>;\n readonly viewOnly?: boolean;\n readonly guest?: GuestShown;\n readonly proposals?: ReplicatedState<GuestProposal[]>;\n readonly owner?: boolean;\n readonly approvals?: ReplicatedState<Approval[]>;\n readonly approvalPolicy?: ReplicatedState<ApprovalPolicy>;\n readonly settings: AgentSettings;\n readonly remote?: AgentRemote;\n readonly pages?: AgentPages;\n readonly closed: Promise<Error | undefined>;\n readonly readOnly?: boolean;\n prompt(text: string, whenBusy?: SendMode): Promise<string>;\n webhook(\n name: string,\n delivery: { id?: string | null; headers?: Record<string, string>; body: string }\n ): Promise<{ delivered: boolean }>;\n close(): Promise<void>;\n}\n\nexport interface Lasting<T> {\n readonly state: ReplicatedState<T>;\n freeze(): void;\n}\n\nexport function lasting<T>(state: ReplicatedState<T>): Lasting<T> {\n let last: T | undefined;\n let frozen = false;\n const read = () => {\n if (frozen) return last;\n try {\n last = state.value;\n } catch {}\n return last;\n };\n return {\n state: {\n get value() {\n return read();\n },\n subscribe: (listener) => (frozen ? () => undefined : state.subscribe(listener)),\n },\n freeze() {\n read();\n frozen = true;\n },\n };\n}\n\nfunction lastingSettings(\n settings: AgentSettings,\n hold: <T>(state: ReplicatedState<T>) => ReplicatedState<T>\n): AgentSettings {\n return {\n state: hold(settings.state),\n connect: (providerId, secret, fields, context) =>\n settings.connect(providerId, secret, fields, context),\n disconnect: (providerId, context) => settings.disconnect(providerId, context),\n signIn: (providerId, context) => settings.signIn(providerId, context),\n dismissMoved: (context) => settings.dismissMoved(context),\n };\n}\n\nconst VERSIONS = new Set(['upgrade_required', 'contract_mismatch']);\n\nexport const OTHER_VERSION =\n \"That SLICC runs another version of the agent, so this tab can't follow it. Update both SLICCs, then reload this page.\";\n\nexport const final = (error: unknown): unknown =>\n isChordError(error) && VERSIONS.has(error.code)\n ? Object.assign(new Error(OTHER_VERSION, { cause: error }), { code: 'EVERSION' })\n : error;\n\nconst ended = (link: PortLink): Promise<never> =>\n link.ended.then((reason) => {\n throw reason ?? new Error('the agent closed the link');\n });\n\nexport async function connectAgent(\n endpoint: PortEndpoint,\n options: { context?: Context } = {}\n): Promise<AgentConnection> {\n const context = options.context ?? BACKGROUND_CONTEXT;\n const link = new PortLink(endpoint);\n const serverId = await link.hello;\n const host = createHost({ name: `follower:${crypto.randomUUID()}`, kind: 'client' });\n host.connect(link);\n const get = (contract: Parameters<typeof host.get>[0]) =>\n host.get(contract, {}, { at: AGENT_HOST }) as unknown as Remote;\n const handles = [\n AgentControl,\n AgentTranscript,\n AgentSettings,\n AgentRemoteService,\n AgentPagesService,\n ].map(get);\n const [controlHandle, transcriptHandle, settingsHandle, remoteHandle, pagesHandle] = handles as [\n Remote,\n Remote,\n Remote,\n Remote,\n Remote,\n ];\n const release = () => {\n for (const handle of handles) handle.release();\n };\n try {\n await Promise.race([Promise.all(handles.map((handle) => handle.ready)), ended(link)]);\n } catch (error) {\n release();\n link.close('handshake failed');\n throw final(error);\n }\n const root: Root = (run, rootOptions) => host.root(run, rootOptions);\n const control = bound<AgentControl>(controlHandle, root, CONTROL_METHODS, controlAnswer);\n const members = Object.fromEntries(\n TRANSCRIPT_STATES.map((name) => [name, transcriptHandle[name] as Readable<unknown>])\n ) as Record<(typeof TRANSCRIPT_STATES)[number], Readable<unknown>>;\n const settingsState = settingsHandle.state as Readable<unknown>;\n const keeping = [...Object.values(members), settingsState].map(kept);\n await Promise.race([Promise.all(keeping.map((keep) => keep.ready)), ended(link)]);\n const settings: AgentSettings = {\n ...bound<Omit<AgentSettings, 'state'>>(settingsHandle, root, SETTINGS_METHODS, settingsAnswer),\n state: replicated(settingsState as Readable<SettingsState | undefined>),\n };\n const remote = bound<AgentRemote>(remoteHandle, root, REMOTE_METHODS, remoteAnswer);\n const pages = bound<AgentPages>(pagesHandle, root, PAGE_METHODS, pageAnswer);\n const state = <K extends keyof typeof members>(name: K) =>\n replicated(members[name] as Readable<never>);\n const held: Lasting<unknown>[] = [];\n const hold = <T>(state: ReplicatedState<T>): ReplicatedState<T> => {\n const kept = lasting(state);\n held.push(kept as Lasting<unknown>);\n return kept.state;\n };\n let open = true;\n const finish = () => {\n if (!open) return;\n open = false;\n for (const kept of held) kept.freeze();\n for (const keep of keeping) keep.stop();\n release();\n };\n const closed = link.ended.then((reason) => {\n finish();\n return reason;\n });\n return {\n serverId,\n readOnly: controlHandle.kind === 'view',\n control,\n transcript: hold<ConversationView>(state('state')),\n deliveries: hold<Record<string, Delivered>>(state('deliveries')),\n agents: hold<AgentsSummary>(state('agents')),\n views: hold<Record<string, ConversationView>>(state('views')),\n commands: hold<Command[]>(state('commands')),\n privateRevision: hold<PrivateRevision>(state('privateRevision')),\n sprinkles: hold<Sprinkle[]>(state('sprinkles')),\n memories: hold<MemoryEntry[]>(state('memories')),\n memoryScopes: hold<MemoryScope[]>(state('memoryScopes')),\n frozen: hold<FrozenCone[]>(state('frozen')),\n changes: hold<ChangesView>(state('changes')),\n monitor: hold<MonitorFeed>(state('monitor')),\n vault: hold<VaultState>(state('vault')),\n labels: hold<Record<string, string>>(state('labels')),\n guests: hold<GuestSeat[]>(state('guests')),\n viewOnly: controlHandle.kind === 'view',\n owner: pagesHandle.kind === 'pages',\n approvals: hold<Approval[]>(state('approvals')),\n approvalPolicy: hold<ApprovalPolicy>(state('approvalPolicy')),\n settings: lastingSettings(settings, hold),\n remote,\n pages,\n closed,\n webhook(name, delivery) {\n return control.webhook(\n name,\n { id: delivery.id ?? null, headers: delivery.headers ?? {}, body: delivery.body },\n context\n );\n },\n async prompt(text, whenBusy = 'followUp') {\n const sent = await control.send({ text, whenBusy, requestId: null }, context);\n if (!sent.accepted) throw new Error(`${sent.error.code}: ${sent.error.message}`);\n const settled = await control.wait(sent.submissionId, context);\n if (settled.status !== 'done') throw new Error(`unanswered: ${settled.reason}`);\n return settled.text;\n },\n async close() {\n finish();\n link.close('closed');\n await closed;\n await host.close();\n },\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["state", "kept"]
7
7
  }
@@ -1,5 +1,5 @@
1
1
  import { createHost, isChordError, replicatedState } from "@earendil-works/chord";
2
- import { lasting } from "../client.js";
2
+ import { final, lasting } from "../client.js";
3
3
  import {
4
4
  AgentGuestService,
5
5
  CONTROL_METHODS
@@ -42,7 +42,9 @@ async function connectGuest(endpoint) {
42
42
  })
43
43
  ]);
44
44
  } catch (error) {
45
- return fail(isChordError(error) && error.code === "forbidden" ? refusal(SEAT_REFUSED) : error);
45
+ return fail(
46
+ isChordError(error) && error.code === "forbidden" ? refusal(SEAT_REFUSED) : final(error)
47
+ );
46
48
  }
47
49
  if (handle.kind !== "guest") return fail(refusal(SEAT_REFUSED));
48
50
  const transcriptMember = handle.transcript;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/guests/client.ts"],
4
- "sourcesContent": ["import { createHost, isChordError, type Readable, replicatedState } from '@earendil-works/chord';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport { type AgentConnection, lasting } from '../client.ts';\nimport {\n type AgentControl,\n AgentGuestService,\n type AgentSettings,\n CONTROL_METHODS,\n type GUEST_METHODS,\n type GuestProposal,\n type GuestShown,\n type SendResponse,\n} from '../services.ts';\nimport { AGENT_HOST, kept, plain, type Remote, replicated } from '../serving.ts';\nimport { type PortEndpoint, PortLink } from '../wire.ts';\nimport type { GuestOpened, Proposed, ProposeRequest } from './index.ts';\n\nexport const GUEST_ONLY = 'a guest can only watch and send messages';\nexport const SEAT_REFUSED = \"This guest link isn't valid any more.\";\n\nconst SAID: Record<string, string> = {\n revoked: 'This guest seat was revoked.',\n empty: 'There is nothing to send.',\n 'too-long': 'That message is too long for a guest seat.',\n gone: 'The cone this seat shares is gone.',\n unavailable: \"Messages from guests need a review, and this agent can't review them yet.\",\n};\n\nexport const proposedSentence = (reason: string): string =>\n SAID[reason] ?? 'The message was not delivered.';\n\nconst refusal = (message: string, code = 'EACCES') => Object.assign(new Error(message), { code });\n\nexport interface GuestConnection extends AgentConnection {\n readonly guest: GuestShown;\n propose(request: ProposeRequest): Promise<Proposed>;\n}\n\nexport async function connectGuest(endpoint: PortEndpoint): Promise<GuestConnection> {\n const link = new PortLink(endpoint);\n let serverId: string;\n try {\n serverId = await link.hello;\n } catch {\n throw refusal(SEAT_REFUSED);\n }\n const host = createHost({ name: `guest:${crypto.randomUUID()}`, kind: 'client' });\n host.connect(link);\n const handle = host.get(AgentGuestService, {}, { at: AGENT_HOST }) as unknown as Remote;\n const fail = async (error: unknown): Promise<never> => {\n handle.release();\n link.close('handshake failed');\n await host.close();\n throw error;\n };\n try {\n await Promise.race([\n handle.ready,\n link.ended.then(() => {\n throw refusal(SEAT_REFUSED);\n }),\n ]);\n } catch (error) {\n return fail(isChordError(error) && error.code === 'forbidden' ? refusal(SEAT_REFUSED) : error);\n }\n if (handle.kind !== 'guest') return fail(refusal(SEAT_REFUSED));\n const transcriptMember = handle.transcript as Readable<ConversationView | undefined>;\n const seatMember = handle.seat as Readable<GuestShown | undefined>;\n const proposalsMember = handle.proposals as Readable<GuestProposal[] | undefined>;\n const keeping = [transcriptMember, seatMember, proposalsMember].map(kept);\n await Promise.race([Promise.all(keeping.map((keep) => keep.ready)), link.ended]);\n const through =\n <T>(name: (typeof GUEST_METHODS)[number]) =>\n async (...args: unknown[]): Promise<T> => {\n const call = handle[name] as (args: unknown[], ctx: unknown) => Promise<T>;\n try {\n return await host.root((ctx) => call(args, ctx), {});\n } catch (error) {\n throw plain(error);\n }\n };\n const propose = (request: ProposeRequest) => through<Proposed>('propose')(request);\n const guest = seatMember.value as GuestShown;\n const refuse = () => Promise.reject(refusal(GUEST_ONLY));\n const control = Object.fromEntries(\n CONTROL_METHODS.map((name) => [name, refuse])\n ) as unknown as AgentControl;\n control.guestOpen = (draft) => through<GuestOpened>('guestOpen')(draft);\n control.guestRevoke = (id) => through<{ revoked: boolean }>('guestRevoke')(id);\n control.send = async (request): Promise<SendResponse> => {\n if (request.attachments?.length)\n return {\n accepted: false,\n submissionId: null,\n error: { code: 'EACCES', message: \"A guest can't attach files.\" },\n };\n const proposed = await propose({ text: request.text, requestId: request.requestId });\n if (proposed.state === 'submitted')\n return { accepted: true, submissionId: proposed.submissionId, error: null };\n if (proposed.state === 'pending')\n return { accepted: true, submissionId: proposed.approval, error: null };\n return {\n accepted: false,\n submissionId: null,\n error: { code: 'EACCES', message: proposedSentence(proposed.reason) },\n };\n };\n const transcript = lasting(replicated(transcriptMember));\n const proposals = lasting(replicated(proposalsMember));\n let open = true;\n const finish = () => {\n if (!open) return;\n open = false;\n transcript.freeze();\n proposals.freeze();\n for (const keep of keeping) keep.stop();\n handle.release();\n };\n const closed = link.ended.then((reason) => {\n finish();\n return reason;\n });\n const settings: AgentSettings = {\n state: replicatedState({ models: [], accounts: [] }),\n connect: refuse,\n disconnect: refuse,\n signIn: refuse,\n dismissMoved: refuse,\n };\n return {\n serverId,\n guest,\n control,\n transcript: transcript.state,\n proposals: proposals.state,\n deliveries: replicatedState({}),\n agents: replicatedState({\n active: guest.agentId,\n agents: [\n { id: guest.agentId, name: guest.name, kind: 'cone' as const, parentId: null, role: null },\n ],\n }),\n settings,\n closed,\n propose,\n prompt: refuse,\n webhook: refuse,\n async close() {\n finish();\n link.close('closed');\n await closed;\n await host.close();\n },\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { createHost, isChordError, type Readable, replicatedState } from '@earendil-works/chord';\nimport type { ConversationView } from '@earendil-works/pi-durable';\nimport { type AgentConnection, final, lasting } from '../client.ts';\nimport {\n type AgentControl,\n AgentGuestService,\n type AgentSettings,\n CONTROL_METHODS,\n type GUEST_METHODS,\n type GuestProposal,\n type GuestShown,\n type SendResponse,\n} from '../services.ts';\nimport { AGENT_HOST, kept, plain, type Remote, replicated } from '../serving.ts';\nimport { type PortEndpoint, PortLink } from '../wire.ts';\nimport type { GuestOpened, Proposed, ProposeRequest } from './index.ts';\n\nexport const GUEST_ONLY = 'a guest can only watch and send messages';\nexport const SEAT_REFUSED = \"This guest link isn't valid any more.\";\n\nconst SAID: Record<string, string> = {\n revoked: 'This guest seat was revoked.',\n empty: 'There is nothing to send.',\n 'too-long': 'That message is too long for a guest seat.',\n gone: 'The cone this seat shares is gone.',\n unavailable: \"Messages from guests need a review, and this agent can't review them yet.\",\n};\n\nexport const proposedSentence = (reason: string): string =>\n SAID[reason] ?? 'The message was not delivered.';\n\nconst refusal = (message: string, code = 'EACCES') => Object.assign(new Error(message), { code });\n\nexport interface GuestConnection extends AgentConnection {\n readonly guest: GuestShown;\n propose(request: ProposeRequest): Promise<Proposed>;\n}\n\nexport async function connectGuest(endpoint: PortEndpoint): Promise<GuestConnection> {\n const link = new PortLink(endpoint);\n let serverId: string;\n try {\n serverId = await link.hello;\n } catch {\n throw refusal(SEAT_REFUSED);\n }\n const host = createHost({ name: `guest:${crypto.randomUUID()}`, kind: 'client' });\n host.connect(link);\n const handle = host.get(AgentGuestService, {}, { at: AGENT_HOST }) as unknown as Remote;\n const fail = async (error: unknown): Promise<never> => {\n handle.release();\n link.close('handshake failed');\n await host.close();\n throw error;\n };\n try {\n await Promise.race([\n handle.ready,\n link.ended.then(() => {\n throw refusal(SEAT_REFUSED);\n }),\n ]);\n } catch (error) {\n return fail(\n isChordError(error) && error.code === 'forbidden' ? refusal(SEAT_REFUSED) : final(error)\n );\n }\n if (handle.kind !== 'guest') return fail(refusal(SEAT_REFUSED));\n const transcriptMember = handle.transcript as Readable<ConversationView | undefined>;\n const seatMember = handle.seat as Readable<GuestShown | undefined>;\n const proposalsMember = handle.proposals as Readable<GuestProposal[] | undefined>;\n const keeping = [transcriptMember, seatMember, proposalsMember].map(kept);\n await Promise.race([Promise.all(keeping.map((keep) => keep.ready)), link.ended]);\n const through =\n <T>(name: (typeof GUEST_METHODS)[number]) =>\n async (...args: unknown[]): Promise<T> => {\n const call = handle[name] as (args: unknown[], ctx: unknown) => Promise<T>;\n try {\n return await host.root((ctx) => call(args, ctx), {});\n } catch (error) {\n throw plain(error);\n }\n };\n const propose = (request: ProposeRequest) => through<Proposed>('propose')(request);\n const guest = seatMember.value as GuestShown;\n const refuse = () => Promise.reject(refusal(GUEST_ONLY));\n const control = Object.fromEntries(\n CONTROL_METHODS.map((name) => [name, refuse])\n ) as unknown as AgentControl;\n control.guestOpen = (draft) => through<GuestOpened>('guestOpen')(draft);\n control.guestRevoke = (id) => through<{ revoked: boolean }>('guestRevoke')(id);\n control.send = async (request): Promise<SendResponse> => {\n if (request.attachments?.length)\n return {\n accepted: false,\n submissionId: null,\n error: { code: 'EACCES', message: \"A guest can't attach files.\" },\n };\n const proposed = await propose({ text: request.text, requestId: request.requestId });\n if (proposed.state === 'submitted')\n return { accepted: true, submissionId: proposed.submissionId, error: null };\n if (proposed.state === 'pending')\n return { accepted: true, submissionId: proposed.approval, error: null };\n return {\n accepted: false,\n submissionId: null,\n error: { code: 'EACCES', message: proposedSentence(proposed.reason) },\n };\n };\n const transcript = lasting(replicated(transcriptMember));\n const proposals = lasting(replicated(proposalsMember));\n let open = true;\n const finish = () => {\n if (!open) return;\n open = false;\n transcript.freeze();\n proposals.freeze();\n for (const keep of keeping) keep.stop();\n handle.release();\n };\n const closed = link.ended.then((reason) => {\n finish();\n return reason;\n });\n const settings: AgentSettings = {\n state: replicatedState({ models: [], accounts: [] }),\n connect: refuse,\n disconnect: refuse,\n signIn: refuse,\n dismissMoved: refuse,\n };\n return {\n serverId,\n guest,\n control,\n transcript: transcript.state,\n proposals: proposals.state,\n deliveries: replicatedState({}),\n agents: replicatedState({\n active: guest.agentId,\n agents: [\n { id: guest.agentId, name: guest.name, kind: 'cone' as const, parentId: null, role: null },\n ],\n }),\n settings,\n closed,\n propose,\n prompt: refuse,\n webhook: refuse,\n async close() {\n finish();\n link.close('closed');\n await closed;\n await host.close();\n },\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -56,6 +56,8 @@ export interface Lasting<T> {
56
56
  freeze(): void;
57
57
  }
58
58
  export declare function lasting<T>(state: ReplicatedState<T>): Lasting<T>;
59
+ export declare const OTHER_VERSION = "That SLICC runs another version of the agent, so this tab can't follow it. Update both SLICCs, then reload this page.";
60
+ export declare const final: (error: unknown) => unknown;
59
61
  export declare function connectAgent(endpoint: PortEndpoint, options?: {
60
62
  context?: Context;
61
63
  }): Promise<AgentConnection>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-agent",
3
- "version": "5.5.1",
3
+ "version": "5.5.2",
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",