@fungi.computer/oyster 0.4.1 → 0.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 (45) hide show
  1. package/dist/agents.d.ts +2 -1
  2. package/dist/agents.js +63 -8
  3. package/dist/apps/gate.d.ts +4 -0
  4. package/dist/apps/gate.js +3 -0
  5. package/dist/boot-host-script.js +1 -1
  6. package/dist/build/candidate-cli.mjs +19 -4
  7. package/dist/channels/binding.d.ts +63 -0
  8. package/dist/channels/binding.js +85 -0
  9. package/dist/channels/conversations.d.ts +235 -0
  10. package/dist/channels/conversations.js +705 -0
  11. package/dist/channels/ids.d.ts +8 -0
  12. package/dist/channels/ids.js +15 -0
  13. package/dist/host.d.ts +1 -1
  14. package/dist/host.js +21 -4
  15. package/dist/keys.d.ts +4 -1
  16. package/dist/keys.js +15 -4
  17. package/dist/machine-link/authority.d.ts +7 -0
  18. package/dist/machine-link/delivery.d.ts +12 -0
  19. package/dist/machine-link/delivery.js +22 -1
  20. package/dist/machine-link/disposition.d.ts +16 -0
  21. package/dist/machine-link/disposition.js +65 -0
  22. package/dist/machine-link/host.d.ts +11 -2
  23. package/dist/machine-link/host.js +125 -5
  24. package/dist/machine-link/link.d.ts +4 -0
  25. package/dist/machine-link/link.js +47 -1
  26. package/dist/page-script.js +4 -4
  27. package/dist/standalone/agent-mail.js +4 -1
  28. package/dist/standalone/agents.d.ts +8 -0
  29. package/dist/standalone/agents.js +21 -2
  30. package/dist/standalone/channel-session-door.d.ts +4 -0
  31. package/dist/standalone/channel-session-door.js +42 -0
  32. package/dist/standalone/machine-link.d.ts +2 -0
  33. package/dist/standalone/machine-link.js +34 -1
  34. package/dist/testing/capabilities.d.ts +2 -0
  35. package/dist/testing/capabilities.js +3 -1
  36. package/dist/workers-git.js +1 -1
  37. package/dist/workers.d.ts +3 -0
  38. package/docs/api/agents.md +8 -4
  39. package/docs/api/apps.md +8 -4
  40. package/docs/api/browser.md +40 -16
  41. package/docs/api/keys.md +25 -3
  42. package/docs/api/workers.md +21 -14
  43. package/docs/site/agent-quickstart.mdx +23 -0
  44. package/examples/standalone/wrangler.jsonc +3 -4
  45. package/package.json +28 -16
@@ -4,8 +4,16 @@ import * as Schema from "effect/Schema";
4
4
  import { BirdDogMailSchema } from "@fungi.computer/bird-dog/effect";
5
5
  import { agentMailAddressSchema, machineAgentOperationSchema, MachineLinkPostUnavailable, } from "@fungi.computer/seam/machine-link-protocol";
6
6
  import { machineLinkFenceFactSchema, machineLinkIdentitySchema, machineLinkName, MachineLinkRefused, } from "./authority.js";
7
+ import { machineLinkDisposition } from "./disposition.js";
7
8
  import { MachineLinkHost } from "./host.js";
8
9
  const pinKey = "machine-link:identity";
10
+ function isRetainedRelayFence(target, relay, fence) {
11
+ return (relay.teamId === target.teamId &&
12
+ relay.deviceId !== target.deviceId &&
13
+ fence !== null &&
14
+ fence.teamId === relay.teamId &&
15
+ fence.deviceId === relay.deviceId);
16
+ }
9
17
  /**
10
18
  * One device's link inside its own Durable Object. The host's DO class
11
19
  * delegates every entry point here. Identity is pinned on the first live
@@ -60,6 +68,12 @@ export function createMachineLink(state, options) {
60
68
  })(envelope.event);
61
69
  return { address, mail: body };
62
70
  };
71
+ const deliverUndeliverable = async (identity, rawOriginal, rawReply) => {
72
+ const checked = machineLinkDisposition(identity, rawOriginal, rawReply);
73
+ if (checked === undefined)
74
+ return "refused";
75
+ return await authority.deliverUndeliverable(identity, checked.original, checked.reply);
76
+ };
63
77
  /**
64
78
  * The link's own sender check, whatever the host answered: the facts must
65
79
  * name the caller and actor it saw, a hosted caller must be hosted, and a
@@ -94,6 +108,8 @@ export function createMachineLink(state, options) {
94
108
  if (envelope.kind !== "agentMail")
95
109
  return "refused";
96
110
  const { address, mail: body } = mail(envelope);
111
+ if (body.from.kind !== "agent")
112
+ return "refused";
97
113
  const caller = {
98
114
  kind: "device",
99
115
  deviceId: identity.deviceId,
@@ -118,6 +134,7 @@ export function createMachineLink(state, options) {
118
134
  replay: openReplayStore,
119
135
  directory: () => authority.directory(identity),
120
136
  receive: (_device, envelope) => receive(identity, envelope),
137
+ disposition: (original, reply) => deliverUndeliverable(identity, original, reply),
121
138
  scheduleWake: (at) => state.storage.setAlarm(at),
122
139
  report: options.report,
123
140
  }));
@@ -149,16 +166,45 @@ export function createMachineLink(state, options) {
149
166
  return "refused";
150
167
  const { address, mail: body } = mail(envelope);
151
168
  const facts = await checkedTarget(device, caller, agentId, null);
169
+ const senderMatches = body.from.kind === "agent" && body.from.agentId === facts.callerAgentId;
170
+ const systemOfflineDisposition = body.from.kind === "system" &&
171
+ body.reply?.outcome === "undeliverable" &&
172
+ body.reply.reason === "recipient_offline" &&
173
+ body.to.agentId === facts.targetAgentId;
152
174
  if (facts.targetHome.kind !== "machine" ||
153
175
  facts.targetHome.deviceId !== device.deviceId ||
154
176
  envelope.key !== body.key ||
155
177
  address.scopeId !== facts.teamId ||
156
178
  address.agentId !== facts.targetAgentId ||
157
179
  body.to.agentId !== facts.targetAgentId ||
158
- body.from.agentId !== facts.callerAgentId)
180
+ (!senderMatches && !systemOfflineDisposition))
159
181
  return "refused";
160
182
  return await hostFor(device).deliver(device.deviceId, envelope);
161
183
  },
184
+ /** A relay's typed terminal reply to an Agent homed on this device. */
185
+ async deliverRelayDisposition(input, relayInput, original, reply) {
186
+ const device = await current(input);
187
+ const relay = machineLinkIdentitySchema.parse(relayInput);
188
+ const retainedRelay = await authority.currentFence(relay);
189
+ if (!isRetainedRelayFence(device, relay, retainedRelay))
190
+ return "refused";
191
+ const checked = machineLinkDisposition(device, original, reply);
192
+ if (checked === undefined)
193
+ return "refused";
194
+ if (!(await authority.verifyRelayDisposition(relay, checked.original, checked.reply)))
195
+ return "refused";
196
+ const home = await authority.agentHome(device, checked.reply.address.agentId);
197
+ if (home?.kind !== "machine" || home.deviceId !== device.deviceId)
198
+ return "refused";
199
+ return await hostFor(device).deliver(device.deviceId, checked.reply, {
200
+ refuseDuplicate: true,
201
+ });
202
+ },
203
+ /** Attest a still-pending disposition in this device's own Post outbox. */
204
+ async hasStagedDisposition(input, original, reply) {
205
+ const device = await current(input);
206
+ return await hostFor(device).hasStagedDisposition(device.deviceId, original, reply);
207
+ },
162
208
  /** One finite Agent operation on this device, returning its receipt. */
163
209
  async operation(input, caller, agentId, raw) {
164
210
  const device = await current(input);