@ai-ecoverse/slicc-agent 5.2.0 → 5.4.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 (78) hide show
  1. package/README.md +18 -5
  2. package/dist/agent.js +6 -1
  3. package/dist/agent.js.map +2 -2
  4. package/dist/client.js +7 -13
  5. package/dist/client.js.map +2 -2
  6. package/dist/control.js +10 -4
  7. package/dist/control.js.map +2 -2
  8. package/dist/guests/attribution.js +22 -0
  9. package/dist/guests/attribution.js.map +7 -0
  10. package/dist/guests/client.js +138 -0
  11. package/dist/guests/client.js.map +7 -0
  12. package/dist/guests/guard.js +38 -0
  13. package/dist/guests/guard.js.map +7 -0
  14. package/dist/guests/index.js +282 -0
  15. package/dist/guests/index.js.map +7 -0
  16. package/dist/guests/seats.js +49 -0
  17. package/dist/guests/seats.js.map +7 -0
  18. package/dist/guests/serve.js +72 -0
  19. package/dist/guests/serve.js.map +7 -0
  20. package/dist/guests/view.js +40 -0
  21. package/dist/guests/view.js.map +7 -0
  22. package/dist/host.js +91 -3
  23. package/dist/host.js.map +2 -2
  24. package/dist/index.js +9 -2
  25. package/dist/index.js.map +2 -2
  26. package/dist/owner.js +2 -1
  27. package/dist/owner.js.map +2 -2
  28. package/dist/page.js +8 -0
  29. package/dist/page.js.map +2 -2
  30. package/dist/secrets/extension.js +4 -1
  31. package/dist/secrets/extension.js.map +2 -2
  32. package/dist/services.js +12 -0
  33. package/dist/services.js.map +2 -2
  34. package/dist/serving.js +7 -0
  35. package/dist/serving.js.map +2 -2
  36. package/dist/spectrum/agent-port.js +14 -6
  37. package/dist/spectrum/agent-port.js.map +2 -2
  38. package/dist/spectrum/approvals-port.js +1 -1
  39. package/dist/spectrum/approvals-port.js.map +2 -2
  40. package/dist/spectrum/guest-port.js +56 -0
  41. package/dist/spectrum/guest-port.js.map +7 -0
  42. package/dist/spectrum/index.js +20 -5
  43. package/dist/spectrum/index.js.map +3 -3
  44. package/dist/spectrum/messages.js +30 -8
  45. package/dist/spectrum/messages.js.map +2 -2
  46. package/dist/spectrum/notices-port.js +2 -17
  47. package/dist/spectrum/notices-port.js.map +2 -2
  48. package/dist/spectrum/proposals.js +56 -0
  49. package/dist/spectrum/proposals.js.map +7 -0
  50. package/dist/types/client.d.ts +7 -2
  51. package/dist/types/control.d.ts +2 -1
  52. package/dist/types/guests/attribution.d.ts +9 -0
  53. package/dist/types/guests/client.d.ts +12 -0
  54. package/dist/types/guests/guard.d.ts +5 -0
  55. package/dist/types/guests/index.d.ts +54 -0
  56. package/dist/types/guests/seats.d.ts +39 -0
  57. package/dist/types/guests/serve.d.ts +17 -0
  58. package/dist/types/guests/view.d.ts +3 -0
  59. package/dist/types/host.d.ts +19 -2
  60. package/dist/types/index.d.ts +4 -1
  61. package/dist/types/owner.d.ts +4 -0
  62. package/dist/types/page.d.ts +1 -0
  63. package/dist/types/services.d.ts +34 -2
  64. package/dist/types/serving.d.ts +8 -1
  65. package/dist/types/spectrum/agent-port.d.ts +0 -2
  66. package/dist/types/spectrum/guest-port.d.ts +26 -0
  67. package/dist/types/spectrum/index.d.ts +14 -1
  68. package/dist/types/spectrum/notices-port.d.ts +1 -3
  69. package/dist/types/spectrum/proposals.d.ts +8 -0
  70. package/dist/types/wire.d.ts +0 -7
  71. package/dist/wire.js +2 -12
  72. package/dist/wire.js.map +2 -2
  73. package/dist/worker.js +2 -1
  74. package/dist/worker.js.map +2 -2
  75. package/package.json +5 -6
  76. package/dist/mismatch.js +0 -41
  77. package/dist/mismatch.js.map +0 -7
  78. package/dist/types/mismatch.d.ts +0 -3
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/spectrum/proposals.ts"],
4
+ "sourcesContent": ["import type { Message, SystemMessage, UserMessage } from '@ai-ecoverse/slicc-spectrum/ui';\nimport type { GuestProposal } from '../services.ts';\n\nexport const UNANSWERED = 'Nobody approved this in time.';\nexport const DECLINED = 'It was declined.';\nexport const WITHDRAWN = 'It was withdrawn.';\n\nconst ENDED: Record<string, string> = {\n unanswered: UNANSWERED,\n rejected: DECLINED,\n withdrawn: WITHDRAWN,\n};\n\nexport function proposalQueue(\n proposals: readonly GuestProposal[] | undefined,\n label: string\n): UserMessage[] {\n return (proposals ?? [])\n .filter((proposal) => proposal.state === 'pending')\n .map((proposal) => ({\n id: `g${proposal.id}`,\n role: 'user',\n text: proposal.text,\n createdAt: proposal.createdAt,\n queued: true,\n mode: 'queue',\n from: label,\n origin: 'guest',\n }));\n}\n\nexport function proposalNotices(proposals: readonly GuestProposal[] | undefined): SystemMessage[] {\n return (proposals ?? []).flatMap((proposal) => {\n const title = ENDED[proposal.state];\n if (!title) return [];\n return [\n {\n id: `g${proposal.id}`,\n role: 'system',\n kind: 'notice',\n title,\n text: proposal.text,\n createdAt: proposal.decidedAt ?? proposal.createdAt,\n },\n ];\n });\n}\n\nexport function merged(messages: readonly Message[], notices: readonly SystemMessage[]): Message[] {\n if (!notices.length) return [...messages];\n const out: Message[] = [];\n const left = [...notices].sort((a, b) => a.createdAt - b.createdAt);\n for (const message of messages) {\n while (left.length && (left[0] as SystemMessage).createdAt < message.createdAt)\n out.push(left.shift() as SystemMessage);\n out.push(message);\n }\n return [...out, ...left];\n}\n"],
5
+ "mappings": "AAGO,MAAM,aAAa;AACnB,MAAM,WAAW;AACjB,MAAM,YAAY;AAEzB,MAAM,QAAgC;AAAA,EACpC,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,WAAW;AACb;AAEO,SAAS,cACd,WACA,OACe;AACf,UAAQ,aAAa,CAAC,GACnB,OAAO,CAAC,aAAa,SAAS,UAAU,SAAS,EACjD,IAAI,CAAC,cAAc;AAAA,IAClB,IAAI,IAAI,SAAS,EAAE;AAAA,IACnB,MAAM;AAAA,IACN,MAAM,SAAS;AAAA,IACf,WAAW,SAAS;AAAA,IACpB,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ;AAAA,EACV,EAAE;AACN;AAEO,SAAS,gBAAgB,WAAkE;AAChG,UAAQ,aAAa,CAAC,GAAG,QAAQ,CAAC,aAAa;AAC7C,UAAM,QAAQ,MAAM,SAAS,KAAK;AAClC,QAAI,CAAC,MAAO,QAAO,CAAC;AACpB,WAAO;AAAA,MACL;AAAA,QACE,IAAI,IAAI,SAAS,EAAE;AAAA,QACnB,MAAM;AAAA,QACN,MAAM;AAAA,QACN;AAAA,QACA,MAAM,SAAS;AAAA,QACf,WAAW,SAAS,aAAa,SAAS;AAAA,MAC5C;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEO,SAAS,OAAO,UAA8B,SAA8C;AACjG,MAAI,CAAC,QAAQ,OAAQ,QAAO,CAAC,GAAG,QAAQ;AACxC,QAAM,MAAiB,CAAC;AACxB,QAAM,OAAO,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS;AAClE,aAAW,WAAW,UAAU;AAC9B,WAAO,KAAK,UAAW,KAAK,CAAC,EAAoB,YAAY,QAAQ;AACnE,UAAI,KAAK,KAAK,MAAM,CAAkB;AACxC,QAAI,KAAK,OAAO;AAAA,EAClB;AACA,SAAO,CAAC,GAAG,KAAK,GAAG,IAAI;AACzB;",
6
+ "names": []
7
+ }
@@ -3,11 +3,12 @@ import type { ConversationView } from '@earendil-works/pi-durable';
3
3
  import type { Approval } from './approvals/record.ts';
4
4
  import type { ChangesView } from './changes/git.ts';
5
5
  import type { FrozenCone } from './freezer/index.ts';
6
+ import type { GuestSeat } from './guests/seats.ts';
6
7
  import type { MemoryEntry, MemoryScope } from './memory/format.ts';
7
8
  import type { AgentRemote } from './remote/host.ts';
8
9
  import type { AgentPages } from './remote/pages.ts';
9
10
  import type { VaultState } from './secrets/vault.ts';
10
- import { AgentControl, AgentSettings, type AgentsSummary, type Command, type Delivered, type MonitorFeed, type PrivateRevision, type SendMode } from './services.ts';
11
+ import { AgentControl, AgentSettings, type AgentsSummary, type Command, type Delivered, type GuestProposal, type GuestShown, type MonitorFeed, type PrivateRevision, type SendMode } from './services.ts';
11
12
  import type { Sprinkle } from './sprinkles/kind.ts';
12
13
  import { type PortEndpoint } from './wire.ts';
13
14
  export interface AgentConnection {
@@ -27,12 +28,16 @@ export interface AgentConnection {
27
28
  readonly monitor?: ReplicatedState<MonitorFeed>;
28
29
  readonly vault?: ReplicatedState<VaultState>;
29
30
  readonly labels?: ReplicatedState<Record<string, string>>;
31
+ readonly guests?: ReplicatedState<GuestSeat[]>;
32
+ readonly viewOnly?: boolean;
33
+ readonly guest?: GuestShown;
34
+ readonly proposals?: ReplicatedState<GuestProposal[]>;
35
+ readonly owner?: boolean;
30
36
  readonly approvals?: ReplicatedState<Approval[]>;
31
37
  readonly settings: AgentSettings;
32
38
  readonly remote?: AgentRemote;
33
39
  readonly pages?: AgentPages;
34
40
  readonly closed: Promise<Error | undefined>;
35
- readonly mismatch?: string;
36
41
  readonly readOnly?: boolean;
37
42
  prompt(text: string, whenBusy?: SendMode): Promise<string>;
38
43
  webhook(name: string, delivery: {
@@ -3,6 +3,7 @@ import type { ChangesRuntime } from './changes/index.ts';
3
3
  import type { Cones } from './cone.ts';
4
4
  import type { Deliveries } from './deliveries.ts';
5
5
  import type { FreezerRuntime } from './freezer/index.ts';
6
+ import type { Guests } from './guests/index.ts';
6
7
  import type { LickSources, Licks } from './licks/index.ts';
7
8
  import type { MemoryRuntime } from './memory/index.ts';
8
9
  import type { Monitor } from './monitor/index.ts';
@@ -23,4 +24,4 @@ export interface HostScoops {
23
24
  }
24
25
  export declare function readable(out: string): string;
25
26
  export declare function submissionId(value: string): SubmissionId | undefined;
26
- export declare function createAgentControl(harness: Harness, target: Conversation | Cones, deliveries?: Deliveries, licks?: HostLicks, scoops?: HostScoops, skills?: Pick<SkillsRuntime, 'expand' | 'commandsFor'>, 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, labels?: Pick<LabelsRuntime, 'label'>): AgentControl;
27
+ export declare function createAgentControl(harness: Harness, target: Conversation | Cones, deliveries?: Deliveries, licks?: HostLicks, scoops?: HostScoops, skills?: Pick<SkillsRuntime, 'expand' | 'commandsFor'>, 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, labels?: Pick<LabelsRuntime, 'label'>, guests?: Pick<Guests, 'open' | 'revoke'>): AgentControl;
@@ -0,0 +1,9 @@
1
+ export declare const GUEST_INPUT_KIND = "slicc.guest-input";
2
+ export type GuestInput = {
3
+ seat: string;
4
+ name: string;
5
+ approval: string | null;
6
+ };
7
+ export declare function guestContent(name: string, text: string): string;
8
+ export declare function guestHeader(name: string): string;
9
+ export declare function guestText(text: string, name: string): string;
@@ -0,0 +1,12 @@
1
+ import { type AgentConnection } from '../client.ts';
2
+ import { type GuestShown } from '../services.ts';
3
+ import { type PortEndpoint } from '../wire.ts';
4
+ import type { Proposed, ProposeRequest } from './index.ts';
5
+ export declare const GUEST_ONLY = "a guest can only watch and send messages";
6
+ export declare const SEAT_REFUSED = "This guest link isn't valid any more.";
7
+ export declare const proposedSentence: (reason: string) => string;
8
+ export interface GuestConnection extends AgentConnection {
9
+ readonly guest: GuestShown;
10
+ propose(request: ProposeRequest): Promise<Proposed>;
11
+ }
12
+ export declare function connectGuest(endpoint: PortEndpoint): Promise<GuestConnection>;
@@ -0,0 +1,5 @@
1
+ import { type Extension, type Harness } from '@earendil-works/pi-durable';
2
+ export declare const GUEST_GUARD = "slicc-guest-guard";
3
+ export declare const GUEST_REQUEST = "guest:";
4
+ export declare const GUEST_TOOLS_BLOCKED = "Tool calls in a run a guest started need a review, which this agent can't do yet.";
5
+ export declare function guestGuard(harness: () => Pick<Harness, 'submission'> | undefined): Extension;
@@ -0,0 +1,54 @@
1
+ import { type Context, type ReplicatedState } from '@earendil-works/chord';
2
+ import { type Harness } from '@earendil-works/pi-durable';
3
+ import type { Agents } from '../agents.ts';
4
+ import type { Approvals } from '../approvals/index.ts';
5
+ import type { GuestPrincipal } from '../serving.ts';
6
+ import { type GuestDraft, type GuestSeat, type SeatRecord } from './seats.ts';
7
+ export type GuestOpened = {
8
+ seat: GuestSeat;
9
+ error: null;
10
+ } | {
11
+ seat: null;
12
+ error: string;
13
+ };
14
+ export type Proposed = {
15
+ state: 'submitted';
16
+ submissionId: string;
17
+ reason: null;
18
+ } | {
19
+ state: 'pending';
20
+ submissionId: null;
21
+ approval: string;
22
+ reason: null;
23
+ } | {
24
+ state: 'unanswered';
25
+ submissionId: null;
26
+ reason: string;
27
+ };
28
+ export interface ProposeRequest {
29
+ text: string;
30
+ requestId: string | null;
31
+ }
32
+ export interface Guests {
33
+ readonly seats: ReplicatedState<GuestSeat[]>;
34
+ open(draft: GuestDraft, context: Context): Promise<GuestOpened>;
35
+ revoke(id: string, context: Context): Promise<{
36
+ revoked: boolean;
37
+ }>;
38
+ admit(id: string, label: string): GuestPrincipal | null;
39
+ record(id: string): SeatRecord | undefined;
40
+ current(guest: GuestPrincipal): SeatRecord | undefined;
41
+ joined(id: string): () => void;
42
+ onEnd(listener: (id: string) => void): () => void;
43
+ propose(guest: GuestPrincipal, request: ProposeRequest, context: Context): Promise<Proposed>;
44
+ dispose(): void;
45
+ }
46
+ export interface GuestsOptions {
47
+ harness: Pick<Harness, 'commit' | 'abortSubmission'>;
48
+ agents: Pick<Agents, 'state' | 'conversation'>;
49
+ approvals?: Pick<Approvals, 'state' | 'request' | 'withdrawSeat'>;
50
+ now?: () => number;
51
+ context?: Context;
52
+ }
53
+ export declare const MAX_PROPOSAL = 32000;
54
+ export declare function createGuests(options: GuestsOptions): Promise<Guests>;
@@ -0,0 +1,39 @@
1
+ import type { ApprovalTier } from '../approvals/record.ts';
2
+ export type GuestGate = ApprovalTier | 'off';
3
+ export type GuestGates = {
4
+ messages: GuestGate;
5
+ tools: GuestGate;
6
+ };
7
+ export type GuestDraft = {
8
+ id: string;
9
+ label: string;
10
+ agentId: string;
11
+ expiresAt: number | null;
12
+ gates: GuestGates;
13
+ };
14
+ export type SeatRecord = GuestDraft & {
15
+ createdAt: number;
16
+ epoch: number;
17
+ revoked: number | null;
18
+ delivered?: string[];
19
+ };
20
+ export interface GuestSeat {
21
+ id: string;
22
+ label: string;
23
+ agentId: string;
24
+ createdAt: number;
25
+ expiresAt: number | null;
26
+ gates: GuestGates;
27
+ connected: boolean;
28
+ pending: number;
29
+ }
30
+ export type GuestsState = {
31
+ seats: Record<string, SeatRecord>;
32
+ };
33
+ export declare const GuestsDoc: import("@earendil-works/pi-durable").SessionDocToken<GuestsState>;
34
+ export declare const MAX_SEAT_MS: number;
35
+ export declare const MAX_LABEL = 64;
36
+ export declare function cleanLabel(raw: unknown): string;
37
+ export declare function isGate(value: unknown): value is GuestGate;
38
+ export declare function checkDraft(draft: GuestDraft, now: number): string | null;
39
+ export declare function liveSeat(seat: SeatRecord | undefined, now: number): boolean;
@@ -0,0 +1,17 @@
1
+ import type { Context, Host, ReplicatedState } from '@earendil-works/chord';
2
+ import type { ConversationView } from '@earendil-works/pi-durable';
3
+ import type { Agents } from '../agents.ts';
4
+ import type { Approval } from '../approvals/index.ts';
5
+ import type { AgentGuest, GuestProposal } from '../services.ts';
6
+ import type { GuestPrincipal } from '../serving.ts';
7
+ import { type PortEndpoint } from '../wire.ts';
8
+ import type { Guests } from './index.ts';
9
+ export declare function proposalsOf(items: readonly Approval[] | undefined, seat: string): GuestProposal[];
10
+ export declare function guestOpener(guests: Guests, agents: Pick<Agents, 'state'>, views: ReplicatedState<Record<string, ConversationView>>, context: Context, approvals?: ReplicatedState<Approval[]>): (principal: GuestPrincipal) => {
11
+ value: AgentGuest;
12
+ dispose(): void;
13
+ } | undefined;
14
+ export declare function admitGuest(guests: Guests, endpoint: PortEndpoint, asked: {
15
+ id: string;
16
+ label: string;
17
+ }, serverId: string, link: number, host: Pick<Host, 'accept'>): void;
@@ -0,0 +1,3 @@
1
+ import type { ConversationView } from '@earendil-works/pi-durable';
2
+ export declare const EMPTY_VIEW: ConversationView;
3
+ export declare function guestView(view: ConversationView | undefined, seat: string): ConversationView;
@@ -1,9 +1,10 @@
1
- import { type Context, type Host, type ReplicatedState } from '@earendil-works/chord';
1
+ import { type Context, type Host, type Readable, type ReplicatedState } from '@earendil-works/chord';
2
2
  import type { Agent } from './agent.ts';
3
3
  import { type ApprovalJudge, type Approvals, type DecidedBy } from './approvals/index.ts';
4
4
  import type { ChangesRuntime } from './changes/index.ts';
5
5
  import { type HostLicks, type HostScoops } from './control.ts';
6
6
  import type { FreezerRuntime } from './freezer/index.ts';
7
+ import { type Guests } from './guests/index.ts';
7
8
  import type { UserClients } from './kernel/users.ts';
8
9
  import type { McpRuntime } from './mcp/index.ts';
9
10
  import type { MemoryRuntime } from './memory/index.ts';
@@ -12,7 +13,8 @@ import { type AgentRemote } from './remote/host.ts';
12
13
  import { type Pages } from './remote/pages.ts';
13
14
  import type { SecretName } from './secrets/names.ts';
14
15
  import type { VaultState } from './secrets/vault.ts';
15
- import { AgentControl, AgentSettings, AgentTranscript } from './services.ts';
16
+ import { AgentControl, type AgentGuest, AgentSettings, AgentTranscript } from './services.ts';
17
+ import { type GuestPrincipal, type Seat } from './serving.ts';
16
18
  import type { SkillsRuntime } from './skills/index.ts';
17
19
  import type { SprinklesRuntime } from './sprinkles/index.ts';
18
20
  import { type Thumbnail } from './thumbs.ts';
@@ -22,12 +24,17 @@ import { type PortEndpoint } from './wire.ts';
22
24
  export interface ConnectOptions {
23
25
  readOnly?: boolean;
24
26
  pages?: boolean;
27
+ guest?: {
28
+ id: string;
29
+ label: string;
30
+ };
25
31
  }
26
32
  export interface AgentHost {
27
33
  readonly serverId: string;
28
34
  readonly agent: Agent;
29
35
  readonly users?: UserClients;
30
36
  readonly pages: Pages;
37
+ readonly guests: Guests;
31
38
  readonly approvals: Approvals;
32
39
  connect(endpoint: PortEndpoint, options?: ConnectOptions): void;
33
40
  close(): Promise<void>;
@@ -67,7 +74,17 @@ interface Served {
67
74
  settings: AgentSettings;
68
75
  remote: AgentRemote;
69
76
  pages: Pages;
77
+ guest?: (principal: GuestPrincipal) => {
78
+ value: AgentGuest;
79
+ dispose(): void;
80
+ } | undefined;
70
81
  decide: (by: DecidedBy) => AgentControl['decide'];
71
82
  }
83
+ export declare const CLOSED_TO_GUESTS = "a guest seat has only its transcript and propose";
84
+ export declare const openToGuests: <F extends object>(factory: F) => F;
85
+ export declare function closedToGuests(host: Host): Host;
86
+ export declare function lazily<T>(state: () => ReplicatedState<T>): Readable<T>;
87
+ export declare const GUEST_MANAGERS: ReadonlySet<Seat>;
88
+ export declare const OWNER_GUESTS = "only the owner can invite or revoke guests";
72
89
  export declare function serveAgent(serving: Served): Host;
73
90
  export {};
@@ -4,6 +4,9 @@ export { APPROVAL_MS, type Approval, type ApprovalAnswer, type ApprovalDecided,
4
4
  export { FILE_LIMIT, IMAGE_LIMIT, type SendAttachment, type ShownAttachment, splitUser, TOTAL_LIMIT, userParts, } from './attachments.ts';
5
5
  export { type AgentConnection, connectAgent } from './client.ts';
6
6
  export { createAgentControl } from './control.ts';
7
+ export { connectGuest, GUEST_ONLY, type GuestConnection, SEAT_REFUSED, } from './guests/client.ts';
8
+ export type { GuestOpened, Proposed, ProposeRequest } from './guests/index.ts';
9
+ export type { GuestDraft, GuestGate, GuestGates, GuestSeat } from './guests/seats.ts';
7
10
  export { type AgentHost, type ConnectOptions, hostAgent } from './host.ts';
8
11
  export type { KernelClient, KernelFs, KernelProcess, KernelStat, SpawnOptions, } from './kernel/client.ts';
9
12
  export { HOME, type KernelEnvOptions, kernelEnvironment, SliccKernelEnv } from './kernel/env.ts';
@@ -18,5 +21,5 @@ export { BEDROCK, createAgentSettings, createSliccModels, DEFAULT_MODEL, DEFAULT
18
21
  export { AGENT_DIRECTORY, type OpfsSqliteOptions, openMemorySqliteStorage, openOpfsSqliteStorage, WasmSqliteDatabase, } from './sqlite.ts';
19
22
  export { type SystemFacts, systemSection } from './system.ts';
20
23
  export { UPLOADS, type Uploads, upload } from './uploads.ts';
21
- export { LINK_PROTOCOL, type PortEndpoint, PortLink } from './wire.ts';
24
+ export { type PortEndpoint, PortLink } from './wire.ts';
22
25
  export { type KernelPorts, kernelPort, kernelPorts, serveConnections, type WorkerScope, } from './worker.ts';
@@ -9,6 +9,10 @@ export interface AgentWorker {
9
9
  export interface PortOptions {
10
10
  readOnly?: boolean;
11
11
  pages?: boolean;
12
+ guest?: {
13
+ id: string;
14
+ label: string;
15
+ };
12
16
  }
13
17
  export interface AgentOwner {
14
18
  connect(options?: PortOptions): Promise<AgentConnection>;
@@ -1,4 +1,5 @@
1
1
  export { type AgentConnection, connectAgent } from './client.ts';
2
+ export { connectGuest, GUEST_ONLY, type GuestConnection, SEAT_REFUSED, } from './guests/client.ts';
2
3
  export { type ByteSink, bridgePort, encodeFrame, FrameReader, type LinkEnd, linkEndpoint, MAX_FRAME, } from './link.ts';
3
4
  export { AGENT_LOCK, type AgentOwner, type AgentWorker, type KernelSource, type OwnerOptions, type PortOptions, startAgent, } from './owner.ts';
4
5
  export { type RemoteControl, type RemoteEvent, type RemoteOptions, remoteControl, } from './remote/client.ts';
@@ -4,6 +4,8 @@ import type { Approval, ApprovalAnswer, ApprovalDecided } from './approvals/inde
4
4
  import type { SendAttachment, ShownAttachment } from './attachments.ts';
5
5
  import type { ChangesView } from './changes/git.ts';
6
6
  import type { FrozenCone } from './freezer/index.ts';
7
+ import type { GuestOpened, Proposed, ProposeRequest } from './guests/index.ts';
8
+ import type { GuestDraft, GuestGates, GuestSeat } from './guests/seats.ts';
7
9
  import type { MemoryEntry, MemoryScope, MemoryTag } from './memory/format.ts';
8
10
  import type { AgentRemote } from './remote/host.ts';
9
11
  import type { VaultState } from './secrets/vault.ts';
@@ -88,6 +90,10 @@ export interface AgentControl {
88
90
  image(ref: ImageRef, context: Context): Promise<StoredImage>;
89
91
  agentCommands(agentId: string, context: Context): Promise<Command[]>;
90
92
  toolLabel(agentId: string, callIds: string[], context: Context): Promise<string | null>;
93
+ guestOpen(draft: GuestDraft, context: Context): Promise<GuestOpened>;
94
+ guestRevoke(id: string, context: Context): Promise<{
95
+ revoked: boolean;
96
+ }>;
91
97
  decide(id: string, decision: ApprovalAnswer, reason: string | null, context: Context): Promise<ApprovalDecided>;
92
98
  }
93
99
  export interface ImageRef {
@@ -233,7 +239,7 @@ export interface AgentSettingsChange {
233
239
  thinkingLevel: 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max' | null;
234
240
  agentId?: string | null;
235
241
  }
236
- export declare const CONTROL_METHODS: ("abort" | "agentCommands" | "changeAccept" | "changeRevert" | "changesOpen" | "compact" | "compactAgent" | "configure" | "createCone" | "createScoop" | "decide" | "discard" | "drop" | "freeze" | "image" | "memoryRemove" | "memorySave" | "monitorClose" | "monitorOpen" | "newChat" | "reset" | "resolveAgentLick" | "resolveLick" | "rewind" | "rewindAgent" | "selectCone" | "send" | "sprinkleCall" | "sprinkleSend" | "stopAgent" | "thaw" | "toolLabel" | "unqueue" | "wait" | "webhook" | "withdraw")[];
242
+ export declare const CONTROL_METHODS: ("abort" | "agentCommands" | "changeAccept" | "changeRevert" | "changesOpen" | "compact" | "compactAgent" | "configure" | "createCone" | "createScoop" | "decide" | "discard" | "drop" | "freeze" | "guestOpen" | "guestRevoke" | "image" | "memoryRemove" | "memorySave" | "monitorClose" | "monitorOpen" | "newChat" | "reset" | "resolveAgentLick" | "resolveLick" | "rewind" | "rewindAgent" | "selectCone" | "send" | "sprinkleCall" | "sprinkleSend" | "stopAgent" | "thaw" | "toolLabel" | "unqueue" | "wait" | "webhook" | "withdraw")[];
237
243
  export declare const CONTROL_READS: readonly (keyof AgentControl & string)[];
238
244
  export declare const AgentControl: RemoteContract;
239
245
  export type Delivered = 'run' | 'steer' | 'follow-up';
@@ -252,6 +258,7 @@ export interface AgentTranscript {
252
258
  readonly monitor: ReplicatedState<MonitorFeed>;
253
259
  readonly vault: ReplicatedState<VaultState>;
254
260
  readonly labels: ReplicatedState<Record<string, string>>;
261
+ readonly guests: ReplicatedState<GuestSeat[]>;
255
262
  readonly approvals: ReplicatedState<Approval[]>;
256
263
  }
257
264
  export interface PrivateRevision {
@@ -262,10 +269,35 @@ export interface Command {
262
269
  description: string;
263
270
  kind: 'prompt' | 'skill' | 'action';
264
271
  }
265
- export declare const TRANSCRIPT_STATES: ("agents" | "approvals" | "changes" | "commands" | "deliveries" | "frozen" | "labels" | "memories" | "memoryScopes" | "monitor" | "privateRevision" | "sprinkles" | "state" | "vault" | "views")[];
272
+ export declare const TRANSCRIPT_STATES: ("agents" | "approvals" | "changes" | "commands" | "deliveries" | "frozen" | "guests" | "labels" | "memories" | "memoryScopes" | "monitor" | "privateRevision" | "sprinkles" | "state" | "vault" | "views")[];
266
273
  export declare const AgentTranscript: RemoteContract;
267
274
  export declare const REMOTE_METHODS: ("abort" | "close" | "next" | "prompt" | "watch")[];
268
275
  export declare const REMOTE_READS: readonly (keyof AgentRemote & string)[];
276
+ export interface GuestShown {
277
+ id: string;
278
+ label: string;
279
+ agentId: string;
280
+ name: string;
281
+ expiresAt: number | null;
282
+ gates: GuestGates;
283
+ }
284
+ export interface GuestProposal {
285
+ id: string;
286
+ text: string;
287
+ state: 'pending' | 'approved' | 'rejected' | 'unanswered' | 'withdrawn';
288
+ reason: string | null;
289
+ createdAt: number;
290
+ decidedAt: number | null;
291
+ }
292
+ export interface AgentGuest {
293
+ readonly transcript: ReplicatedState<ConversationView>;
294
+ readonly seat: ReplicatedState<GuestShown>;
295
+ readonly proposals: ReplicatedState<GuestProposal[]>;
296
+ propose(request: ProposeRequest, context: Context): Promise<Proposed>;
297
+ }
298
+ export declare const GUEST_STATES: readonly ['transcript', 'seat', 'proposals'];
299
+ export declare const GUEST_METHODS: readonly ['propose'];
300
+ export declare const AgentGuestService: RemoteContract;
269
301
  export declare const AgentRemoteService: RemoteContract;
270
302
  export declare const PAGE_METHODS: ("answer" | "close" | "next" | "open" | "report")[];
271
303
  export declare const AgentPagesService: RemoteContract;
@@ -1,12 +1,19 @@
1
1
  import { type Ctx, type Principal, type Readable, type RemoteContract, type ReplicatedState } from '@earendil-works/chord';
2
2
  import { type Context } from '@earendil-works/chord/context';
3
3
  export declare const AGENT_HOST = "agent";
4
- export type Seat = 'owner' | 'control' | 'view';
4
+ export type Seat = 'owner' | 'control' | 'view' | 'guest';
5
+ export interface GuestPrincipal {
6
+ id: string;
7
+ name: string;
8
+ epoch: number;
9
+ }
5
10
  export interface AgentPrincipal {
6
11
  seat: Seat;
7
12
  link: number;
13
+ guest?: GuestPrincipal;
8
14
  }
9
15
  export declare const seatOf: (principal: Principal) => Seat;
16
+ export declare const guestOf: (principal: Principal) => GuestPrincipal | undefined;
10
17
  export declare const principalKey: (principal: Principal) => string;
11
18
  export interface Members {
12
19
  methods?: readonly string[];
@@ -5,7 +5,6 @@ import type { Command } from '../services.ts';
5
5
  import { Emitter } from './emitter.ts';
6
6
  export declare const CONE = "cone";
7
7
  export declare function slashCommand({ kind, ...command }: Command): SlashCommand;
8
- export declare const WATCH_ONLY = "Watch only.";
9
8
  export type AgentHold = VaultHold;
10
9
  export type AgentAdapterEvents = AgentEvents & {
11
10
  hold: AgentHold | null;
@@ -16,7 +15,6 @@ export declare class AgentAdapter extends Emitter<AgentAdapterEvents> implements
16
15
  notify(text: string, agentId?: string): void;
17
16
  ready(): Promise<void>;
18
17
  hold(): AgentHold | null;
19
- readOnly(): string | null;
20
18
  list(): readonly Agent[];
21
19
  active(): string;
22
20
  select(id: string): void;
@@ -0,0 +1,26 @@
1
+ import type { GuestDraft, GuestEvents, GuestPort, GuestSeat } from '@ai-ecoverse/slicc-spectrum/ui';
2
+ import type { AgentConnection } from '../client.ts';
3
+ import { Emitter } from './emitter.ts';
4
+ export interface GuestHub {
5
+ mint(request: GuestDraft & {
6
+ agentId: string;
7
+ }): Promise<{
8
+ id: string;
9
+ url: string;
10
+ }>;
11
+ revoke(id: string): Promise<void>;
12
+ }
13
+ export declare const NO_HUB = "Inviting guests needs the tray hub.";
14
+ export declare const NO_CONTROL = "Only a tab with Control can invite guests.";
15
+ export declare const NO_OWNER = "Only the owner's tab can invite guests.";
16
+ export declare class GuestAdapter extends Emitter<GuestEvents> implements GuestPort {
17
+ #private;
18
+ constructor(connection: AgentConnection, hub: GuestHub | null);
19
+ list(): readonly GuestSeat[];
20
+ unavailable(): string | null;
21
+ mint(agentId: string, draft: GuestDraft): Promise<{
22
+ seat: GuestSeat;
23
+ url: string;
24
+ }>;
25
+ revoke(id: string): Promise<void>;
26
+ }
@@ -1,8 +1,10 @@
1
1
  import type { MonitorSection } from '@ai-ecoverse/slicc-spectrum/ui';
2
2
  import type { AgentConnection } from '../client.ts';
3
+ import type { GuestGates } from '../guests/seats.ts';
3
4
  import { AgentAdapter } from './agent-port.ts';
4
5
  import { ApprovalsAdapter, type HostApprovals } from './approvals-port.ts';
5
6
  import { ChangesAdapter } from './changes-port.ts';
7
+ import { GuestAdapter, type GuestHub } from './guest-port.ts';
6
8
  import { MemoryAdapter } from './memory-port.ts';
7
9
  import { MonitorAdapter } from './monitor-port.ts';
8
10
  import { type HostNotices, NoticesAdapter } from './notices-port.ts';
@@ -13,14 +15,22 @@ import { TrayAdapter } from './tray-port.ts';
13
15
  export { AgentAdapter, CONE } from './agent-port.ts';
14
16
  export { ApprovalsAdapter, type HostApprovals, NOT_HERE, shown as shownApproval, } from './approvals-port.ts';
15
17
  export { ChangesAdapter, type ChangesEvents } from './changes-port.ts';
18
+ export { GuestAdapter, type GuestHub, NO_CONTROL, NO_HUB, NO_OWNER, } from './guest-port.ts';
16
19
  export { MemoryAdapter } from './memory-port.ts';
17
20
  export { assistant, errorAction, errorPart, isBusy, queued, textOf, toMessages, } from './messages.ts';
18
21
  export { MonitorAdapter, type MonitorAdapterOptions, type MonitorInput, monitorSnapshot, } from './monitor-port.ts';
19
- export { type HostNotices, KEYS_MOVED, MISMATCH, mismatchBanner, movedList, movedNotice, NoticesAdapter, } from './notices-port.ts';
22
+ export { type HostNotices, KEYS_MOVED, movedList, movedNotice, NoticesAdapter, } from './notices-port.ts';
20
23
  export { PageAdapter, type PageDocument, type PageOptions, type PageRun, type PageTarget, } from './page-port.ts';
21
24
  export { type Login, SETTINGS_KEY, SettingsAdapter } from './settings-port.ts';
22
25
  export { SprinkleAdapter } from './sprinkle-port.ts';
23
26
  export { TrayAdapter } from './tray-port.ts';
27
+ export interface GuestSeatShown {
28
+ id: string;
29
+ label: string;
30
+ agentId: string;
31
+ expiresAt: number | null;
32
+ gates: GuestGates;
33
+ }
24
34
  export declare function createAgentModel(connection: AgentConnection, options?: {
25
35
  storage?: Pick<Storage, 'getItem' | 'setItem'> | null;
26
36
  login?: Login;
@@ -28,6 +38,7 @@ export declare function createAgentModel(connection: AgentConnection, options?:
28
38
  extra?: () => readonly MonitorSection[];
29
39
  };
30
40
  page?: PageOptions;
41
+ guests?: GuestHub;
31
42
  approvals?: HostApprovals;
32
43
  notices?: HostNotices;
33
44
  }): {
@@ -41,4 +52,6 @@ export declare function createAgentModel(connection: AgentConnection, options?:
41
52
  monitor: MonitorAdapter;
42
53
  notices: NoticesAdapter;
43
54
  page: PageAdapter | null;
55
+ guests: GuestAdapter;
56
+ seat?: GuestSeatShown;
44
57
  };
@@ -4,8 +4,6 @@ import type { AgentSettings } from '../services.ts';
4
4
  import { type Report } from './attempt.ts';
5
5
  import { Emitter } from './emitter.ts';
6
6
  export declare const KEYS_MOVED = "keys-moved";
7
- export declare const MISMATCH = "agent-mismatch";
8
- export declare function mismatchBanner(text: string): Notice;
9
7
  export declare function movedList(accounts: readonly MovedName[]): string;
10
8
  export declare function movedNotice(accounts: readonly MovedName[]): Notice;
11
9
  export type HostNotices = Pick<NoticesPort, 'list' | 'act' | 'dismiss' | 'on'>;
@@ -13,7 +11,7 @@ export declare class NoticesAdapter extends Emitter<{
13
11
  notices: readonly Notice[];
14
12
  }> implements NoticesPort {
15
13
  #private;
16
- constructor(settings: AgentSettings, report: Report, mismatch?: string, host?: HostNotices);
14
+ constructor(settings: AgentSettings, report: Report, host?: HostNotices);
17
15
  list(): readonly Notice[];
18
16
  act(id: string, action: string): Promise<void>;
19
17
  dismiss(id: string): void;
@@ -0,0 +1,8 @@
1
+ import type { Message, SystemMessage, UserMessage } from '@ai-ecoverse/slicc-spectrum/ui';
2
+ import type { GuestProposal } from '../services.ts';
3
+ export declare const UNANSWERED = "Nobody approved this in time.";
4
+ export declare const DECLINED = "It was declined.";
5
+ export declare const WITHDRAWN = "It was withdrawn.";
6
+ export declare function proposalQueue(proposals: readonly GuestProposal[] | undefined, label: string): UserMessage[];
7
+ export declare function proposalNotices(proposals: readonly GuestProposal[] | undefined): SystemMessage[];
8
+ export declare function merged(messages: readonly Message[], notices: readonly SystemMessage[]): Message[];
@@ -11,13 +11,6 @@ export type Frame = {
11
11
  } | {
12
12
  close: true;
13
13
  } | Uint8Array;
14
- export declare const LINK_PROTOCOL = 2;
15
- export interface Greeting {
16
- protocol: number;
17
- serverId: string;
18
- }
19
- export declare const greeting: (serverId: string) => string;
20
- export declare function greeted(hello: string): Greeting;
21
14
  export declare class PortLink implements Link {
22
15
  #private;
23
16
  readonly hello: Promise<string>;
package/dist/wire.js CHANGED
@@ -1,10 +1,3 @@
1
- const LINK_PROTOCOL = 2;
2
- const GREETING = /^slicc-agent-link\/(\d+) (.+)$/;
3
- const greeting = (serverId) => `slicc-agent-link/${LINK_PROTOCOL} ${serverId}`;
4
- function greeted(hello) {
5
- const found = GREETING.exec(hello);
6
- return found ? { protocol: Number(found[1]), serverId: found[2] } : { protocol: 1, serverId: hello };
7
- }
8
1
  const encoder = new TextEncoder();
9
2
  const decoder = new TextDecoder();
10
3
  function parse(chunk) {
@@ -45,7 +38,7 @@ class PortLink {
45
38
  return this.#closed;
46
39
  }
47
40
  greet(serverId) {
48
- this.#endpoint.postMessage({ hello: greeting(serverId) });
41
+ this.#endpoint.postMessage({ hello: serverId });
49
42
  }
50
43
  send(frame) {
51
44
  if (this.#closed) return;
@@ -98,9 +91,6 @@ class PortLink {
98
91
  }
99
92
  }
100
93
  export {
101
- LINK_PROTOCOL,
102
- PortLink,
103
- greeted,
104
- greeting
94
+ PortLink
105
95
  };
106
96
  //# sourceMappingURL=wire.js.map
package/dist/wire.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/wire.ts"],
4
- "sourcesContent": ["import type { Frame as ChordFrame, Link } from '@earendil-works/chord';\n\nexport interface PortEndpoint {\n postMessage(message: unknown, transfer?: Transferable[]): void;\n addEventListener(type: 'message', listener: (event: MessageEvent) => void): void;\n addEventListener(\n type: 'close' | 'messageerror' | 'error',\n listener: (event: Event) => void\n ): void;\n start?(): void;\n close?(): void;\n}\n\nexport type Frame = { hello: string } | { close: true } | Uint8Array;\n\nexport const LINK_PROTOCOL = 2;\n\nconst GREETING = /^slicc-agent-link\\/(\\d+) (.+)$/;\n\nexport interface Greeting {\n protocol: number;\n serverId: string;\n}\n\nexport const greeting = (serverId: string): string =>\n `slicc-agent-link/${LINK_PROTOCOL} ${serverId}`;\n\nexport function greeted(hello: string): Greeting {\n const found = GREETING.exec(hello);\n return found\n ? { protocol: Number(found[1]), serverId: found[2] as string }\n : { protocol: 1, serverId: hello };\n}\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\nfunction parse(chunk: Uint8Array): ChordFrame {\n const frame = JSON.parse(decoder.decode(chunk)) as { t?: unknown } | null;\n if (frame === null || typeof frame !== 'object' || typeof frame.t !== 'string')\n throw new TypeError('not a chord frame');\n return frame as ChordFrame;\n}\n\nexport class PortLink implements Link {\n readonly hello: Promise<string>;\n readonly ended: Promise<Error | undefined>;\n readonly #endpoint: PortEndpoint;\n readonly #queue: Uint8Array[] = [];\n readonly #closers = new Set<(reason: string) => void>();\n #handler: ((frame: ChordFrame) => void) | undefined;\n #closed = false;\n #greet!: (hello: string) => void;\n #refuse!: (error: Error) => void;\n #finish!: (reason: Error | undefined) => void;\n\n constructor(endpoint: PortEndpoint) {\n this.#endpoint = endpoint;\n this.hello = new Promise((greet, refuse) => {\n this.#greet = greet;\n this.#refuse = refuse;\n });\n this.hello.catch(() => undefined);\n this.ended = new Promise((finish) => {\n this.#finish = finish;\n });\n endpoint.addEventListener('message', ({ data }) => this.#receive(data as Frame));\n const failed = (event: Event) => this.#end(`agent port ${event.type}`, true);\n endpoint.addEventListener('messageerror', failed);\n endpoint.addEventListener('error', failed);\n endpoint.addEventListener('close', () => this.#end('peer closed', false));\n endpoint.start?.();\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n\n greet(serverId: string): void {\n this.#endpoint.postMessage({ hello: greeting(serverId) } satisfies Frame);\n }\n\n send(frame: ChordFrame): void {\n if (this.#closed) return;\n const bytes = encoder.encode(JSON.stringify(frame));\n this.#endpoint.postMessage(bytes, [bytes.buffer]);\n }\n\n receive(handler: (frame: ChordFrame) => void): void {\n this.#handler = handler;\n for (const chunk of this.#queue.splice(0)) this.#deliver(chunk);\n }\n\n onClose(handler: (reason: string) => void): void {\n if (this.#closed) handler('closed');\n else this.#closers.add(handler);\n }\n\n close(reason = 'closed'): void {\n if (this.#closed) return;\n this.send({ t: 'bye', reason });\n this.#endpoint.postMessage({ close: true } satisfies Frame);\n this.#end(reason, false);\n }\n\n #receive(frame: Frame): void {\n if (this.#closed) return;\n if (frame instanceof Uint8Array) {\n if (this.#handler) this.#deliver(frame);\n else this.#queue.push(frame);\n } else if ('hello' in frame) this.#greet(frame.hello);\n else this.#end('peer closed', false);\n }\n\n #deliver(chunk: Uint8Array): void {\n let frame: ChordFrame;\n try {\n frame = parse(chunk);\n } catch {\n this.close('protocol error');\n return;\n }\n if (frame.t === 'bye') this.#end(frame.reason ?? 'peer closed', false);\n else this.#handler?.(frame);\n }\n\n #end(reason: string, failed: boolean): void {\n if (this.#closed) return;\n this.#closed = true;\n this.#queue.length = 0;\n this.#endpoint.close?.();\n const error = new Error(reason);\n this.#refuse(new Error(`agent port closed before hello: ${reason}`));\n this.#finish(failed ? error : undefined);\n for (const handler of [...this.#closers]) handler(reason);\n this.#closers.clear();\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { Frame as ChordFrame, Link } from '@earendil-works/chord';\n\nexport interface PortEndpoint {\n postMessage(message: unknown, transfer?: Transferable[]): void;\n addEventListener(type: 'message', listener: (event: MessageEvent) => void): void;\n addEventListener(\n type: 'close' | 'messageerror' | 'error',\n listener: (event: Event) => void\n ): void;\n start?(): void;\n close?(): void;\n}\n\nexport type Frame = { hello: string } | { close: true } | Uint8Array;\n\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder();\n\nfunction parse(chunk: Uint8Array): ChordFrame {\n const frame = JSON.parse(decoder.decode(chunk)) as { t?: unknown } | null;\n if (frame === null || typeof frame !== 'object' || typeof frame.t !== 'string')\n throw new TypeError('not a chord frame');\n return frame as ChordFrame;\n}\n\nexport class PortLink implements Link {\n readonly hello: Promise<string>;\n readonly ended: Promise<Error | undefined>;\n readonly #endpoint: PortEndpoint;\n readonly #queue: Uint8Array[] = [];\n readonly #closers = new Set<(reason: string) => void>();\n #handler: ((frame: ChordFrame) => void) | undefined;\n #closed = false;\n #greet!: (hello: string) => void;\n #refuse!: (error: Error) => void;\n #finish!: (reason: Error | undefined) => void;\n\n constructor(endpoint: PortEndpoint) {\n this.#endpoint = endpoint;\n this.hello = new Promise((greet, refuse) => {\n this.#greet = greet;\n this.#refuse = refuse;\n });\n this.hello.catch(() => undefined);\n this.ended = new Promise((finish) => {\n this.#finish = finish;\n });\n endpoint.addEventListener('message', ({ data }) => this.#receive(data as Frame));\n const failed = (event: Event) => this.#end(`agent port ${event.type}`, true);\n endpoint.addEventListener('messageerror', failed);\n endpoint.addEventListener('error', failed);\n endpoint.addEventListener('close', () => this.#end('peer closed', false));\n endpoint.start?.();\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n\n greet(serverId: string): void {\n this.#endpoint.postMessage({ hello: serverId } satisfies Frame);\n }\n\n send(frame: ChordFrame): void {\n if (this.#closed) return;\n const bytes = encoder.encode(JSON.stringify(frame));\n this.#endpoint.postMessage(bytes, [bytes.buffer]);\n }\n\n receive(handler: (frame: ChordFrame) => void): void {\n this.#handler = handler;\n for (const chunk of this.#queue.splice(0)) this.#deliver(chunk);\n }\n\n onClose(handler: (reason: string) => void): void {\n if (this.#closed) handler('closed');\n else this.#closers.add(handler);\n }\n\n close(reason = 'closed'): void {\n if (this.#closed) return;\n this.send({ t: 'bye', reason });\n this.#endpoint.postMessage({ close: true } satisfies Frame);\n this.#end(reason, false);\n }\n\n #receive(frame: Frame): void {\n if (this.#closed) return;\n if (frame instanceof Uint8Array) {\n if (this.#handler) this.#deliver(frame);\n else this.#queue.push(frame);\n } else if ('hello' in frame) this.#greet(frame.hello);\n else this.#end('peer closed', false);\n }\n\n #deliver(chunk: Uint8Array): void {\n let frame: ChordFrame;\n try {\n frame = parse(chunk);\n } catch {\n this.close('protocol error');\n return;\n }\n if (frame.t === 'bye') this.#end(frame.reason ?? 'peer closed', false);\n else this.#handler?.(frame);\n }\n\n #end(reason: string, failed: boolean): void {\n if (this.#closed) return;\n this.#closed = true;\n this.#queue.length = 0;\n this.#endpoint.close?.();\n const error = new Error(reason);\n this.#refuse(new Error(`agent port closed before hello: ${reason}`));\n this.#finish(failed ? error : undefined);\n for (const handler of [...this.#closers]) handler(reason);\n this.#closers.clear();\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/worker.js CHANGED
@@ -4,8 +4,9 @@ function serveConnections(scope, host) {
4
4
  const port = message?.connect;
5
5
  if (!port) return;
6
6
  const pages = message?.pages === false ? { pages: false } : {};
7
+ const guest = message?.guest ? { guest: message.guest } : {};
7
8
  host.then(
8
- (ready) => ready.connect(port, { readOnly: message?.readOnly === true, ...pages }),
9
+ (ready) => ready.connect(port, { readOnly: message?.readOnly === true, ...pages, ...guest }),
9
10
  () => port.close?.()
10
11
  );
11
12
  });