@astrosheep/square 0.3.92 → 0.3.94

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 (119) hide show
  1. package/dist/activity-feed.d.ts +3 -5
  2. package/dist/activity-feed.js +16 -14
  3. package/dist/artifact.js +2 -2
  4. package/dist/boundary-presentation.d.ts +2 -2
  5. package/dist/catch-decisions.d.ts +4 -6
  6. package/dist/catch-decisions.js +4 -4
  7. package/dist/claude-delivery-status.js +10 -4
  8. package/dist/claude-delivery.js +19 -11
  9. package/dist/claude-mod.js +24 -53
  10. package/dist/cli/context.d.ts +9 -4
  11. package/dist/cli/context.js +30 -25
  12. package/dist/cli/express-presentation.d.ts +3 -1
  13. package/dist/cli/express-presentation.js +26 -14
  14. package/dist/cli/json.d.ts +3 -0
  15. package/dist/cli/json.js +14 -0
  16. package/dist/cli/mcp-server-command.js +2 -2
  17. package/dist/cli/observation-commands.d.ts +1 -3
  18. package/dist/cli/observation-commands.js +43 -56
  19. package/dist/cli/program.js +2 -2
  20. package/dist/cli/registry.js +6 -3
  21. package/dist/cli/square-commands.d.ts +1 -1
  22. package/dist/cli/square-commands.js +46 -54
  23. package/dist/codex-hook.d.ts +3 -2
  24. package/dist/codex-hook.js +13 -4
  25. package/dist/codex-queue.js +4 -4
  26. package/dist/decisions.d.ts +5 -11
  27. package/dist/decisions.js +9 -20
  28. package/dist/delivery-deferral.js +2 -2
  29. package/dist/delivery-health.d.ts +0 -1
  30. package/dist/delivery-health.js +1 -3
  31. package/dist/delivery-operations.d.ts +2 -2
  32. package/dist/delivery-operations.js +38 -38
  33. package/dist/delivery.d.ts +1 -1
  34. package/dist/delivery.js +1 -1
  35. package/dist/harness-links.d.ts +1 -10
  36. package/dist/harness-links.js +144 -109
  37. package/dist/harness.js +4 -13
  38. package/dist/help.js +12 -10
  39. package/dist/inbox.d.ts +9 -22
  40. package/dist/inbox.js +23 -27
  41. package/dist/index.d.ts +1 -3
  42. package/dist/index.js +1 -1
  43. package/dist/mcp.d.ts +3 -0
  44. package/dist/mcp.js +23 -24
  45. package/dist/model.d.ts +8 -15
  46. package/dist/model.js +4 -7
  47. package/dist/notifications.d.ts +1 -1
  48. package/dist/notifications.js +6 -5
  49. package/dist/operation-presentation.d.ts +5 -4
  50. package/dist/operation-presentation.js +50 -45
  51. package/dist/owner.d.ts +11 -0
  52. package/dist/owner.js +32 -0
  53. package/dist/paseo-delivery.d.ts +4 -1
  54. package/dist/paseo-delivery.js +7 -12
  55. package/dist/paseo-state.d.ts +2 -2
  56. package/dist/paseo-state.js +6 -4
  57. package/dist/plugins/opencode/delivery.js +27 -15
  58. package/dist/ports.d.ts +13 -33
  59. package/dist/presentation-operations.js +4 -4
  60. package/dist/presentation.d.ts +5 -8
  61. package/dist/presentation.js +33 -43
  62. package/dist/runtime.d.ts +1 -7
  63. package/dist/runtime.js +0 -14
  64. package/dist/seats.d.ts +2 -3
  65. package/dist/seats.js +7 -15
  66. package/dist/session-lifecycle.d.ts +6 -2
  67. package/dist/session-lifecycle.js +39 -11
  68. package/dist/sqlite-square.d.ts +36 -22
  69. package/dist/sqlite-square.js +138 -161
  70. package/dist/square-api.d.ts +15 -10
  71. package/dist/square-api.js +26 -22
  72. package/dist/square-core.d.ts +0 -5
  73. package/dist/square-core.js +0 -9
  74. package/dist/square-domain.d.ts +4 -2
  75. package/dist/square-domain.js +56 -21
  76. package/dist/square-facade.d.ts +45 -87
  77. package/dist/square-index.d.ts +5 -13
  78. package/dist/square-index.js +13 -38
  79. package/dist/square-location.d.ts +1 -14
  80. package/dist/square-location.js +4 -69
  81. package/dist/square-migration-host-ledger.d.ts +2 -2
  82. package/dist/square-migration-host-ledger.js +6 -5
  83. package/dist/square-migration.d.ts +2 -1
  84. package/dist/square-migration.js +52 -30
  85. package/dist/square-sqlite.d.ts +6 -2
  86. package/dist/square-sqlite.js +54 -28
  87. package/dist/square-tools.d.ts +7 -10
  88. package/dist/square-tools.js +22 -54
  89. package/dist/stream.js +4 -5
  90. package/dist/views.d.ts +21 -10
  91. package/dist/views.js +38 -38
  92. package/dist/wakes.d.ts +1 -2
  93. package/dist/wakes.js +1 -1
  94. package/guides/application-facade-delivery-evidence.md +1 -1
  95. package/guides/claude-native.md +18 -9
  96. package/guides/codex-delivery.md +17 -6
  97. package/guides/mcp-connection-lifetime.md +1 -1
  98. package/guides/mcp.md +3 -3
  99. package/guides/migration.md +142 -151
  100. package/guides/operation-cancellation.md +1 -1
  101. package/guides/participant-ownership.md +78 -47
  102. package/guides/pi-tools.md +33 -4
  103. package/guides/primitive-history-consolidation.md +1 -1
  104. package/guides/row-storage-design.md +8 -8
  105. package/guides/square-api.md +244 -88
  106. package/package.json +2 -1
  107. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  108. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  109. package/plugins/opencode/delivery.ts +27 -17
  110. package/plugins/paseo/client/palette.ts +18 -18
  111. package/plugins/paseo/client/square-message.tsx +17 -23
  112. package/plugins/pi/square-pi.js +17 -17
  113. package/dist/square-backend.d.ts +0 -13
  114. package/dist/square-backend.js +0 -1
  115. package/dist/square-stdio.d.ts +0 -22
  116. package/dist/square-stdio.js +0 -235
  117. package/dist/stdio-server.d.ts +0 -3
  118. package/dist/stdio-server.js +0 -212
  119. package/guides/backends.md +0 -193
@@ -2,10 +2,11 @@ import crypto from 'node:crypto';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { isSquareError } from '../model.js';
5
- import { formatActivityId, parseActivityId } from '../square-api.js';
5
+ import { selectOwnSeat, formatActivityId, parseActivityId } from '../square-api.js';
6
6
  import { formatRefusal, refusalText, renderActivityBlocked, renderActivityLimit, renderActivityUncertain, renderExpressNoWait, renderExpressWaiting, joinRecoveryCommand, participantsRecoveryCommand, participantCommandPrefix, commandPrefix, quoteShell, takeoverRecoveryLines, withPathOutput, } from '../presentation.js';
7
7
  import { renderExpression } from '../operation-presentation.js';
8
8
  import { formatDuration } from '../time.js';
9
+ import { jsonResult } from './json.js';
9
10
  export { renderExpression } from '../operation-presentation.js';
10
11
  export function saveActivityDraft(squarePath, name, body, now = Date.now()) {
11
12
  const draftDir = path.join(/^[A-Za-z][A-Za-z0-9+.-]*:/.test(squarePath) ? process.cwd() : path.dirname(squarePath), 'drafts');
@@ -39,10 +40,11 @@ export function activityRetryCommand(squarePath, name, opts, force = opts.force
39
40
  + (opts.noMention === true ? ' --no-mention' : '');
40
41
  const metadata = opts.metadataJson === undefined ? '' : ` --metadata ${quoteShell(opts.metadataJson)}`;
41
42
  const body = opts.bodyProvided === false ? '' : ' -';
42
- return `${participantCommandPrefix(squarePath, name)} express${force ? ' --force' : ''}${opts.noWait ? ' --no-wait' : ''}${reach}${metadata}${replyArgument(opts.reply)}${body}`;
43
+ return `${participantCommandPrefix(squarePath, name, opts.recoveryOwner)} express${force ? ' --force' : ''}${opts.noWait ? ' --no-wait' : ''}${reach}${metadata}${replyArgument(opts.reply)}${body}`;
43
44
  }
44
45
  /** Body adaptation, terminal IO and drafts only; Participant owns every attempt and wait. */
45
- export async function executeExpression(open, squarePath, requestedName, rawInput, opts) {
46
+ export async function executeExpression(open, squarePath, requestedName, rawInput, opts, json = false, resolveOwner) {
47
+ let owner;
46
48
  let square;
47
49
  let attempted = false;
48
50
  let announcedWait;
@@ -52,25 +54,27 @@ export async function executeExpression(open, squarePath, requestedName, rawInpu
52
54
  const interrupt = () => controller.abort();
53
55
  process.once('SIGINT', interrupt);
54
56
  try {
57
+ owner = typeof resolveOwner === 'function' ? resolveOwner() : resolveOwner;
55
58
  square = await open({ signal: controller.signal });
56
- const participant = await square.participant(requestedName);
59
+ const participant = await selectOwnSeat(square, owner, requestedName);
57
60
  name = participant.name;
58
61
  attempted = true;
59
- const { metadataJson: _metadataJson, bodyProvided: _bodyProvided, ...expressOptions } = opts;
62
+ const { metadataJson: _metadataJson, bodyProvided: _bodyProvided, recoveryOwner: _owner, ...expressOptions } = opts;
60
63
  const outcome = await participant.express(rawInput === undefined ? undefined : rawInput.replace(/\r\n/g, '\n').trim(), {
61
- ...expressOptions, reply: opts.reply, observe: true,
64
+ ...expressOptions, reply: opts.reply, ...(json ? {} : { observe: true }),
62
65
  }, {
63
66
  signal: controller.signal,
64
67
  onProgress(progress) {
65
68
  if (progress.kind !== 'waiting' || announcedWait === progress.reason)
66
69
  return;
67
- process.stdout.write(renderExpressWaiting(progress) + '\n');
70
+ (json ? process.stderr : process.stdout).write(renderExpressWaiting(progress) + '\n');
68
71
  announcedWait = progress.reason;
69
72
  },
70
73
  });
71
74
  landedId = outcome.activity.id;
72
- name = outcome.observation.participant;
73
- const rendered = renderExpression(squarePath, outcome);
75
+ if (!json)
76
+ name = outcome.observation.participant;
77
+ const rendered = json ? jsonResult(outcome) : renderExpression(squarePath, outcome, opts.recoveryOwner);
74
78
  if (rendered !== undefined)
75
79
  process.stdout.write(rendered);
76
80
  }
@@ -81,9 +85,10 @@ export async function executeExpression(open, squarePath, requestedName, rawInpu
81
85
  const draftPath = saveActivityDraft(squarePath, name, rawInput ?? '');
82
86
  if (square === undefined)
83
87
  square = await open().catch(() => undefined);
84
- const fresh = await square?.recovery(name).catch(() => undefined);
88
+ const selected = square === undefined ? undefined : await selectOwnSeat(square, owner, name).catch(() => undefined);
89
+ const fresh = await square?.recovery(selected?.name ?? name).catch(() => undefined);
85
90
  name = fresh?.participant ?? name;
86
- const output = { squarePath, name, draftPath, participantCount: fresh?.standingCount, held: fresh?.held };
91
+ const output = { squarePath, name, owner: opts.recoveryOwner, draftPath, participantCount: fresh?.standingCount, held: fresh?.held };
87
92
  const confirmedUnsent = landedId === undefined && detail?.landedId === undefined
88
93
  && detail?.expressEffect !== 'landed' && detail?.expressEffect !== 'unknown'
89
94
  && (!attempted || detail?.expressEffect === 'unsent' || (isSquareError(error) && error.facts?.activityUnsent === true));
@@ -99,7 +104,7 @@ export async function executeExpression(open, squarePath, requestedName, rawInpu
99
104
  if (isSquareError(error)) {
100
105
  if (error.code === 'behind' && fresh?.activitySummaries !== undefined) {
101
106
  process.stdout.write(renderActivityBlocked({ ...output, retryCommand: retryInput,
102
- activitySummaries: fresh.activitySummaries.map((summary) => ({ ...summary, previews: [...summary.previews] })),
107
+ activitySummaries: fresh.activitySummaries,
103
108
  }));
104
109
  exitCode(1);
105
110
  return;
@@ -137,13 +142,20 @@ export async function executeExpression(open, squarePath, requestedName, rawInpu
137
142
  : 'Invalid --reply: expected an activity id like act/12'
138
143
  : refusalText(error);
139
144
  const lines = [message, `· draft kept: ${draftPath}`];
145
+ if (reason === 'needs_seat') {
146
+ lines[0] = `✕ ${message}`;
147
+ lines.push(joinRecoveryCommand(squarePath, name, opts.recoveryOwner));
148
+ process.stderr.write(formatRefusal(squarePath, lines, output));
149
+ exitCode(2);
150
+ return;
151
+ }
140
152
  const repair = reason === 'bell_mention_conflict' || (reason === 'not_standing' && detail?.subject === 'recipient');
141
153
  if (isSquareError(error)) {
142
154
  if ((reason === 'never_joined' && detail?.subject === 'caller') || error.code === 'unknown_participant' || error.code === 'not_joined') {
143
- lines.push(joinRecoveryCommand(squarePath, name));
155
+ lines.push(joinRecoveryCommand(squarePath, name, opts.recoveryOwner));
144
156
  }
145
157
  if (error.code === 'already_joined')
146
- lines.push(...takeoverRecoveryLines(squarePath, name));
158
+ lines.push(...takeoverRecoveryLines(squarePath, name, opts.recoveryOwner));
147
159
  if (repair) {
148
160
  lines.push(participantsRecoveryCommand(squarePath), reason === 'bell_mention_conflict'
149
161
  ? ' · ring the bell without explicit mentions:'
@@ -0,0 +1,3 @@
1
+ export declare const JSON_COMMANDS: ReadonlySet<string>;
2
+ /** Serialize the SDK receipt, not its optional presentation or live participant handle. */
3
+ export declare function jsonResult(result: object): string;
@@ -0,0 +1,14 @@
1
+ export const JSON_COMMANDS = new Set([
2
+ 'join', 'express', 'catch', 'history', 'listen', 'ignore', 'unlisten', 'listening',
3
+ 'hold', 'resume', 'done', 'status', 'participants', 'inbox',
4
+ ]);
5
+ /** Serialize the SDK receipt, not its optional presentation or live participant handle. */
6
+ export function jsonResult(result) {
7
+ const { observation: _observation, ...data } = result;
8
+ const participant = data.participant;
9
+ if (participant !== null && typeof participant === 'object' && 'name' in participant
10
+ && 'express' in participant && typeof participant.express === 'function') {
11
+ data.participant = { name: participant.name };
12
+ }
13
+ return `${JSON.stringify(data)}\n`;
14
+ }
@@ -1,4 +1,4 @@
1
- import { fail, squareInput } from './context.js';
1
+ import { fail, squareInput, commandIdentity } from './context.js';
2
2
  export const mcpServerCommand = {
3
3
  parse(argv) {
4
4
  if (argv.length > 0)
@@ -7,7 +7,7 @@ export const mcpServerCommand = {
7
7
  },
8
8
  async execute(_intent, context) {
9
9
  const { runSquareMcpStdio } = await import('../mcp.js');
10
- await runSquareMcpStdio(squareInput(context));
10
+ await runSquareMcpStdio({ ...squareInput(context), ...commandIdentity(context.env, context.owner) });
11
11
  },
12
12
  present() { },
13
13
  };
@@ -4,7 +4,6 @@ interface HistoryCommandOptions {
4
4
  query: HistoryQuery;
5
5
  noTruncate: boolean;
6
6
  format?: string[];
7
- json: boolean;
8
7
  continuationArgs: string[];
9
8
  }
10
9
  export declare const listCommand: CommandSpec<DiscoveryOptions, string>;
@@ -22,8 +21,7 @@ interface ParticipantsCommandOptions {
22
21
  export declare const participantsCommand: CommandSpec<ParticipantsCommandOptions, string>;
23
22
  export declare const statusCommand: CommandSpec<undefined, string>;
24
23
  interface InboxIntent {
25
- sessionId: string;
26
- json: boolean;
24
+ owner: string;
27
25
  limit: number;
28
26
  }
29
27
  export declare const inboxCommand: CommandSpec<InboxIntent, string>;
@@ -9,7 +9,9 @@ import { style } from '../tty-style.js';
9
9
  import { formatRelativeTime, formatTimestamp, parseTimeOrRelative } from '../time.js';
10
10
  import { discover, inbox, formatActivityId, parseActivityId, CATCH_DEFAULT_LIMIT, CATCH_MAX_LIMIT, HISTORY_MAX_LIMIT, DEFAULT_LIST_DEPTH, MAX_LIST_DEPTH, DEFAULT_LIST_LIMIT, MAX_LIST_LIMIT } from '../square-api.js';
11
11
  import { renderCatchOutcome, renderCatchProgress } from './catch-presentation.js';
12
- import { withSquare, squareInput, now, fail, parseBoundedLimit, parseDurationMs, parseNameList, parseNonNegativeInteger, readStdin, requireSquarePath, requireValue, usage, } from './context.js';
12
+ import { selectOwnSeat } from '../square-api.js';
13
+ import { jsonResult } from './json.js';
14
+ import { withSquare, squareInput, now, fail, parseBoundedLimit, parseDurationMs, parseNameList, parseNonNegativeInteger, readStdin, requireSquarePath, requireValue, usage, commandIdentity, } from './context.js';
13
15
  const INBOX_DEFAULT_LIMIT = 20;
14
16
  const INBOX_MAX_LIMIT = 100;
15
17
  const INBOX_DISPLAY_CHARS = 160;
@@ -109,7 +111,7 @@ export const streamCommand = {
109
111
  else if (argv[index] === '--after') {
110
112
  if (hasLast)
111
113
  fail('Invalid stream options: --last and --after cannot be combined.');
112
- after = parseActRef(requireValue(argv, index, argv[index]), '--after');
114
+ after = formatActivityId(parseActRef(requireValue(argv, index, argv[index]), '--after'));
113
115
  index += 1;
114
116
  }
115
117
  else
@@ -191,7 +193,7 @@ export const catchCommand = {
191
193
  replace = true;
192
194
  else if (argv[index] === '--now')
193
195
  now = true;
194
- else
196
+ else if (argv[index] !== '--json')
195
197
  fail(`✕ catch does not know ${argv[index]}\nsquare catch --help`);
196
198
  }
197
199
  if (id !== undefined) {
@@ -218,7 +220,7 @@ export const catchCommand = {
218
220
  const interrupt = () => controller.abort();
219
221
  process.once('SIGINT', interrupt);
220
222
  try {
221
- const outcome = await withSquare(context, async (square) => (await square.participant(context.name)).catch({ ...intent, observe: true }, {
223
+ const outcome = await withSquare(context, async (square) => (await selectOwnSeat(square, commandIdentity(context.env, context.owner).owner, context.name)).catch({ ...intent, ...(context.json ? {} : { observe: true }) }, {
222
224
  signal: controller.signal,
223
225
  onProgress(progress) {
224
226
  const line = renderCatchProgress(squarePath, progress);
@@ -226,9 +228,15 @@ export const catchCommand = {
226
228
  process.stderr.write(line);
227
229
  },
228
230
  }), { signal: controller.signal });
229
- const rendered = renderCatchOutcome(squarePath, outcome, intent);
230
- process.stdout.write(rendered.stdout);
231
- process.exitCode = rendered.exitCode;
231
+ if (context.json) {
232
+ process.stdout.write(jsonResult(outcome));
233
+ process.exitCode = outcome.reason === 'already-active' || outcome.reason === 'capped' ? 1 : 0;
234
+ }
235
+ else {
236
+ const rendered = renderCatchOutcome(squarePath, outcome, intent, context.owner ?? context.env.SQUARE_OWNER);
237
+ process.stdout.write(rendered.stdout);
238
+ process.exitCode = rendered.exitCode;
239
+ }
232
240
  }
233
241
  catch (error) {
234
242
  if (!controller.signal.aborted)
@@ -288,7 +296,6 @@ function parseHistory(argv, context) {
288
296
  let fixed;
289
297
  let order;
290
298
  let format;
291
- let json = false;
292
299
  const participants = [];
293
300
  for (let index = 0; index < argv.length; index++) {
294
301
  const flag = argv[index];
@@ -358,9 +365,7 @@ function parseHistory(argv, context) {
358
365
  format = requireValue(argv, index, flag).split(',').map((item) => item.trim()).filter(Boolean);
359
366
  index += 1;
360
367
  }
361
- else if (flag === '--json')
362
- json = true;
363
- else
368
+ else if (flag !== '--json')
364
369
  fail(`✕ history does not know ${flag}\nsquare history --help`);
365
370
  }
366
371
  if (grep !== undefined && fixed !== undefined)
@@ -372,7 +377,7 @@ function parseHistory(argv, context) {
372
377
  return {
373
378
  query: { limit, from: participants, since, after, before, at: at.length === 0 ? undefined : at,
374
379
  contextBefore: beforeContext, contextAfter: afterContext, mention, grep, fixed, order },
375
- noTruncate, format, json,
380
+ noTruncate, format,
376
381
  continuationArgs: historyContinuationArgs(argv),
377
382
  };
378
383
  }
@@ -396,30 +401,13 @@ function renderFields(sayNumbers, item, fields) {
396
401
  }
397
402
  }).join('\t');
398
403
  }
399
- function jsonLine(sayNumbers, item) {
400
- const act = item;
401
- return JSON.stringify({
402
- id: formatActivityId(item.index),
403
- index: item.index,
404
- kind: act.kind,
405
- author: act.actor ?? null,
406
- at: act.at,
407
- ts: formatTimestamp(act.at),
408
- body: 'body' in act && typeof act.body === 'string' ? act.body : '',
409
- number: act.kind === 'say' ? sayNumbers[act.index] : null,
410
- mentions: act.kind === 'say' ? [...(act.mentions ?? [])] : [],
411
- metadata: act.kind === 'say' ? act.metadata ?? null : null,
412
- reach: act.kind === 'say' ? act.reach ?? null : null,
413
- reply: act.kind === 'say' && act.reply !== undefined ? formatActivityId(act.reply) : null,
414
- });
415
- }
416
404
  export const historyCommand = {
417
405
  parse(argv, context) { return parseHistory(argv, context); },
418
406
  async execute(options, context) {
419
407
  const squarePath = requireSquarePath(context);
420
408
  let outcome;
421
409
  try {
422
- outcome = await withSquare(context, (square) => square.history({ ...options.query, observe: true }));
410
+ outcome = await withSquare(context, (square) => square.history({ ...options.query, ...(context.json ? {} : { observe: true }) }));
423
411
  }
424
412
  catch (error) {
425
413
  const bounds = error?.operation?.historyBounds;
@@ -427,10 +415,10 @@ export const historyCommand = {
427
415
  fail(`✕ history is capped at ${bounds.maxLimit} activities\n${historyContinuationCommand(options, squarePath, ['--limit', String(bounds.maxLimit)])}`);
428
416
  throw error;
429
417
  }
418
+ if (context.json)
419
+ return jsonResult(outcome);
430
420
  const projection = outcome.observation;
431
421
  const events = projection.activities;
432
- if (options.json)
433
- return events.map((item) => jsonLine(projection.sayNumbers, item)).join('\n') + (events.length > 0 ? '\n' : '');
434
422
  if (options.format !== undefined && options.format.length > 0) {
435
423
  return events.map((item) => renderFields(projection.sayNumbers, item, options.format)).join('\n') + (events.length > 0 ? '\n' : '');
436
424
  }
@@ -448,6 +436,8 @@ function participantsLimitCommand(squarePath, limit) {
448
436
  function parseParticipants(argv, context) {
449
437
  let limit = PARTICIPANTS_DEFAULT_LIMIT;
450
438
  for (let index = 0; index < argv.length; index++) {
439
+ if (argv[index] === '--json')
440
+ continue;
451
441
  if (argv[index] !== '--limit')
452
442
  usage(context.command);
453
443
  limit = parseBoundedLimit(argv[index + 1], '--limit', PARTICIPANTS_MAX_LIMIT, participantsLimitCommand(requireSquarePath(context), PARTICIPANTS_MAX_LIMIT));
@@ -459,35 +449,37 @@ export const participantsCommand = {
459
449
  parse(argv, context) { return parseParticipants(argv, context); },
460
450
  async execute(intent, context) {
461
451
  const squarePath = requireSquarePath(context);
462
- const data = await withSquare(context, (square) => square.participants({ observe: true }));
463
- return renderParticipantsResult(squarePath, data, intent.limit);
452
+ const data = await withSquare(context, (square) => square.participants(context.json ? {} : { observe: true }));
453
+ return context.json ? jsonResult(data) : renderParticipantsResult(squarePath, data, intent.limit);
464
454
  },
465
455
  present: (result) => process.stdout.write(result),
466
456
  };
467
457
  export const statusCommand = {
468
- parse(argv, context) { if (argv.length > 0)
458
+ parse(argv, context) { if (argv.some((arg) => arg !== '--json'))
469
459
  usage(context.command); return undefined; },
470
460
  async execute(_intent, context) {
471
461
  const squarePath = requireSquarePath(context);
472
- const result = await withSquare(context, (square) => square.status({ viewer: context.name, observe: true }));
473
- return renderStatusResult(squarePath, result);
462
+ const result = await withSquare(context, async (square) => {
463
+ const viewer = (await selectOwnSeat(square, commandIdentity(context.env, context.owner).owner, context.name, { optional: true }))?.name;
464
+ return square.status({ viewer, ...(context.json ? {} : { observe: true }) });
465
+ });
466
+ return context.json ? jsonResult(result) : renderStatusResult(squarePath, result);
474
467
  },
475
468
  present: (result) => process.stdout.write(result),
476
469
  };
477
470
  export const inboxCommand = {
478
471
  parse(argv, context) {
479
- let sessionId;
480
- let json = false;
472
+ let owner;
481
473
  let limitValue;
482
474
  let hasLimit = false;
483
475
  let duplicateLimit = false;
484
476
  for (let index = 0; index < argv.length; index++) {
485
- if (argv[index] === '--for-session') {
486
- sessionId = requireValue(argv, index, argv[index]);
477
+ if (argv[index] === '--for-owner') {
478
+ owner = requireValue(argv, index, argv[index]);
487
479
  index += 1;
488
480
  }
489
481
  else if (argv[index] === '--json')
490
- json = true;
482
+ continue;
491
483
  else if (argv[index] === '--limit') {
492
484
  if (hasLimit)
493
485
  duplicateLimit = true;
@@ -501,28 +493,23 @@ export const inboxCommand = {
501
493
  else
502
494
  usage(context.command);
503
495
  }
504
- if (!sessionId)
505
- fail('inbox requires --for-session <session-id>.');
506
- const retry = `square inbox --for-session ${quoteShell(sessionId)} --limit ${INBOX_MAX_LIMIT}${json ? ' --json' : ''}`;
496
+ if (!owner)
497
+ fail('inbox requires --for-owner <owner>.');
498
+ const retry = `square inbox --for-owner ${quoteShell(owner)} --limit ${INBOX_MAX_LIMIT}${context.json ? ' --json' : ''}`;
507
499
  if (duplicateLimit)
508
500
  fail(`✕ inbox accepts one --limit\n${retry}`);
509
501
  const limit = hasLimit
510
502
  ? parseBoundedLimit(limitValue, '--limit', INBOX_MAX_LIMIT, retry)
511
503
  : INBOX_DEFAULT_LIMIT;
512
- return { sessionId, json, limit };
504
+ return { owner, limit };
513
505
  },
514
506
  async execute(intent, context) {
515
- const data = await inbox(squareInput(context), intent.sessionId, { limit: intent.limit });
516
- const rows = data.rows.map((membership) => ({
517
- namePreview: inboxDisplay(membership.name),
518
- squarePathPreview: inboxDisplay(membership.squarePath),
519
- pending: membership.pending,
520
- }));
521
- if (intent.json)
522
- return `${JSON.stringify({ rows, total: data.total })}\n`;
507
+ const data = await inbox(squareInput(context), intent.owner, { limit: intent.limit });
508
+ if (context.json)
509
+ return jsonResult(data);
523
510
  return [
524
- ...rows.map((row) => `${row.namePreview}\t${row.squarePathPreview}\t${row.pending}\n`),
525
- ...(rows.length < data.total ? [`${rows.length} of ${data.total} memberships shown\n`] : []),
511
+ ...data.rows.map((row) => `${inboxDisplay(row.name)}\t${inboxDisplay(row.squarePath)}\t${row.pending}\n`),
512
+ ...(data.rows.length < data.total ? [`${data.rows.length} of ${data.total} memberships shown\n`] : []),
526
513
  ].join('');
527
514
  },
528
515
  present: (result) => process.stdout.write(result),
@@ -12,7 +12,7 @@ async function handleSquareError(error, context) {
12
12
  const recovery = error.facts?.reason === 'migration_required' || context === undefined || squarePath === undefined ? undefined
13
13
  : await withSquare({ ...context, name: detail?.participant ?? context.name }, (square) => square.recovery(detail?.participant ?? context.name)).catch(() => undefined);
14
14
  const name = detail?.participant ?? recovery?.participant ?? context?.name;
15
- const lines = renderFailureLines(error, { squarePath, name });
15
+ const lines = renderFailureLines(error, { squarePath, name, owner: context?.owner ?? context?.env.SQUARE_OWNER });
16
16
  if (squarePath === undefined)
17
17
  process.stderr.write(`${lines.join('\n')}\n`);
18
18
  else
@@ -32,7 +32,7 @@ export async function runCli(rawArgs = process.argv.slice(2)) {
32
32
  }
33
33
  const parsed = await parseGlobalArgs(rawArgs);
34
34
  const command = parsed.args[0];
35
- context = defaultContext(command ?? 'help', parsed.squarePath, parsed.name);
35
+ context = { ...defaultContext(command ?? 'help', parsed.squarePath, parsed.name), owner: parsed.owner };
36
36
  if (command === undefined || command === '--help' || command === '-h') {
37
37
  await executeRegisteredCommand('help', [], context);
38
38
  return;
@@ -3,6 +3,7 @@ import { doctorCommand, migrateCommand } from './maintenance-commands.js';
3
3
  import { harnessCommand, installCommand, uninstallCommand } from './harness-command.js';
4
4
  import { helpCommand, versionCommand } from './meta-commands.js';
5
5
  import { mcpServerCommand } from './mcp-server-command.js';
6
+ import { JSON_COMMANDS } from './json.js';
6
7
  import { catchCommand, claudeModCommand, codexHookCommand, historyCommand, inboxCommand, listCommand, participantsCommand, statusCommand, streamCommand, } from './observation-commands.js';
7
8
  /** Every public command is an executable adapter, including aliases and utility commands. */
8
9
  export const commandRegistry = [
@@ -41,7 +42,9 @@ export async function executeRegisteredCommand(name, argv, context) {
41
42
  const command = findCommand(name);
42
43
  if (command === undefined)
43
44
  throw new Error(`No registered command named ${name}`);
44
- const intent = command.spec.parse(argv, context);
45
- const result = await command.spec.execute(intent, context);
46
- command.spec.present(result, context);
45
+ const json = JSON_COMMANDS.has(name) && argv.includes('--json');
46
+ const options = json ? { ...context, json } : context;
47
+ const intent = command.spec.parse(argv, options);
48
+ const result = await command.spec.execute(intent, options);
49
+ command.spec.present(result, options);
47
50
  }
@@ -36,5 +36,5 @@ export declare const unlistenCommand: CommandSpec<ListenerIntent, string>;
36
36
  export declare const listeningCommand: CommandSpec<ListenerIntent, string>;
37
37
  export declare const doneCommand: CommandSpec<BodyIntent, string>;
38
38
  export declare const holdCommand: CommandSpec<BodyIntent, string>;
39
- export declare const resumeCommand: CommandSpec<Record<string, never>, string>;
39
+ export declare const resumeCommand: CommandSpec<BodyIntent, string>;
40
40
  export {};
@@ -1,9 +1,10 @@
1
1
  import { formatHardCap } from '../model.js';
2
2
  import { commandPrefix, participantCommandPrefix, withPathOutput, } from '../presentation.js';
3
3
  import { renderJoinResult, renderLifecycleResult, renderListenerChange, renderListeningResult, } from '../operation-presentation.js';
4
- import { Square } from '../square-api.js';
4
+ import { Square, requireOwner, requireJoinName, selectOwnSeat } from '../square-api.js';
5
5
  import { executeExpression } from './express-presentation.js';
6
- import { squareInput, withSquare, fail, parseBoundedLimit, parseHardCap, parsePositiveInteger, readStdin, requireSquarePath, requireValue, resolveBody, usage, } from './context.js';
6
+ import { jsonResult } from './json.js';
7
+ import { squareInput, withSquare, fail, parseBoundedLimit, parseHardCap, parsePositiveInteger, readStdin, requireSquarePath, requireValue, resolveBody, usage, commandIdentity, } from './context.js';
7
8
  const JOIN_DEFAULT_LAST = 10;
8
9
  const JOIN_MAX_LAST = 100;
9
10
  export const buildCommand = {
@@ -69,14 +70,17 @@ export const joinCommand = {
69
70
  }
70
71
  else if (argv[index] === '--kick')
71
72
  kick = true;
72
- else
73
+ else if (argv[index] !== '--json')
73
74
  usage(context.command);
74
75
  }
75
76
  return { lastN, kick };
76
77
  },
77
78
  async execute(intent, context) {
78
- const data = await withSquare(context, (square) => square.join(context.name, { takeover: intent.kick, last: intent.lastN, observe: true }));
79
- return renderJoinResult(data);
79
+ const { owner, wakeAddress } = commandIdentity(context.env, context.owner);
80
+ const data = await withSquare(context, (square) => square.join(requireJoinName(context.name), {
81
+ owner: requireOwner(owner), wakeAddress, takeover: intent.kick, last: intent.lastN, ...(context.json ? {} : { observe: true }),
82
+ }));
83
+ return context.json ? jsonResult(data) : renderJoinResult(data, context.owner ?? context.env.SQUARE_OWNER);
80
84
  },
81
85
  present: (result) => process.stdout.write(result),
82
86
  };
@@ -92,6 +96,8 @@ function parseActivity(argv, context) {
92
96
  const bodyArgs = [];
93
97
  for (let index = 0; index < argv.length; index++) {
94
98
  const argument = argv[index];
99
+ if (argument === '--json')
100
+ continue;
95
101
  if (argument === '-f' || argument === '--force')
96
102
  force = true;
97
103
  else if (argument === '--no-wait')
@@ -141,23 +147,25 @@ export const expressCommand = {
141
147
  await executeExpression((control) => Square.at(squareInput(context), control), requireSquarePath(context), context.name, rawInput, {
142
148
  force: intent.force, noWait: intent.noWait, noMention: intent.noMention,
143
149
  mentions: intent.mentions, reach: intent.reach, reply: intent.reply, metadata: intent.metadata,
150
+ recoveryOwner: context.owner ?? context.env.SQUARE_OWNER,
144
151
  metadataJson: intent.metadataJson, bodyProvided: intent.activity !== undefined,
145
- });
152
+ }, context.json, () => commandIdentity(context.env, context.owner).owner);
146
153
  },
147
154
  present: () => { },
148
155
  };
149
156
  function parseListener(argv, context, targetRequired) {
150
- if (targetRequired ? argv.length !== 1 : argv.length !== 0)
157
+ const args = argv.filter((arg) => arg !== '--json');
158
+ if (targetRequired ? args.length !== 1 : args.length !== 0)
151
159
  usage(context.command);
152
- return targetRequired ? { target: argv[0] } : {};
160
+ return targetRequired ? { target: args[0] } : {};
153
161
  }
154
162
  function listenerCommand(verb) {
155
163
  return {
156
164
  parse(argv, context) { return parseListener(argv, context, true); },
157
165
  async execute(intent, context) {
158
166
  const squarePath = requireSquarePath(context);
159
- const data = await withSquare(context, async (square) => (await square.participant(context.name))[verb](intent.target, { observe: true }));
160
- return renderListenerChange(squarePath, verb, data);
167
+ const data = await withSquare(context, async (square) => (await selectOwnSeat(square, commandIdentity(context.env, context.owner).owner, context.name))[verb](intent.target, context.json ? {} : { observe: true }));
168
+ return context.json ? jsonResult(data) : renderListenerChange(squarePath, verb, data);
161
169
  },
162
170
  present: (result) => process.stdout.write(result),
163
171
  };
@@ -169,50 +177,34 @@ export const listeningCommand = {
169
177
  parse(argv, context) { return parseListener(argv, context, false); },
170
178
  async execute(_intent, context) {
171
179
  const squarePath = requireSquarePath(context);
172
- const data = await withSquare(context, async (square) => (await square.participant(context.name)).listening({ observe: true }));
173
- return renderListeningResult(squarePath, data);
174
- },
175
- present: (result) => process.stdout.write(result),
176
- };
177
- export const doneCommand = {
178
- parse(argv, context) {
179
- if (argv.length > 1)
180
- usage(context.command);
181
- return { body: argv[0] };
182
- },
183
- async execute(intent, context) {
184
- const body = intent.body === '-' || ((intent.body === undefined || intent.body === '') && !process.stdin.isTTY)
185
- ? await readStdin() : intent.body;
186
- const squarePath = requireSquarePath(context);
187
- const data = await withSquare(context, async (square) => (await square.participant(context.name)).done(body, { observe: true }));
188
- return renderLifecycleResult(squarePath, 'done', data);
189
- },
190
- present: (result) => process.stdout.write(result),
191
- };
192
- export const holdCommand = {
193
- parse(argv, context) {
194
- if (argv.length > 1)
195
- usage(context.command);
196
- return { body: argv[0] };
197
- },
198
- async execute(intent, context) {
199
- const body = (await resolveBody(intent.body ?? '')).replace(/\r\n/g, '\n').trim();
200
- const squarePath = requireSquarePath(context);
201
- const result = await withSquare(context, async (square) => (await square.participant(context.name)).hold(body, { observe: true }));
202
- return renderLifecycleResult(squarePath, 'hold', result);
203
- },
204
- present: (result) => process.stdout.write(result),
205
- };
206
- export const resumeCommand = {
207
- parse(argv, context) {
208
- if (argv.length !== 0)
209
- usage(context.command);
210
- return {};
211
- },
212
- async execute(_intent, context) {
213
- const squarePath = requireSquarePath(context);
214
- const result = await withSquare(context, async (square) => (await square.participant(context.name)).resume({ observe: true }));
215
- return renderLifecycleResult(squarePath, 'resume', result);
180
+ const data = await withSquare(context, async (square) => (await selectOwnSeat(square, commandIdentity(context.env, context.owner).owner, context.name)).listening(context.json ? {} : { observe: true }));
181
+ return context.json ? jsonResult(data) : renderListeningResult(squarePath, data);
216
182
  },
217
183
  present: (result) => process.stdout.write(result),
218
184
  };
185
+ function lifecycleCommand(verb) {
186
+ return {
187
+ parse(argv, context) {
188
+ const args = argv.filter((arg) => arg !== '--json');
189
+ if (args.length > (verb === 'resume' ? 0 : 1))
190
+ usage(context.command);
191
+ return { body: args[0] };
192
+ },
193
+ async execute(intent, context) {
194
+ const body = verb === 'hold' ? (await resolveBody(intent.body ?? '')).replace(/\r\n/g, '\n').trim()
195
+ : verb === 'done' && (intent.body === '-' || ((intent.body === undefined || intent.body === '') && !process.stdin.isTTY))
196
+ ? await readStdin() : intent.body;
197
+ const squarePath = requireSquarePath(context);
198
+ const result = await withSquare(context, async (square) => {
199
+ const participant = await selectOwnSeat(square, commandIdentity(context.env, context.owner).owner, context.name);
200
+ const options = context.json ? {} : { observe: true };
201
+ return verb === 'resume' ? participant.resume(options) : participant[verb](body, options);
202
+ });
203
+ return context.json ? jsonResult(result) : renderLifecycleResult(squarePath, verb, result);
204
+ },
205
+ present: (result) => process.stdout.write(result),
206
+ };
207
+ }
208
+ export const doneCommand = lifecycleCommand('done');
209
+ export const holdCommand = lifecycleCommand('hold');
210
+ export const resumeCommand = lifecycleCommand('resume');
@@ -1,4 +1,5 @@
1
- import type { Body, PendingMembership } from './model.js';
1
+ import { type Owner } from './owner.js';
2
+ import type { PendingMembership } from './model.js';
2
3
  import type { WakeTransports } from './notifications.js';
3
4
  export interface CodexHookInput {
4
5
  session_id?: unknown;
@@ -8,6 +9,6 @@ export interface CodexHookInput {
8
9
  }
9
10
  export type HookTrace = (line: string) => void;
10
11
  export declare const CODEX_HOOK_BUDGET_MS = 4500;
11
- export declare function codexHookResponse(input: CodexHookInput, lookup?: (identity: Body, env?: NodeJS.ProcessEnv, signal?: AbortSignal) => Promise<PendingMembership[]> | PendingMembership[], env?: NodeJS.ProcessEnv, deliveryTransports?: WakeTransports, trace?: HookTrace): Promise<object | undefined>;
12
+ export declare function codexHookResponse(input: CodexHookInput, lookup?: (identity: Owner, env?: NodeJS.ProcessEnv, signal?: AbortSignal) => Promise<PendingMembership[]> | PendingMembership[], env?: NodeJS.ProcessEnv, deliveryTransports?: WakeTransports, trace?: HookTrace): Promise<object | undefined>;
12
13
  export declare function runCodexHook(inputText: string, env?: NodeJS.ProcessEnv): Promise<string>;
13
14
  export declare const runCodexHookAsync: typeof runCodexHook;
@@ -1,8 +1,10 @@
1
1
  import { handleCodexHook } from './packages/agent-delivery/src/codex-hook.js';
2
2
  import { presentPendingAtBoundary } from './boundary-presentation.js';
3
3
  import { sessionInbox } from './inbox.js';
4
- import { endSessionMemberships } from './session-lifecycle.js';
4
+ import { harnessOwner } from './owner.js';
5
+ import { detachSessionSeats, restoreSessionSeats } from './session-lifecycle.js';
5
6
  import { codexBoundaryStore } from './codex-boundary-state.js';
7
+ import { codexWakeAddress } from './codex-address.js';
6
8
  import { PRIVILEGED_HOOK_BUDGET_MS, sweepPrivilegedPending } from './notifications.js';
7
9
  export const CODEX_HOOK_BUDGET_MS = 4500;
8
10
  const defaultTrace = (line) => { process.stderr.write(`${line}\n`); };
@@ -10,13 +12,20 @@ export async function codexHookResponse(input, lookup = sessionInbox, env = proc
10
12
  const configured = Number(env.SQUARE_CODEX_HOOK_BUDGET_MS ?? CODEX_HOOK_BUDGET_MS);
11
13
  const startedAt = Date.now();
12
14
  let mark = startedAt;
15
+ const timeoutMs = Number.isFinite(configured) && configured > 0 ? configured : CODEX_HOOK_BUDGET_MS;
16
+ const signal = AbortSignal.timeout(timeoutMs);
17
+ if ((input.hook_event_name === 'SessionStart' || input.hook_event_name === 'SessionResume')
18
+ && typeof input.session_id === 'string' && input.session_id.trim()) {
19
+ await restoreSessionSeats(harnessOwner({ channel: 'codex', session: input.session_id }), env, codexWakeAddress(input.session_id, env), signal);
20
+ }
13
21
  return handleCodexHook(input, {
14
22
  boundaries: codexBoundaryStore(env),
15
- timeoutMs: Number.isFinite(configured) && configured > 0 ? configured : CODEX_HOOK_BUDGET_MS,
23
+ timeoutMs,
24
+ signal,
16
25
  sweepTimeoutMs: PRIVILEGED_HOOK_BUDGET_MS,
17
- pull: (id, signal) => presentPendingAtBoundary({ channel: 'codex', session: id }, (context) => context, lookup, env, signal),
26
+ pull: (id, signal) => presentPendingAtBoundary(harnessOwner({ channel: 'codex', session: id }), (context) => context, lookup, env, signal),
18
27
  sweep: (cwd, deadline, signal) => sweepPrivilegedPending(cwd ?? process.cwd(), env, deliveryTransports, deadline, signal),
19
- end: async (id) => { await endSessionMemberships('codex', id, env); },
28
+ end: async (id) => { await detachSessionSeats('codex', id, env); },
20
29
  trace: (name, detail = '') => {
21
30
  const now = Date.now();
22
31
  const durationMs = now - mark;