run402 4.70.9 → 4.71.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 (34) hide show
  1. package/gitvault-surface.json +1 -1
  2. package/lib/cold-start.mjs +33 -7
  3. package/lib/command-manifest.mjs +8 -1
  4. package/lib/org-context.mjs +8 -2
  5. package/lib/rooms-context.mjs +14 -3
  6. package/lib/rooms.mjs +299 -2
  7. package/lib/sdk-errors.mjs +25 -0
  8. package/package.json +1 -1
  9. package/sdk/dist/namespaces/rooms.d.ts +53 -1
  10. package/sdk/dist/namespaces/rooms.d.ts.map +1 -1
  11. package/sdk/dist/namespaces/rooms.js +130 -0
  12. package/sdk/dist/namespaces/rooms.js.map +1 -1
  13. package/sdk/dist/namespaces/rooms.types.d.ts +94 -0
  14. package/sdk/dist/namespaces/rooms.types.d.ts.map +1 -1
  15. package/sdk/dist/node/bearer-claim-key.d.ts +126 -0
  16. package/sdk/dist/node/bearer-claim-key.d.ts.map +1 -0
  17. package/sdk/dist/node/bearer-claim-key.js +209 -0
  18. package/sdk/dist/node/bearer-claim-key.js.map +1 -0
  19. package/sdk/dist/node/gitvault-address.d.ts +19 -0
  20. package/sdk/dist/node/gitvault-address.d.ts.map +1 -1
  21. package/sdk/dist/node/gitvault-address.js +20 -0
  22. package/sdk/dist/node/gitvault-address.js.map +1 -1
  23. package/sdk/dist/node/gitvault-handoff.d.ts +3 -95
  24. package/sdk/dist/node/gitvault-handoff.d.ts.map +1 -1
  25. package/sdk/dist/node/gitvault-handoff.js +9 -122
  26. package/sdk/dist/node/gitvault-handoff.js.map +1 -1
  27. package/sdk/dist/node/index.d.ts +3 -1
  28. package/sdk/dist/node/index.d.ts.map +1 -1
  29. package/sdk/dist/node/index.js +8 -1
  30. package/sdk/dist/node/index.js.map +1 -1
  31. package/sdk/dist/node/paid-fetch.d.ts +8 -0
  32. package/sdk/dist/node/paid-fetch.d.ts.map +1 -1
  33. package/sdk/dist/node/paid-fetch.js +41 -1
  34. package/sdk/dist/node/paid-fetch.js.map +1 -1
@@ -1,5 +1,5 @@
1
1
  {
2
- "surface_version": "4.70.9",
2
+ "surface_version": "4.71.0",
3
3
  "verbs": [
4
4
  "repos create",
5
5
  "repos list",
@@ -14,6 +14,14 @@
14
14
  * untouched; this module does not replace it, only the one path `repos
15
15
  * create` needs when it discovers there is no tier and nothing else has
16
16
  * set one up yet.
17
+ *
18
+ * add-room-invite design D9 splits the funded-wallet half (allowance →
19
+ * faucet-if-empty → brief settlement poll) out as {@link ensureFundedWallet}
20
+ * — `rooms join <key>` needs exactly that half, with NO tier purchase and NO
21
+ * project creation (the claim itself is the one x402 payment that funds the
22
+ * onboarding). {@link foldColdStartChain} is now their composition —
23
+ * `ensureFundedWallet` followed by the tier step — so every EXISTING caller
24
+ * (`repos create`/`resume`/`join`) is unchanged.
17
25
  */
18
26
  import { readAllowance, saveAllowance } from "./config.mjs";
19
27
  import { getSdk } from "./sdk.mjs";
@@ -22,13 +30,16 @@ const USDC_ABI = [{ name: "balanceOf", type: "function", stateMutability: "view"
22
30
  const USDC_SEPOLIA = "0x036CbD53842c5426634e7929541eC2318f3dCF7e";
23
31
 
24
32
  /**
33
+ * Allowance → faucet-if-empty → brief settlement poll. NO tier purchase, no
34
+ * project creation — the caller decides what (if anything) to pay for next.
35
+ *
25
36
  * @param {(line: string) => void} announce Called once per step, so the
26
37
  * caller can print each one it took (client-surface spec: "announcing
27
38
  * each step").
28
- * @returns {Promise<{allowance_created: boolean, faucet_requested: boolean, tier: object|null}>}
39
+ * @returns {Promise<{allowance_created: boolean, faucet_requested: boolean, address: string}>}
29
40
  */
30
- export async function foldColdStartChain(announce = () => {}) {
31
- const out = { allowance_created: false, faucet_requested: false, tier: null };
41
+ export async function ensureFundedWallet(announce = () => {}) {
42
+ const out = { allowance_created: false, faucet_requested: false, address: "" };
32
43
 
33
44
  let allowance = readAllowance();
34
45
  if (!allowance) {
@@ -40,6 +51,7 @@ export async function foldColdStartChain(announce = () => {}) {
40
51
  out.allowance_created = true;
41
52
  announce(`allowance created: ${allowance.address}`);
42
53
  }
54
+ out.address = allowance.address;
43
55
 
44
56
  const { createPublicClient, http } = await import("viem");
45
57
  const { baseSepolia } = await import("viem/chains");
@@ -48,7 +60,7 @@ export async function foldColdStartChain(announce = () => {}) {
48
60
  try {
49
61
  balance = Number(await client.readContract({ address: USDC_SEPOLIA, abi: USDC_ABI, functionName: "balanceOf", args: [allowance.address] }));
50
62
  } catch {
51
- /* an RPC hiccup here is not fatal — the tier purchase below will surface a real payment failure if the balance really is zero */
63
+ /* an RPC hiccup here is not fatal — the payment below will surface a real failure if the balance really is zero */
52
64
  }
53
65
  if (balance === 0) {
54
66
  announce("balance is 0 — requesting the testnet faucet");
@@ -70,8 +82,22 @@ export async function foldColdStartChain(announce = () => {}) {
70
82
  saveAllowance({ ...allowance, funded: true, lastFaucet: new Date().toISOString() });
71
83
  }
72
84
 
73
- announce("subscribing to the prototype tier (one x402 testnet payment, perpetual)");
74
- out.tier = await getSdk().tier.set("prototype");
75
- announce(`prototype tier active${out.tier?.status === "already_active" ? " (already active)" : ""}`);
76
85
  return out;
77
86
  }
87
+
88
+ /**
89
+ * `ensureFundedWallet` + the tier step. Unchanged from before the split —
90
+ * every existing caller (`repos create`/`resume`/`join`) keeps working with
91
+ * no edits.
92
+ *
93
+ * @param {(line: string) => void} announce Called once per step.
94
+ * @returns {Promise<{allowance_created: boolean, faucet_requested: boolean, tier: object|null}>}
95
+ */
96
+ export async function foldColdStartChain(announce = () => {}) {
97
+ const funded = await ensureFundedWallet(announce);
98
+
99
+ announce("subscribing to the prototype tier (one x402 testnet payment, perpetual)");
100
+ const tier = await getSdk().tier.set("prototype");
101
+ announce(`prototype tier active${tier?.status === "already_active" ? " (already active)" : ""}`);
102
+ return { allowance_created: funded.allowance_created, faucet_requested: funded.faucet_requested, tier };
103
+ }
@@ -257,7 +257,14 @@ export const COMMAND_MANIFEST = [
257
257
  { path: ["subscriptions", "add"], positionals: [], projectScoped: false, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub" },
258
258
  { path: ["subscriptions", "list"], positionals: [], projectScoped: false, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub" },
259
259
  { path: ["subscriptions", "rm"], positionals: [p("subscription_id")], projectScoped: false, legacyPositionalProject: false, minimalArgs: ["r_1"], runStyle: "sub" },
260
- { path: ["rooms", "join"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub" },
260
+ // add-room-invite design D9: `rooms join` is one verb, two forms no
261
+ // positional registers a presence (the behaviorally-tested path, unchanged
262
+ // by the optional positional below since minimalArgs stays empty); a
263
+ // `kri1_…` positional claims a room seat first (a real x402 payment + an
264
+ // org-membership mutation), so that form is never behaviorally exercised
265
+ // here — the same reasoning `repos join`'s own row states.
266
+ { path: ["rooms", "join"], positionals: [p("key", { required: false })], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub" },
267
+ { path: ["rooms", "invite"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub", skipBehavioral: "mints a single-use bearer key from a live room, registering the inviter's presence and posting a real room message — never run against the gate's own checkout" },
261
268
  { path: ["rooms", "leave"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub" },
262
269
  { path: ["messages", "send"], positionals: [p("body")], projectScoped: true, legacyPositionalProject: false, minimalArgs: ["hello"], runStyle: "sub" },
263
270
  { path: ["messages", "list"], positionals: [], projectScoped: true, legacyPositionalProject: false, minimalArgs: [], runStyle: "sub" },
@@ -98,13 +98,19 @@ function orgFromRoomEnv(env) {
98
98
  * best-effort, gracefully degrading like `findBindingKey`: no repository,
99
99
  * no pin, or a shape-invalid value all answer `null` rather than throwing,
100
100
  * so a bare directory or a checkout with no gitvault remote costs nothing.
101
+ * Checks a real vault checkout's pin first (gated on `r402.repoId`), then
102
+ * falls back to the bare org/room pin a room-only `rooms join <key>` writes
103
+ * in a directory with no vault at all (add-room-invite design D10).
101
104
  */
102
105
  async function readGitvaultPinnedOrgId(cwd) {
103
106
  try {
104
- const { readPinnedGitvaultRepo } = await import("#sdk/node");
107
+ const { readPinnedGitvaultRepo, readPinnedRoomBinding } = await import("#sdk/node");
105
108
  const pinned = await readPinnedGitvaultRepo(cwd);
106
109
  const orgId = trimmed(pinned?.org_id);
107
- return orgId && ORG_ID_RE.test(orgId) ? orgId : null;
110
+ if (orgId && ORG_ID_RE.test(orgId)) return orgId;
111
+ const bare = await readPinnedRoomBinding(cwd);
112
+ const bareOrgId = trimmed(bare?.org_id);
113
+ return bareOrgId && ORG_ID_RE.test(bareOrgId) ? bareOrgId : null;
108
114
  } catch {
109
115
  return null;
110
116
  }
@@ -36,13 +36,24 @@ import { describeRejectedValue } from "../core-dist/redact.js";
36
36
  export const ROOM_ENV = "RUN402_ROOM";
37
37
  export const PRESENCE_ENV = "RUN402_PRESENCE_ID";
38
38
 
39
- /** `r402.room` from `cwd`'s LOCAL git config (kygit-handoff design D10) — best-effort, `null` on any absence or failure. */
39
+ /**
40
+ * `r402.room` from `cwd`'s LOCAL git config (kygit-handoff design D10) —
41
+ * best-effort, `null` on any absence or failure. Checks a real vault
42
+ * checkout's pin first (`readPinnedGitvaultRepo`, gated on `r402.repoId`
43
+ * being present), then falls back to the bare room/org pin `rooms join
44
+ * <key>` writes in a directory with NO vault at all (add-room-invite design
45
+ * D10) — `readPinnedGitvaultRepo` would otherwise see that pin as absent,
46
+ * since it never sets `r402.repoId`.
47
+ */
40
48
  async function readGitvaultPinnedRoom(cwd) {
41
49
  try {
42
- const { readPinnedGitvaultRepo } = await import("#sdk/node");
50
+ const { readPinnedGitvaultRepo, readPinnedRoomBinding } = await import("#sdk/node");
43
51
  const pinned = await readPinnedGitvaultRepo(cwd);
44
52
  const room = typeof pinned?.room === "string" ? pinned.room.trim() : "";
45
- return room.length > 0 ? room : null;
53
+ if (room.length > 0) return room;
54
+ const bare = await readPinnedRoomBinding(cwd);
55
+ const bareRoom = typeof bare?.room === "string" ? bare.room.trim() : "";
56
+ return bareRoom.length > 0 ? bareRoom : null;
46
57
  } catch {
47
58
  return null;
48
59
  }
package/lib/rooms.mjs CHANGED
@@ -10,6 +10,7 @@
10
10
  * Gateway subsystem: add-agent-messaging (/orgs/v1/:org_id/rooms/:room_key/*).
11
11
  * Session presence cache: ./.run402/messaging.json (gitignore).
12
12
  */
13
+ import { readFileSync } from "node:fs";
13
14
  import { getSdk } from "./sdk.mjs";
14
15
  import { fail, reportSdkError } from "./sdk-errors.mjs";
15
16
  import {
@@ -22,6 +23,7 @@ import {
22
23
  positionalArgs,
23
24
  requirePositionalCount,
24
25
  failUnknownSubcommand,
26
+ validateRegularFile,
25
27
  } from "./argparse.mjs";
26
28
  import {
27
29
  resolveRoom,
@@ -29,19 +31,23 @@ import {
29
31
  withPresenceRetry,
30
32
  registerFreshPresence,
31
33
  rememberPresence,
32
- getRoomState,
33
34
  updateRoomState,
34
35
  } from "./rooms-context.mjs";
35
36
  import { resolveTaskLabel } from "./harness-context.mjs";
37
+ import { ensureFundedWallet } from "./cold-start.mjs";
36
38
 
37
39
  export const IMPORTANCE = ["normal", "high"];
38
40
 
39
41
  const ROOM_FLAGS = ["--project", "--org", "--room"];
42
+ const INVITE_VALUE_FLAGS = [...ROOM_FLAGS, "--note", "--note-file", "--expires-in"];
40
43
 
41
44
  const HELP = `run402 rooms — arrive in a room, see who is live, leave when done
42
45
 
43
46
  Usage:
44
47
  run402 rooms join [--name <name>] [--task <text>]
48
+ run402 rooms join <kri1_…> [--json]
49
+ run402 rooms invite [--note <text> | --note-file <path> | stdin]
50
+ [--room <key>] [--expires-in <seconds>] [--json]
45
51
  run402 rooms leave [<presence_id>]
46
52
 
47
53
  Addressing:
@@ -50,6 +56,27 @@ Addressing:
50
56
  (omit both) Resolved from RUN402_ROOM, a .run402.json binding, or the
51
57
  wallet profile's selected org
52
58
 
59
+ Room Invite (mint a key from the room you stand in, join through one):
60
+ - \`rooms invite\` mints a single-use \`kri1_…\` bearer key. Whoever claims it
61
+ FIRST becomes a permanent \`viewer\` of this org — the narrowest membership
62
+ that can message, and NEVER wider: there is no --role, and a viewer can
63
+ never be auto-admitted as a vault writer. To bring a collaborator into the
64
+ CODE (a vault, a checkpoint, write access), use \`run402 repos invite\`
65
+ instead — this door is talk-only.
66
+ - The key is printed to stdout EXACTLY ONCE (\`--json\` still keeps it out of
67
+ stderr). It is not recoverable if lost — mint a new one.
68
+ - \`rooms join <kri1_…>\` folds a funded-wallet chain (allowance → faucet if
69
+ empty → briefly wait for settlement) and pays a $0.01 testnet seat via
70
+ x402 to claim it — the payment IS the join, so a joiner with no funds
71
+ fails closed rather than joining unpaid. No tier is purchased, no project
72
+ is created. A same-payer replay never pays twice.
73
+ - After a key-form join: the host org becomes this wallet's current org,
74
+ and the binding is written where the next \`run402 messages wait\` reads
75
+ it from — \`.run402.json\` in a plain directory, or local git config
76
+ (\`r402.orgId\`/\`r402.room\`, excluded from git via .git/info/exclude) when
77
+ standing inside a git repository (never a .run402.json committed into a
78
+ stranger's clone).
79
+
53
80
  Notes:
54
81
  - join registers this session's presence and returns who else is live, what
55
82
  they are working on, and what they have claimed — the arrive-and-look call.
@@ -194,6 +221,261 @@ async function leave(argv) {
194
221
  }
195
222
  }
196
223
 
224
+ async function readStdinTextLocal() {
225
+ const chunks = [];
226
+ for await (const chunk of process.stdin) {
227
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
228
+ }
229
+ return Buffer.concat(chunks).toString("utf-8");
230
+ }
231
+
232
+ /**
233
+ * `rooms invite`'s note is a PLAIN STRING, ≤4 KiB (design D7) — not the
234
+ * structured `kygit.invite-note.v1` JSON `run402 repos invite` reads: the
235
+ * gateway already reads every room message in plaintext, so there is
236
+ * nothing to seal and nothing to schema-validate beyond a length cap.
237
+ * Optional — `undefined` when nothing was supplied and stdin is a TTY.
238
+ */
239
+ async function readRoomInviteNote(a) {
240
+ const inline = flagValue(a, "--note");
241
+ const noteFile = flagValue(a, "--note-file");
242
+ if (inline != null && noteFile != null) {
243
+ fail({ code: "BAD_USAGE", message: "Pass either --note or --note-file, not both.", hint: "run402 rooms invite --help" });
244
+ }
245
+ if (inline != null) return inline;
246
+ if (noteFile != null) {
247
+ validateRegularFile(noteFile, "--note-file");
248
+ return readFileSync(noteFile, "utf-8");
249
+ }
250
+ if (process.stdin?.isTTY) return undefined; // no note source given — the note is optional
251
+ const text = await readStdinTextLocal();
252
+ return text.trim().length > 0 ? text : undefined;
253
+ }
254
+
255
+ /**
256
+ * `run402 rooms invite` (add-room-invite design D7) — mint from the room
257
+ * this checkout stands in: register-or-resume the inviter's OWN presence
258
+ * FIRST (reusing {@link ensurePresence}, the same logic `rooms join`'s
259
+ * no-key form already runs) so the row carries `inviter_presence_id`, mint,
260
+ * post ONE room fact naming the invite id (never the key), echo the
261
+ * gateway's blast-radius warning to stderr, and print the `kri1_` key to
262
+ * stdout EXACTLY ONCE. A presence or fact failure is reported on the result
263
+ * and never voids the mint.
264
+ */
265
+ async function invite(argv) {
266
+ const a = normalizeArgv(argv);
267
+ assertKnownFlags(a, [...INVITE_VALUE_FLAGS, "--json", "--help", "-h"], INVITE_VALUE_FLAGS);
268
+ requirePositionalCount(positionalArgs(a, INVITE_VALUE_FLAGS), INVITE_VALUE_FLAGS, {
269
+ min: 0, max: 0, command: "run402 rooms invite",
270
+ });
271
+
272
+ const room = await resolveRoom({
273
+ org: flagValue(a, "--org"), room: flagValue(a, "--room"), project: flagValue(a, "--project"),
274
+ });
275
+ const note = await readRoomInviteNote(a);
276
+ const expiresRaw = flagValue(a, "--expires-in");
277
+ const expiresInSeconds = expiresRaw != null ? parseIntegerFlag("--expires-in", expiresRaw, { min: 60, max: 86400 }) : undefined;
278
+ const asJson = a.includes("--json");
279
+
280
+ const sdk = getSdk();
281
+ try {
282
+ const { task } = await resolveTaskLabel({});
283
+ // design D7: register (or resume) the inviter's OWN presence BEFORE
284
+ // minting, so the row carries `inviter_presence_id`. A failure here is
285
+ // reported, never thrown — the mint proceeds without one.
286
+ let inviterPresence = null;
287
+ let inviterPresenceReport = { registered: false };
288
+ try {
289
+ const presence = await ensurePresence(room, { task });
290
+ inviterPresence = presence;
291
+ inviterPresenceReport = { registered: true, presence_id: presence.presence_id, name: presence.name };
292
+ } catch (e) {
293
+ inviterPresenceReport = { registered: false, error: e instanceof Error ? e.message : String(e) };
294
+ }
295
+
296
+ const result = await sdk.rooms.invite(room.orgId, room.roomKey, {
297
+ ...(note !== undefined ? { note } : {}),
298
+ ...(inviterPresence ? { inviterPresenceId: inviterPresence.presence_id } : {}),
299
+ ...(expiresInSeconds != null ? { expiresInSeconds } : {}),
300
+ });
301
+
302
+ for (const w of result.warnings ?? []) {
303
+ console.error(w.message ?? `${w.code}`);
304
+ }
305
+ console.error(`invite minted: role ${result.role}, expires ${result.expires_at}, room ${result.room?.room_key ?? room.roomKey}`);
306
+ console.error("recipient runs: run402 rooms join <key printed below>");
307
+
308
+ // design D7: post ONE room fact AFTER the mint succeeds — never before
309
+ // (a mint refusal must leave no orphan message), and never naming the
310
+ // key, only the invite id.
311
+ let roomFact = { posted: false, reason: "inviter presence was not registered" };
312
+ if (inviterPresence) {
313
+ const inviteShort = result.invite_id.slice(0, 8);
314
+ try {
315
+ const sent = await sdk.rooms.sendMessage(room.orgId, room.roomKey, {
316
+ body: `Invited another agent to this room (invite ${inviteShort}, expires ${result.expires_at}).`,
317
+ presenceId: inviterPresence.presence_id,
318
+ idempotencyKey: `room-invite:${result.invite_id}:minted`,
319
+ });
320
+ roomFact = { posted: true, message_id: sent.message_id, cursor: sent.cursor };
321
+ // The inviter's own fact must not wake the inviter's next `messages
322
+ // wait` — advance this checkout's stored cursor past it (best-effort).
323
+ try { updateRoomState(room.orgId, room.roomKey, { cursor: sent.cursor }); } catch { /* never fails a mint */ }
324
+ } catch (e) {
325
+ roomFact = { posted: false, reason: e instanceof Error ? e.message : String(e) };
326
+ }
327
+ }
328
+ if (inviterPresenceReport.registered === false) {
329
+ console.error(`note: your own presence was not registered (${inviterPresenceReport.error}) — the invite still mints and is claimable`);
330
+ }
331
+ if (roomFact.posted === false && inviterPresence) {
332
+ console.error(`note: the room fact was not posted (${roomFact.reason}) — the invite still mints and remains claimable`);
333
+ }
334
+
335
+ const finalResult = { ...result, inviter_presence: inviterPresenceReport, room_fact: roomFact };
336
+ if (asJson) {
337
+ printInviteResultJson(finalResult);
338
+ } else {
339
+ printInviteResultKeyOnly(result);
340
+ }
341
+ } catch (err) {
342
+ reportSdkError(err);
343
+ }
344
+ }
345
+
346
+ /** The key rides the JSON result — still stdout, still exactly once. */
347
+ function printInviteResultJson(finalResult) {
348
+ console.log(JSON.stringify(finalResult, null, 2));
349
+ }
350
+
351
+ /** The key alone, so `KEY=$(run402 rooms invite ...)` works — everything else (the warning, the mint summary) is on stderr. */
352
+ function printInviteResultKeyOnly(result) {
353
+ console.log(result.key);
354
+ }
355
+
356
+ /**
357
+ * `run402 rooms join <kri1_…>` (add-room-invite design D9/D10) — parse the
358
+ * key CLIENT-SIDE first (a wrong-kind vault key refuses by name before ANY
359
+ * network call, including the faucet), fold `ensureFundedWallet` (allowance
360
+ * → faucet-if-empty → brief settlement poll, announced on stderr), claim
361
+ * through the SDK's paid fetch, then leave arrival state exactly where
362
+ * `run402 messages wait` reads it: the host org as this wallet's current
363
+ * org; the binding written to `.run402.json` outside a git repository, or
364
+ * pinned in local git config (and `.run402/` excluded from git) inside one;
365
+ * the returned cursor persisted. There is no `--no-init` — the payment IS
366
+ * the claim.
367
+ */
368
+ async function joinWithKey(key, a) {
369
+ const asJson = a.includes("--json");
370
+
371
+ // Parse-only pre-check (design D9): refuses a `kgh1_`/`kgi1_` vault key by
372
+ // name, synchronously, before `ensureFundedWallet` ever touches the
373
+ // network — the gateway (faucet included) must never be contacted for a
374
+ // wrong-kind key.
375
+ const { parseRoomInviteKey } = await import("#sdk/node");
376
+ try {
377
+ parseRoomInviteKey(key);
378
+ } catch (err) {
379
+ reportSdkError(err);
380
+ return;
381
+ }
382
+
383
+ try {
384
+ await ensureFundedWallet((line) => console.error(line));
385
+ const result = await getSdk().rooms.join(key);
386
+
387
+ // Arrival state (design D10) — best-effort throughout: the claim already
388
+ // succeeded, and none of this may fail a completed join.
389
+ try {
390
+ const { setSelectedOrgId } = await import("./org-context.mjs");
391
+ setSelectedOrgId(result.org_id);
392
+ } catch { /* best-effort */ }
393
+
394
+ const cwd = process.cwd();
395
+ let insideGitRepo = false;
396
+ try {
397
+ const { hardenedGit } = await import("#sdk/node");
398
+ await hardenedGit(cwd, ["rev-parse", "--git-dir"]);
399
+ insideGitRepo = true;
400
+ } catch {
401
+ insideGitRepo = false;
402
+ }
403
+ if (!insideGitRepo) {
404
+ try {
405
+ const { updateBindingFile } = await import("./wallet-context.mjs");
406
+ updateBindingFile(cwd, { org: result.org_id, room: result.room.room_key });
407
+ } catch { /* best-effort */ }
408
+ } else {
409
+ try {
410
+ const { pinRoomBinding, excludeMessagingCacheFromGit } = await import("#sdk/node");
411
+ await pinRoomBinding(cwd, { org_id: result.org_id, room_key: result.room.room_key });
412
+ await excludeMessagingCacheFromGit(cwd);
413
+ } catch { /* best-effort */ }
414
+ }
415
+ if (typeof result.cursor === "string") {
416
+ try { updateRoomState(result.org_id, result.room.room_key, { cursor: result.cursor }); } catch { /* best-effort */ }
417
+ }
418
+ // Live-proof defect A: cache the presence the CLAIM ITSELF registered,
419
+ // exactly the way `registerFreshPresence`'s no-key `join` already does
420
+ // via `rememberPresence` — without this, the joiner's very next
421
+ // coordination call (no cached presence_id) registers a SECOND presence
422
+ // for the same arrival. Guarded for an older gateway that predates the
423
+ // `presence` field: `rememberPresence` itself no-ops on anything that
424
+ // isn't `{presence_id: string, ...}`, so this never throws either way.
425
+ try { rememberPresence(result.org_id, result.room.room_key, result.presence); } catch { /* best-effort */ }
426
+
427
+ const nextActions = [...(result.next_actions ?? [])];
428
+ if (!nextActions.some((na) => na.type === "wait_room")) {
429
+ nextActions.push({ type: "wait_room", command: "run402 messages wait", why: "Block until the other agent speaks; silence returns who is still here." });
430
+ }
431
+
432
+ if (asJson) {
433
+ printClaimResultJson(result, nextActions);
434
+ } else {
435
+ renderClaimResultText(result, nextActions);
436
+ }
437
+ } catch (err) {
438
+ reportSdkError(err);
439
+ }
440
+ }
441
+
442
+ function printClaimResultJson(result, nextActions) {
443
+ console.log(JSON.stringify({ ...result, next_actions: nextActions }, null, 2));
444
+ }
445
+
446
+ function renderClaimResultText(result, nextActions) {
447
+ // The note is plain text, not a structured schema — printed verbatim as
448
+ // Markdown (it may already contain Markdown formatting the inviter wrote).
449
+ if (result.note) {
450
+ console.log(result.note);
451
+ console.error("");
452
+ }
453
+ console.error(`joined org ${result.org_id}, room ${result.room.room_key} — role ${result.membership.role}`);
454
+ if (result.deduplicated) {
455
+ console.error("note: this key was already claimed by this same payer — no second payment was made (safe replay)");
456
+ }
457
+ console.error(`seat: $${(result.seat.amount_usd_micros / 1_000_000).toFixed(2)} on ${result.seat.network}${result.seat.charge_id ? ` (charge ${result.seat.charge_id})` : ""}`);
458
+ if (result.inviter) {
459
+ const labels = [result.inviter.program, result.inviter.model].filter(Boolean).join("/");
460
+ const liveness = result.inviter.state === "active" ? "live" : result.inviter.state;
461
+ console.error(`invited by ${result.inviter.name}${labels ? ` (${labels})` : ""} — ${liveness}`);
462
+ } else {
463
+ console.error("invited by: unknown (the inviter never registered a presence)");
464
+ }
465
+ const others = (result.live_presences ?? []);
466
+ if (others.length > 0) console.error(`also live: ${others.map((p) => p.name).join(", ")}`);
467
+ const recent = result.recent_messages ?? [];
468
+ if (recent.length > 0) {
469
+ console.error(`recent messages (${recent.length}):`);
470
+ for (const m of recent.slice().reverse()) {
471
+ console.error(` ${m.sender ?? "?"}: ${m.body_snippet ?? m.body ?? ""}`);
472
+ }
473
+ }
474
+ for (const na of nextActions) {
475
+ if (na.command) console.error(`next: ${na.command}${na.why ? ` — ${na.why}` : ""}`);
476
+ }
477
+ }
478
+
197
479
  export async function run(sub, args) {
198
480
  const argv = Array.isArray(args) ? args : [];
199
481
  if (!sub || hasHelp([sub, ...argv])) {
@@ -202,7 +484,22 @@ export async function run(sub, args) {
202
484
  }
203
485
  switch (sub) {
204
486
  case "join": {
205
- await who(argv);
487
+ // `run402 rooms join <kri1_…>` (add-room-invite design D9): a
488
+ // positional key form claims a seat and arrives; no positional keeps
489
+ // the existing arrive-and-look behavior unchanged.
490
+ const a = normalizeArgv(argv);
491
+ const positionals = positionalArgs(a, ["--name", "--task", ...ROOM_FLAGS]);
492
+ if (positionals.length === 0) {
493
+ await who(argv);
494
+ break;
495
+ }
496
+ assertKnownFlags(a, ["--json", "--help", "-h"], []);
497
+ requirePositionalCount(positionals, [], { min: 1, max: 1, command: "run402 rooms join [<kri1_…>]" });
498
+ await joinWithKey(positionals[0], a);
499
+ break;
500
+ }
501
+ case "invite": {
502
+ await invite(argv);
206
503
  break;
207
504
  }
208
505
  case "leave": {
@@ -63,6 +63,20 @@ export function parseFlagJson(name, value) {
63
63
  }
64
64
  }
65
65
 
66
+ // Live-proof defect B — the gateway's own terminal room-invite claim
67
+ // refusals (never settled; see `sdk/src/namespaces/rooms.ts`'s
68
+ // `ROOM_INVITE_TERMINAL_REFUSAL_CODES`, the source of truth this mirrors).
69
+ // A small, deliberate duplication rather than an import, so this
70
+ // dependency-free error reporter stays that way — these five strings are
71
+ // gateway-owned and stable.
72
+ const ROOM_INVITE_TERMINAL_REFUSAL_CODES = new Set([
73
+ "ROOM_INVITE_KEY_INVALID",
74
+ "ROOM_INVITE_KEY_EXPIRED",
75
+ "ROOM_INVITE_KEY_REVOKED",
76
+ "ROOM_INVITE_KEY_ALREADY_CLAIMED",
77
+ "ROOM_INVITE_CLAIM_REQUIRES_WALLET",
78
+ ]);
79
+
66
80
  export function reportSdkError(err) {
67
81
  if (err?.name === "ProjectCredentialNotFound" || err?.code === "PROJECT_CREDENTIAL_NOT_FOUND") {
68
82
  fail({
@@ -137,6 +151,17 @@ export function reportSdkError(err) {
137
151
  }
138
152
  }
139
153
 
154
+ // Live-proof defect B: a terminal room-invite claim refusal is the
155
+ // gateway's OWN typed envelope by the time it reaches here (the SDK's
156
+ // default paid fetch classifies these five codes as `"failed"`, never
157
+ // `"ambiguous"` — see `sdk/src/node/paid-fetch.ts` and
158
+ // `isTerminalRoomInviteRefusal`), so the truthful, reassuring fact —
159
+ // no payment was charged — is worth stating explicitly rather than
160
+ // leaving the caller to infer it from `mutation_state`.
161
+ if (ROOM_INVITE_TERMINAL_REFUSAL_CODES.has(payload.code) && payload.hint === undefined) {
162
+ payload.hint = "This key was not claimable (already used, expired, revoked, or this route requires a wallet, not a session) — you were not charged; no payment was made for this attempt.";
163
+ }
164
+
140
165
  // Keep `status: "error"` as the outer envelope even if the response body
141
166
  // happened to contain its own `status` field (e.g. `{"status":"degraded"}`
142
167
  // from /health 503 responses). Downstream scripts match on this sentinel.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "run402",
3
- "version": "4.70.9",
3
+ "version": "4.71.0",
4
4
  "description": "CLI for Run402 — full-stack backend infrastructure for AI agents: Postgres, auth, storage, serverless functions and atomic deploys. Paid with x402/MPP. Includes $0.03 image generation.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -29,7 +29,7 @@
29
29
  * keep coordinating.
30
30
  */
31
31
  import type { Client } from "../kernel.js";
32
- import type { AckRoomMessageOptions, CreatedRoomClaim, CreateRoomClaimInput, ListPresencesOptions, ListRoomClaimsOptions, ListRoomMessagesOptions, PresenceRegistration, RegisterPresenceOptions, RoomAckResult, RoomClaimList, RoomClaimReleaseResult, RoomMessage, RoomLeaveResult, RoomList, RoomMessagePage, RoomMessageWaitResult, RoomPresence, RoomPresenceList, RoomSummary, SendRoomMessageInput, SentRoomMessage, WaitForRoomMessagesOptions } from "./rooms.types.js";
32
+ import type { AckRoomMessageOptions, CreatedRoomClaim, CreateRoomClaimInput, ListPresencesOptions, ListRoomClaimsOptions, ListRoomMessagesOptions, PresenceRegistration, RegisterPresenceOptions, RoomAckResult, RoomClaimList, RoomClaimReleaseResult, RoomInviteJoinResult, RoomInviteMintOptions, RoomInviteMintResult, RoomMessage, RoomLeaveResult, RoomList, RoomMessagePage, RoomMessageWaitResult, RoomPresence, RoomPresenceList, RoomSummary, SendRoomMessageInput, SentRoomMessage, WaitForRoomMessagesOptions } from "./rooms.types.js";
33
33
  export declare class Rooms {
34
34
  private readonly client;
35
35
  constructor(client: Client);
@@ -172,6 +172,36 @@ export declare class Rooms {
172
172
  * `already_released: true` with the original time.
173
173
  */
174
174
  releaseClaim(orgId: string, roomKey: string, claimId: string): Promise<RoomClaimReleaseResult>;
175
+ /**
176
+ * Mint a Room Invite Key from the room the caller stands in
177
+ * (`POST /orgs/v1/:org_id/rooms/:room_key/invites`) — a single-use bearer
178
+ * key (`kri1_…`) whose claimant becomes a permanent `viewer` of the org,
179
+ * the narrowest membership that can message (design D4: never `--role`,
180
+ * never wider, never auto-admitted as a vault writer). Requires
181
+ * `developer`+ (session, wallet, or admin credential — a delegate is
182
+ * refused, since a room invite confers org membership).
183
+ *
184
+ * `invite_id` and `master_secret` are generated LOCALLY (design D3): the
185
+ * gateway never sees `master_secret`, only the SHA-256 `auth_hash` this
186
+ * call derives and sends. The assembled key is returned exactly ONCE —
187
+ * nothing here or downstream persists it.
188
+ */
189
+ invite(orgId: string, roomKey: string, opts?: RoomInviteMintOptions): Promise<RoomInviteMintResult>;
190
+ /**
191
+ * Claim a Room Invite Key (`POST /rooms/v1/invites/:invite_id/claim`) —
192
+ * parses the key CLIENT-SIDE first, refusing a `kgh1_`/`kgi1_` vault key
193
+ * BY NAME (pointing at `run402 repos resume`/`run402 repos join`) before
194
+ * any network call (design D3). The claim is an x402-PAID resource (the
195
+ * `room_seat` SKU, testnet only): the VERIFIED PAYER of that payment
196
+ * becomes the claimant, so this call is sent through the client's paid
197
+ * fetch WITHOUT a bearer credential (`withAuth: false`) — no
198
+ * `SIGN-IN-WITH-X` header, and any cached control-plane session is
199
+ * deliberately not attached, exactly matching the gateway's own
200
+ * `403 ROOM_INVITE_CLAIM_REQUIRES_WALLET` refusal for a bearer-credentialed
201
+ * request at this route. A same-payer replay never pays twice
202
+ * (`deduplicated: true`, no second charge).
203
+ */
204
+ join(key: string): Promise<RoomInviteJoinResult>;
175
205
  /**
176
206
  * Return a room-scoped sub-client with `(orgId, roomKey)` pre-bound.
177
207
  * Synchronous — both ids are explicit. For a project's default room
@@ -187,6 +217,26 @@ export declare class Rooms {
187
217
  */
188
218
  forProject(projectId: string): Promise<ScopedRoom>;
189
219
  }
220
+ /**
221
+ * Terminal room-invite claim refusals the gateway's own x402 paywall NEVER
222
+ * settles for (design D5 — the paywall buffers and settles only on a
223
+ * sub-400 response, so any of these five codes means no payment ever
224
+ * completed, refunded or otherwise). Live-proof defect B: without this, a
225
+ * spent/expired/revoked key, or a bearer credential presented to the claim
226
+ * route, surfaced as a generic `X402_PAYMENT_OUTCOME_AMBIGUOUS` — alarming
227
+ * and wrong, since the gateway had already answered with one of these and
228
+ * moved no funds. `node/paid-fetch.ts`'s default paid fetch recognizes a
229
+ * response carrying one of these codes, FROM this SDK's own configured API
230
+ * origin, as `"failed"` rather than `"ambiguous"` — letting the gateway's
231
+ * own typed envelope (not a synthesized payment-attempt error) reach the
232
+ * caller. Scoped by CODE, never by route: an arbitrary third-party paid URL
233
+ * — even one that echoes one of these exact strings — is never treated as
234
+ * this SDK's own origin, so its non-2xx after dispatch stays genuinely
235
+ * ambiguous, unchanged.
236
+ */
237
+ export declare const ROOM_INVITE_TERMINAL_REFUSAL_CODES: ReadonlySet<string>;
238
+ /** True when `envelope.code` is one of {@link ROOM_INVITE_TERMINAL_REFUSAL_CODES}. */
239
+ export declare function isTerminalRoomInviteRefusal(envelope: Record<string, unknown> | null | undefined): boolean;
190
240
  /**
191
241
  * A room-scoped sub-client returned by {@link Rooms.scoped} /
192
242
  * {@link Rooms.forProject}. The `(orgId, roomKey)` pair is bound at
@@ -225,5 +275,7 @@ export declare class ScopedRoom {
225
275
  listClaims(opts?: ListRoomClaimsOptions): Promise<RoomClaimList>;
226
276
  /** See {@link Rooms.releaseClaim}. */
227
277
  releaseClaim(claimId: string): Promise<RoomClaimReleaseResult>;
278
+ /** See {@link Rooms.invite} — pre-bound to this room. */
279
+ invite(opts?: RoomInviteMintOptions): Promise<RoomInviteMintResult>;
228
280
  }
229
281
  //# sourceMappingURL=rooms.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rooms.d.ts","sourceRoot":"","sources":["../../src/namespaces/rooms.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,OAAO,KAAK,EACV,qBAAqB,EACrB,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,uBAAuB,EACvB,oBAAoB,EACpB,uBAAuB,EACvB,aAAa,EACb,aAAa,EACb,sBAAsB,EACtB,WAAW,EACX,eAAe,EACf,QAAQ,EACR,eAAe,EACf,qBAAqB,EACrB,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACX,oBAAoB,EACpB,eAAe,EACf,0BAA0B,EAC3B,MAAM,kBAAkB,CAAC;AAqC1B,qBAAa,KAAK;IACJ,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM;IAE3C;;;;;;;;;;OAUG;IACG,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAS5C;;;;;;;;OAQG;IACG,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAU/D;;;;;;;;;;;;;;;;OAgBG;IACG,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAgBzF;;;;;;;;;OASG;IACG,gBAAgB,CACpB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,uBAA4B,GACjC,OAAO,CAAC,oBAAoB,CAAC;IAoBhC;;;;;OAKG;IACG,aAAa,CACjB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,oBAAyB,GAC9B,OAAO,CAAC,gBAAgB,CAAC;IAa5B;;;OAGG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAgB5F;;;;;;;;;;;OAWG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC;IA8BxG;;;;;;;;OAQG;IACG,YAAY,CAChB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,uBAA4B,GACjC,OAAO,CAAC,eAAe,CAAC;IAa3B;;;;;;;;;;;;;;;OAeG;IACG,eAAe,CACnB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,0BAA+B,GACpC,OAAO,CAAC,qBAAqB,CAAC;IAqEjC;;;OAGG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAgBzF;;;;;OAKG;IACG,UAAU,CACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,qBAA0B,GAC/B,OAAO,CAAC,aAAa,CAAC;IAmBzB;;;;;;;;OAQG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAyBzG;;;;;OAKG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,aAAa,CAAC;IAa1G;;;;;OAKG;IACG,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAgBpG;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU;IAIlD;;;;;;OAMG;IACG,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;CAiBzD;AAED;;;;GAIG;AACH,qBAAa,UAAU;IAMT,OAAO,CAAC,QAAQ,CAAC,KAAK;IALlC,yDAAyD;IACzD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6FAA6F;IAC7F,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEI,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAWzE,6BAA6B;IAC7B,GAAG,IAAI,OAAO,CAAC,WAAW,CAAC;IAI3B,+BAA+B;IAC/B,KAAK,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAInD,0CAA0C;IAC1C,gBAAgB,CAAC,IAAI,GAAE,uBAA4B,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAInF,uCAAuC;IACvC,aAAa,CAAC,IAAI,GAAE,oBAAyB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAIzE,qCAAqC;IACrC,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAItD,qCAAqC;IACrC,WAAW,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC;IAIlE,sCAAsC;IACtC,YAAY,CAAC,IAAI,GAAE,uBAA4B,GAAG,OAAO,CAAC,eAAe,CAAC;IAI1E,oCAAoC;IACpC,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAInD,yCAAyC;IACzC,eAAe,CAAC,IAAI,GAAE,0BAA+B,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAItF,oCAAoC;IACpC,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,aAAa,CAAC;IAIvF,qCAAqC;IACrC,WAAW,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAInE,oCAAoC;IACpC,UAAU,CAAC,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,aAAa,CAAC;IAIpE,sCAAsC;IACtC,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC;CAG/D"}
1
+ {"version":3,"file":"rooms.d.ts","sourceRoot":"","sources":["../../src/namespaces/rooms.ts"],"names":[],"mappings":"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"}