@astrosheep/square 0.3.93 → 0.3.95

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 (146) hide show
  1. package/dist/activity-feed.d.ts +3 -5
  2. package/dist/activity-feed.js +16 -14
  3. package/dist/artifact.js +3 -3
  4. package/dist/attention.d.ts +19 -0
  5. package/dist/attention.js +30 -0
  6. package/dist/boundary-presentation.d.ts +2 -2
  7. package/dist/boundary-presentation.js +33 -40
  8. package/dist/catch-decisions.d.ts +5 -8
  9. package/dist/catch-decisions.js +45 -25
  10. package/dist/claude-delivery-status.js +10 -4
  11. package/dist/claude-delivery.js +19 -11
  12. package/dist/claude-mod.js +24 -53
  13. package/dist/cli/context.d.ts +9 -4
  14. package/dist/cli/context.js +30 -27
  15. package/dist/cli/express-presentation.d.ts +3 -1
  16. package/dist/cli/express-presentation.js +28 -16
  17. package/dist/cli/json.d.ts +3 -0
  18. package/dist/cli/json.js +14 -0
  19. package/dist/cli/mcp-server-command.js +2 -2
  20. package/dist/cli/observation-commands.d.ts +1 -3
  21. package/dist/cli/observation-commands.js +43 -56
  22. package/dist/cli/program.js +3 -3
  23. package/dist/cli/registry.js +7 -6
  24. package/dist/cli/square-commands.d.ts +0 -2
  25. package/dist/cli/square-commands.js +30 -44
  26. package/dist/codex-hook.d.ts +3 -2
  27. package/dist/codex-hook.js +13 -4
  28. package/dist/codex-queue.js +4 -4
  29. package/dist/decisions.d.ts +5 -16
  30. package/dist/decisions.js +9 -33
  31. package/dist/delivery-attempts.d.ts +52 -0
  32. package/dist/delivery-attempts.js +73 -0
  33. package/dist/delivery-deferral.js +2 -2
  34. package/dist/delivery-effects.d.ts +139 -0
  35. package/dist/delivery-effects.js +163 -0
  36. package/dist/delivery-health.d.ts +0 -1
  37. package/dist/delivery-health.js +1 -3
  38. package/dist/delivery-operations.d.ts +2 -2
  39. package/dist/delivery-operations.js +76 -101
  40. package/dist/delivery-primitives.d.ts +5 -0
  41. package/dist/delivery-primitives.js +3 -0
  42. package/dist/delivery.d.ts +1 -1
  43. package/dist/delivery.js +7 -6
  44. package/dist/help.js +14 -14
  45. package/dist/http-delivery.d.ts +16 -0
  46. package/dist/http-delivery.js +101 -0
  47. package/dist/inbox.d.ts +9 -22
  48. package/dist/inbox.js +23 -27
  49. package/dist/index.d.ts +1 -3
  50. package/dist/index.js +1 -1
  51. package/dist/mcp.d.ts +3 -0
  52. package/dist/mcp.js +23 -24
  53. package/dist/model.d.ts +12 -21
  54. package/dist/model.js +5 -8
  55. package/dist/notifications.d.ts +1 -1
  56. package/dist/notifications.js +8 -5
  57. package/dist/operation-presentation.d.ts +6 -6
  58. package/dist/operation-presentation.js +55 -58
  59. package/dist/owner.d.ts +11 -0
  60. package/dist/owner.js +32 -0
  61. package/dist/paseo-delivery.d.ts +4 -1
  62. package/dist/paseo-delivery.js +7 -12
  63. package/dist/paseo-state.d.ts +2 -2
  64. package/dist/paseo-state.js +6 -4
  65. package/dist/plugins/opencode/delivery.js +27 -15
  66. package/dist/ports.d.ts +18 -47
  67. package/dist/presentation-operations.d.ts +14 -19
  68. package/dist/presentation-operations.js +51 -61
  69. package/dist/presentation.d.ts +7 -17
  70. package/dist/presentation.js +46 -91
  71. package/dist/receive.d.ts +66 -0
  72. package/dist/receive.js +54 -0
  73. package/dist/runtime.d.ts +1 -8
  74. package/dist/runtime.js +2 -17
  75. package/dist/seats.d.ts +2 -3
  76. package/dist/seats.js +7 -15
  77. package/dist/session-lifecycle.d.ts +6 -2
  78. package/dist/session-lifecycle.js +39 -11
  79. package/dist/sqlite-square.d.ts +36 -23
  80. package/dist/sqlite-square.js +162 -211
  81. package/dist/square-api.d.ts +16 -11
  82. package/dist/square-api.js +27 -23
  83. package/dist/square-core.d.ts +0 -14
  84. package/dist/square-core.js +0 -9
  85. package/dist/square-domain.d.ts +4 -2
  86. package/dist/square-domain.js +65 -52
  87. package/dist/square-facade.d.ts +47 -105
  88. package/dist/square-index.d.ts +5 -13
  89. package/dist/square-index.js +13 -38
  90. package/dist/square-legacy-framing.d.ts +2 -0
  91. package/dist/square-legacy-framing.js +8 -0
  92. package/dist/square-location.d.ts +1 -14
  93. package/dist/square-location.js +4 -70
  94. package/dist/square-migration-host-ledger.d.ts +2 -2
  95. package/dist/square-migration-host-ledger.js +6 -5
  96. package/dist/square-migration.d.ts +4 -1
  97. package/dist/square-migration.js +93 -38
  98. package/dist/square-receive.d.ts +6 -0
  99. package/dist/square-receive.js +18 -0
  100. package/dist/square-sqlite.d.ts +9 -3
  101. package/dist/square-sqlite.js +139 -114
  102. package/dist/square-tools.d.ts +7 -10
  103. package/dist/square-tools.js +21 -61
  104. package/dist/stream.js +4 -5
  105. package/dist/views.d.ts +23 -13
  106. package/dist/views.js +47 -53
  107. package/dist/wakes.d.ts +1 -5
  108. package/dist/wakes.js +4 -6
  109. package/examples/symmetric-domains.mjs +250 -0
  110. package/guides/application-facade-delivery-evidence.md +6 -6
  111. package/guides/attention-primitive.md +32 -0
  112. package/guides/change-waits.md +2 -2
  113. package/guides/claude-native.md +18 -9
  114. package/guides/codex-delivery.md +17 -6
  115. package/guides/delivery-primitives.md +180 -0
  116. package/guides/mcp-connection-lifetime.md +1 -1
  117. package/guides/mcp.md +3 -3
  118. package/guides/migration.md +148 -151
  119. package/guides/operation-cancellation.md +1 -1
  120. package/guides/participant-ownership.md +78 -47
  121. package/guides/pi-tools.md +34 -5
  122. package/guides/primitive-catch-consolidation.md +5 -5
  123. package/guides/primitive-history-consolidation.md +2 -2
  124. package/guides/primitive-status-consolidation.md +3 -3
  125. package/guides/primitive-storage-consolidation.md +1 -1
  126. package/guides/receive-primitive.md +90 -0
  127. package/guides/row-runtime-delivery-evidence.md +1 -1
  128. package/guides/row-storage-design.md +22 -28
  129. package/guides/square-api.md +314 -91
  130. package/guides/symmetric-domains.md +92 -0
  131. package/package.json +14 -1
  132. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  133. package/plugins/claude/skills/square/SKILL.md +2 -6
  134. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  135. package/plugins/opencode/delivery.ts +27 -17
  136. package/plugins/paseo/client/palette.ts +18 -18
  137. package/plugins/paseo/client/square-message.tsx +17 -23
  138. package/plugins/pi/square-pi.js +17 -17
  139. package/skills/square/SKILL.md +2 -6
  140. package/dist/square-backend.d.ts +0 -13
  141. package/dist/square-backend.js +0 -1
  142. package/dist/square-stdio.d.ts +0 -22
  143. package/dist/square-stdio.js +0 -235
  144. package/dist/stdio-server.d.ts +0 -3
  145. package/dist/stdio-server.js +0 -212
  146. package/guides/backends.md +0 -193
@@ -1,13 +1,16 @@
1
+ import { type Owner } from '../square-api.js';
1
2
  import { type HardCap } from '../model.js';
2
3
  import { Square } from '../square-api.js';
3
- import type { Body, OperationControl } from '../square-facade.js';
4
+ import type { OperationControl } from '../square-facade.js';
4
5
  export interface CommandContext {
5
6
  cwd: string;
6
7
  env: NodeJS.ProcessEnv;
7
8
  squarePath?: string;
8
9
  name?: string;
10
+ owner?: Owner;
9
11
  homeDir: string;
10
12
  command: string;
13
+ json?: boolean;
11
14
  }
12
15
  export interface CommandSpec<Intent = unknown, Result = void> {
13
16
  parse(argv: string[], context: CommandContext): Intent;
@@ -30,18 +33,20 @@ export interface ParsedGlobalArgs {
30
33
  explicitSquarePath: boolean;
31
34
  multipleSquares: boolean;
32
35
  name?: string;
36
+ owner?: Owner;
33
37
  args: string[];
34
38
  }
35
39
  export declare function parseGlobalArgs(rawArgs: string[]): Promise<ParsedGlobalArgs>;
36
40
  export declare function locationIsRequired(command: string): boolean;
37
41
  export declare function defaultContext(command: string, squarePath?: string, name?: string): CommandContext;
38
42
  export declare function squareInput(context: CommandContext): {
39
- wakeAddress?: import("../model.js").WakeAddress | undefined;
40
- body?: Body | undefined;
41
43
  cwd: string;
42
44
  env: NodeJS.ProcessEnv;
43
45
  path: string | undefined;
44
- participant: string | undefined;
46
+ };
47
+ export declare function commandIdentity(env: NodeJS.ProcessEnv, explicit?: Owner): {
48
+ owner: Owner;
49
+ wakeAddress?: import('../model.js').WakeAddress;
45
50
  };
46
51
  /** Adapter lifetime only; each callback uses the actual public resource API. */
47
52
  export declare function withSquare<T>(context: CommandContext, run: (square: Square) => Promise<T>, control?: OperationControl): Promise<T>;
@@ -1,9 +1,10 @@
1
1
  import os from 'node:os';
2
+ import { harnessOwner, localUserOwner } from '../square-api.js';
3
+ import { harnessSessionSources } from '../participant-identity.js';
2
4
  import { codexWakeAddress } from '../codex-address.js';
3
5
  import { commandUsageHint } from '../help.js';
4
6
  import { parseParticipantList, validateName } from '../model.js';
5
7
  import { Square, squareClock } from '../square-api.js';
6
- import { harnessSessionSources } from '../participant-identity.js';
7
8
  export async function readStdin() {
8
9
  try {
9
10
  let content = '';
@@ -98,8 +99,6 @@ const LOCATION_REQUIRED_COMMANDS = new Set([
98
99
  'unlisten',
99
100
  'listening',
100
101
  'done',
101
- 'hold',
102
- 'resume',
103
102
  'history',
104
103
  'status',
105
104
  'participants',
@@ -110,12 +109,18 @@ export async function parseGlobalArgs(rawArgs) {
110
109
  const args = [...rawArgs];
111
110
  let requestedPath;
112
111
  let name;
112
+ let owner;
113
113
  for (let index = 0; index < args.length; index++) {
114
114
  if (args[index] === '--location') {
115
115
  requestedPath = requireValue(args, index, args[index]);
116
116
  args.splice(index, 2);
117
117
  index -= 1;
118
118
  }
119
+ else if (args[index] === '--owner') {
120
+ owner = requireValue(args, index, args[index]);
121
+ args.splice(index, 2);
122
+ index -= 1;
123
+ }
119
124
  else if (args[index] === '--as') {
120
125
  name = requireValue(args, index, args[index]);
121
126
  args.splice(index, 2);
@@ -132,39 +137,37 @@ export async function parseGlobalArgs(rawArgs) {
132
137
  fail(`✕ ${command} needs a square location\nsquare ls`);
133
138
  }
134
139
  const squarePath = requestedPath ?? configured;
135
- return { squarePath, explicitSquarePath: explicitSquarePath || configured !== undefined, multipleSquares: false, name, args };
140
+ return { squarePath, explicitSquarePath: explicitSquarePath || configured !== undefined, multipleSquares: false, name, owner, args };
136
141
  }
137
142
  export function locationIsRequired(command) {
138
143
  return LOCATION_REQUIRED_COMMANDS.has(command);
139
144
  }
140
145
  export function defaultContext(command, squarePath, name) {
141
- return { command, cwd: process.cwd(), env: { ...process.env }, squarePath, name, homeDir: os.homedir() };
146
+ return { command, cwd: process.cwd(), env: { ...process.env }, squarePath, name, owner: undefined, homeDir: os.homedir() };
142
147
  }
143
148
  export function squareInput(context) {
144
- const { body, wakeAddress } = commandIdentity(context.env);
145
- return { cwd: context.cwd, env: context.env, path: context.squarePath, participant: context.name,
146
- ...(body === undefined ? {} : { body }), ...(wakeAddress === undefined ? {} : { wakeAddress }) };
147
- }
148
- function commandIdentity(env) {
149
- const coordinates = harnessSessionSources.flatMap(({ variable, channel, provider }) => {
150
- const session = env[variable]?.trim();
151
- return session === undefined || session === '' ? [] : [{ channel, session, native: provider !== undefined }];
152
- });
153
- const native = coordinates.filter((coordinate) => coordinate.native);
154
- if (native.length > 1)
155
- throw new Error('Multiple harness execution coordinates are present; select one explicit body.');
156
- const paseo = coordinates.find((coordinate) => coordinate.channel === 'paseo');
157
- // A child marker without a reliable agent id must never inherit the parent or Paseo identity.
149
+ return { cwd: context.cwd, env: context.env, path: context.squarePath };
150
+ }
151
+ export function commandIdentity(env, explicit) {
152
+ if (explicit !== undefined)
153
+ return { owner: explicit };
154
+ const configured = env.SQUARE_OWNER;
155
+ if (configured !== undefined)
156
+ return { owner: configured };
157
+ const selected = harnessSessionSources.flatMap(({ variable, channel }) => env[variable]?.trim() ? [{ channel, session: env[variable].trim() }] : []);
158
+ const native = selected.filter(source => source.channel !== 'paseo');
158
159
  if (env.CLAUDE_CODE_CHILD_SESSION === '1')
159
- return {};
160
- const selected = native[0] ?? paseo;
161
- if (selected === undefined)
162
- return {};
163
- const body = { channel: selected.channel, session: selected.session };
164
- const wakeAddress = selected.channel === 'codex'
165
- ? codexWakeAddress(selected.session, env)
160
+ throw new Error('Harness identity is incomplete; provide --owner explicitly.');
161
+ if (native.length > 1)
162
+ throw new Error('Multiple harness execution coordinates are present; select one explicit owner.');
163
+ const paseo = selected.find(source => source.channel === 'paseo');
164
+ const coordinate = native[0] ?? paseo;
165
+ if (coordinate === undefined)
166
+ return { owner: localUserOwner() };
167
+ const owner = harnessOwner({ channel: coordinate.channel, session: coordinate.session });
168
+ const wakeAddress = coordinate.channel === 'codex' ? codexWakeAddress(coordinate.session, env)
166
169
  : paseo === undefined ? undefined : { kind: 'paseo', address: { agentId: paseo.session } };
167
- return { body, ...(wakeAddress === undefined ? {} : { wakeAddress }) };
170
+ return { owner, ...(wakeAddress === undefined ? {} : { wakeAddress }) };
168
171
  }
169
172
  /** Adapter lifetime only; each callback uses the actual public resource API. */
170
173
  export async function withSquare(context, run, control) {
@@ -1,7 +1,9 @@
1
1
  import { type Square } from '../square-api.js';
2
+ import type { Owner } from '../square-api.js';
2
3
  import type { OperationControl, ExpressOptions } from '../square-facade.js';
3
4
  export { renderExpression } from '../operation-presentation.js';
4
5
  export interface ActivityOptions extends Omit<ExpressOptions, 'reply'> {
6
+ readonly recoveryOwner?: Owner;
5
7
  readonly reply?: string;
6
8
  readonly metadataJson?: string;
7
9
  readonly bodyProvided?: boolean;
@@ -10,4 +12,4 @@ export declare function saveActivityDraft(squarePath: string, name: string, body
10
12
  /** Retry choices are transport syntax, never a second admission decision. */
11
13
  export declare function activityRetryCommand(squarePath: string, name: string, opts: ActivityOptions, force?: boolean): string;
12
14
  /** Body adaptation, terminal IO and drafts only; Participant owns every attempt and wait. */
13
- export declare function executeExpression(open: (control?: OperationControl) => Promise<Square>, squarePath: string, requestedName: string | undefined, rawInput: string | undefined, opts: ActivityOptions): Promise<void>;
15
+ export declare function executeExpression(open: (control?: OperationControl) => Promise<Square>, squarePath: string, requestedName: string | undefined, rawInput: string | undefined, opts: ActivityOptions, json?: boolean, resolveOwner?: Owner | (() => Owner)): Promise<void>;
@@ -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 };
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;
@@ -111,9 +116,9 @@ export async function executeExpression(open, squarePath, requestedName, rawInpu
111
116
  exitCode(1);
112
117
  return;
113
118
  }
114
- if (error.code === 'held' || error.code === 'throttled') {
119
+ if (error.code === 'throttled') {
115
120
  process.stdout.write(renderExpressNoWait({ ...output, reason: error.code, retryCommand: retryInput,
116
- delayMs: error.facts?.retryAfterMs ?? 1, holder: error.facts?.holder, holdReason: error.facts?.holdReason,
121
+ delayMs: error.facts?.retryAfterMs ?? 1,
117
122
  }));
118
123
  exitCode(1);
119
124
  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
+ '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,12 +12,12 @@ 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
19
19
  process.stderr.write(formatRefusal(squarePath, lines, {
20
- participantCount: recovery?.standingCount, held: recovery?.held,
20
+ participantCount: recovery?.standingCount,
21
21
  }));
22
22
  process.exit(error.code === 'not_found' ? 1 : 2);
23
23
  }
@@ -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;
@@ -1,8 +1,9 @@
1
- import { buildCommand, doneCommand, expressCommand, holdCommand, ignoreCommand, joinCommand, listenCommand, listeningCommand, resumeCommand, unlistenCommand } from './square-commands.js';
1
+ import { buildCommand, doneCommand, expressCommand, ignoreCommand, joinCommand, listenCommand, listeningCommand, unlistenCommand } from './square-commands.js';
2
2
  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 = [
@@ -20,8 +21,6 @@ export const commandRegistry = [
20
21
  { names: ['unlisten'], spec: unlistenCommand },
21
22
  { names: ['listening'], spec: listeningCommand },
22
23
  { names: ['done'], spec: doneCommand },
23
- { names: ['hold'], spec: holdCommand },
24
- { names: ['resume'], spec: resumeCommand },
25
24
  { names: ['install'], spec: installCommand },
26
25
  { names: ['uninstall'], spec: uninstallCommand },
27
26
  { names: ['harness'], spec: harnessCommand },
@@ -41,7 +40,9 @@ export async function executeRegisteredCommand(name, argv, context) {
41
40
  const command = findCommand(name);
42
41
  if (command === undefined)
43
42
  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);
43
+ const json = JSON_COMMANDS.has(name) && argv.includes('--json');
44
+ const options = json ? { ...context, json } : context;
45
+ const intent = command.spec.parse(argv, options);
46
+ const result = await command.spec.execute(intent, options);
47
+ command.spec.present(result, options);
47
48
  }
@@ -35,6 +35,4 @@ export declare const ignoreCommand: CommandSpec<ListenerIntent, string>;
35
35
  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
- export declare const holdCommand: CommandSpec<BodyIntent, string>;
39
- export declare const resumeCommand: CommandSpec<Record<string, never>, string>;
40
38
  export {};