@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
package/dist/help.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { JSON_COMMANDS } from './cli/json.js';
1
2
  const COMMANDS = [
2
3
  { names: ['help'], usage: 'help [command]', summary: 'Show the command index or help for one command.' },
3
4
  { names: ['version'], usage: 'version', summary: 'Print the installed version.', hiddenFromIndex: true },
@@ -31,7 +32,7 @@ const COMMANDS = [
31
32
  '',
32
33
  'Options:',
33
34
  ' -f, --force Express without first catching unread activity or satisfying attention etiquette.',
34
- ' --no-wait If held or throttled, save a draft and return instead of waiting. A held wait gives up on its own after 10 minutes.',
35
+ ' --no-wait If throttled, save a draft and return instead of waiting.',
35
36
  ' --metadata <json> Attach a bounded JSON object (at most 16 KiB UTF-8 and 256 nesting levels); it may be the only content.',
36
37
  ' Metadata is returned in structured activity results but is not shown in human text or searchable.',
37
38
  ],
@@ -52,9 +53,9 @@ const COMMANDS = [
52
53
  details: ['Options:', ' --ndjson Emit one JSON event per line.', ' --for <name> With --ndjson, emit notifications for one participant.', ' --last <N> Emit the last N eligible activities first (default 10, maximum 100).', ' --after <id> Resume after a canonical activity id (act/12).'],
53
54
  },
54
55
  {
55
- names: ['inbox'], usage: 'inbox --for-session <session-id> [--limit <N>] [--json]', hiddenFromIndex: true,
56
+ names: ['inbox'], usage: 'inbox --for-owner <owner> [--limit <N>]', hiddenFromIndex: true,
56
57
  summary: 'Inspect a bounded machine-local notification snapshot for a native session.',
57
- details: ['Options:', ' --for-session <id> Required harness session id.', ' --limit <N> Snapshot memberships (default 20, maximum 100).', ' --json Emit the bounded snapshot as structured JSON.'],
58
+ details: ['Options:', ' --for-owner <id> Required opaque owner.', ' --limit <N> Snapshot memberships (default 20, maximum 100).'],
58
59
  },
59
60
  { names: ['codex-hook'], usage: '{command}', summary: 'Present pending attention at one native agent boundary.', hiddenFromIndex: true },
60
61
  { names: ['claude-mod'], usage: '{command}', summary: 'Receive a structured operation from the loaded Claude mod.', hiddenFromIndex: true },
@@ -67,10 +68,8 @@ const COMMANDS = [
67
68
  {
68
69
  names: ['participants'], usage: 'participants [--limit <count>]', usesSquare: true, group: 'host',
69
70
  summary: 'Show the participant roster and current states.',
70
- details: ['Options:', ' --limit <count> Show the first count names in roster order (default 20, maximum 100).'],
71
+ details: ['Options:', ' --limit <count> Show the first count names in human text (default 20, maximum 100).'],
71
72
  },
72
- { names: ['hold'], usage: '--as <name> hold [reason | -]', usesSquare: true, group: 'participant', summary: 'Raise a hand and pause participant activity.' },
73
- { names: ['resume'], usage: '--as <name> resume', usesSquare: true, group: 'participant', summary: 'Lower the raised hand and resume activity.' },
74
73
  {
75
74
  names: ['install'], usage: 'install (--all | <target>...) [-f]', group: 'maintenance',
76
75
  summary: 'Install Square support for one or more agent hosts.',
@@ -93,7 +92,7 @@ const COMMANDS = [
93
92
  {
94
93
  names: ['migrate'], usage: 'migrate', usesSquare: true, group: 'maintenance',
95
94
  summary: 'Convert a supported older square to the current format in place.',
96
- details: ['For the selected .square artifact only. Stop sessions using this artifact before conversion. Back up the stopped artifact, migrate it, then run square --location <path> doctor before starting updated clients.', 'Versions 2, 3 and 4 are accepted; version 1, unrelated or unsupported artifacts are refused without a write. Names whose own owner and route evidence agrees may share a body; names with missing or conflicting ownership stay standing without write or delivery authority. Migration reports recovered seats, unowned standing names, shared-body seats and skipped evidence. There is no --check, --backup or --force.'],
95
+ details: ['For the selected .square artifact only. Stop sessions using this artifact before conversion. Back up the stopped artifact, migrate it, then run square --location <path> doctor before starting updated clients.', 'Versions 2–7 migrate explicitly to format 8. Format 7 migration deletes hold/resume activities and their keyed rows, clears replies to those activities, and preserves surviving activity ids with gaps. Format 6 attachment and opaque-owner conversion are preserved; versions 4/5 first gain attached seats, and 4 also gains empty deferrals. Version 1, unrelated or unsupported artifacts refuse without a write. Names whose legacy owner and routes agree may share an owner; ambiguous names stay standing without custody. Rejoin with the same owner after migration to republish discovery. There is no --check, --backup or --force.'],
97
96
  },
98
97
  ];
99
98
  function definitionFor(command) {
@@ -104,7 +103,7 @@ function isHelpFlag(value) {
104
103
  }
105
104
  export function renderGlobalHelp() {
106
105
  const groups = [
107
- { key: 'participant', title: 'In the square:', order: ['join', 'express', 'listen', 'unlisten', 'ignore', 'listening', 'catch', 'history', 'status', 'hold', 'resume', 'done'] },
106
+ { key: 'participant', title: 'In the square:', order: ['join', 'express', 'listen', 'unlisten', 'ignore', 'listening', 'catch', 'history', 'status', 'done'] },
108
107
  { key: 'host', title: 'Prepare and manage:', order: ['build', 'list', 'participants'] },
109
108
  { key: 'maintenance', title: 'Setup:', order: ['install', 'uninstall', 'doctor', 'migrate'] },
110
109
  ];
@@ -117,10 +116,11 @@ export function renderGlobalHelp() {
117
116
  '',
118
117
  ]);
119
118
  return [
120
- 'Usage: square [--location <square>] [--as <name>] <command> [args...]',
121
- 'Environment: SQUARE_LOCATION sets the CLI location (local artifact path or stdio://name/key); square-accessing commands require it or --location. Use --as to select a participant.',
122
- 'Backends: SQUARE_BACKENDS_CONFIG selects {backends:{name:{command,args?,cwd?,env?}}}; default ~/.config/square/backends.json. See guides/backends.md.',
123
- 'CLI flags override environment values. Participants join explicitly; no square is joined automatically.',
119
+ 'Usage: square [--location <square>] [--as <name>] [--owner <id>] <command> [args...]',
120
+ 'Environment: SQUARE_LOCATION sets the CLI local artifact path; square-accessing commands require it or --location. Use --as to select a participant.',
121
+ 'Identity: --owner overrides SQUARE_OWNER, then a complete harness identity, then user:<OS user>@<host>. Incomplete or ambiguous harness identities refuse. Owners without wake use catch --idle.',
122
+ 'Output: --json on join, express, catch, history, listen, ignore, unlisten, listening, done, status, participants and inbox prints the SDK result without observation; a participant handle becomes {name}.',
123
+ 'History emits one JSON document; stream --ndjson emits one SDK record per line.',
124
124
  '',
125
125
  ...commandLines,
126
126
  "Run 'square <command> --help' for command options.",
@@ -132,7 +132,7 @@ export function renderSubcommandHelp(command) {
132
132
  if (definition === undefined)
133
133
  return undefined;
134
134
  const aliases = definition.names.filter((name) => name !== command);
135
- const usage = definition.usage.replace('{command}', command);
135
+ const usage = definition.usage.replace('{command}', command) + (JSON_COMMANDS.has(command) ? ' [--json]' : '');
136
136
  return [
137
137
  `Usage: square ${definition.usesSquare ? '[--location <square>] ' : ''}${usage}`,
138
138
  ...(aliases.length > 0 ? [`Aliases: ${aliases.join(', ')}`] : []),
@@ -151,7 +151,7 @@ export function helpRequest(rawArgs) {
151
151
  const args = [];
152
152
  for (let index = 0; index < rawArgs.length; index++) {
153
153
  const arg = rawArgs[index];
154
- if (arg === '--location' || arg === '--as') {
154
+ if (arg === '--location' || arg === '--as' || arg === '--owner') {
155
155
  const value = rawArgs[index + 1];
156
156
  if (value === undefined || value.startsWith('--'))
157
157
  return undefined;
@@ -0,0 +1,16 @@
1
+ import type { WakeRoute } from './model.js';
2
+ import type { WakeOutcome, WakeRequest, WakeTransportPort } from './ports.js';
3
+ export interface HttpWakeTransportOptions {
4
+ fetch?: typeof fetch;
5
+ env?: NodeJS.ProcessEnv;
6
+ }
7
+ /** HTTP accepts wake custody only; it never consumes or presents Square activity. */
8
+ export declare class HttpWakeTransport implements WakeTransportPort {
9
+ private readonly opts;
10
+ constructor(opts?: HttpWakeTransportOptions);
11
+ probe(route: WakeRoute): Promise<boolean | {
12
+ outcome: 'not-capable';
13
+ diagnostic: string;
14
+ }>;
15
+ attempt(request: WakeRequest, timeoutMs: number, beforeSend: () => Promise<boolean>): Promise<WakeOutcome>;
16
+ }
@@ -0,0 +1,101 @@
1
+ import { wakePreview } from './delivery.js';
2
+ /** Configuration only: probing never contacts the receiver. */
3
+ function configuration(route, env) {
4
+ if (route.kind !== 'http')
5
+ return undefined;
6
+ try {
7
+ const url = new URL(route.address.url);
8
+ if (!['http:', 'https:'].includes(url.protocol) || !url.hostname || url.username || url.password
9
+ || route.address.url.includes('#'))
10
+ return undefined;
11
+ const headers = new Headers({ 'Content-Type': 'application/json' });
12
+ const tokenEnv = route.address.tokenEnv;
13
+ if (tokenEnv !== undefined) {
14
+ const token = tokenEnv.trim() ? env[tokenEnv] : undefined;
15
+ // RFC 6750 bearer credentials; whitespace/control characters cannot become headers.
16
+ if (!token || token !== token.trim() || !/^[A-Za-z0-9._~+\/-]+=*$/.test(token))
17
+ return undefined;
18
+ headers.set('Authorization', `Bearer ${token}`);
19
+ }
20
+ return { url: route.address.url, headers };
21
+ }
22
+ catch {
23
+ return undefined;
24
+ }
25
+ }
26
+ /** HTTP accepts wake custody only; it never consumes or presents Square activity. */
27
+ export class HttpWakeTransport {
28
+ opts;
29
+ constructor(opts = {}) {
30
+ this.opts = opts;
31
+ }
32
+ async probe(route) {
33
+ return configuration(route, this.opts.env ?? process.env) !== undefined
34
+ ? true : { outcome: 'not-capable', diagnostic: 'HTTP wake configuration unavailable.' };
35
+ }
36
+ async attempt(request, timeoutMs, beforeSend) {
37
+ const deadline = Date.now() + timeoutMs;
38
+ if (request.route.kind !== 'http')
39
+ return { outcome: 'not-capable' };
40
+ const config = configuration(request.route, this.opts.env ?? process.env);
41
+ if (config === undefined)
42
+ return { outcome: 'failed', signature: 'http_config_rejected', message: 'Invalid HTTP wake configuration.' };
43
+ const controller = new AbortController();
44
+ let sent = false;
45
+ const budgetUnavailable = () => sent
46
+ ? { outcome: 'unknown', signature: 'http_send_timeout', message: 'HTTP wake acceptance is unknown after timeout.' }
47
+ : { outcome: 'failed', signature: 'http_pre_send_timeout', message: 'HTTP wake budget elapsed before sending.' };
48
+ if (!Number.isFinite(timeoutMs) || Date.now() >= deadline)
49
+ return budgetUnavailable();
50
+ let timer;
51
+ const timeout = new Promise(resolve => {
52
+ timer = setTimeout(() => { controller.abort(); resolve(budgetUnavailable()); }, Math.max(0, deadline - Date.now()));
53
+ });
54
+ const send = async () => {
55
+ try {
56
+ const preview = request.artifact === undefined ? undefined
57
+ : (await request.artifact.read(facts => wakePreview(facts, request), controller.signal)).value;
58
+ if (preview === undefined)
59
+ return { outcome: 'not-capable', diagnostic: 'No pending Square activity to preview.' };
60
+ const body = JSON.stringify({
61
+ location: request.location, participant: request.participant, activity: request.activity, actor: request.actor,
62
+ ...(request.attemptN === undefined ? {} : { attemptN: request.attemptN }), preview,
63
+ });
64
+ if (controller.signal.aborted || Date.now() >= deadline)
65
+ return budgetUnavailable();
66
+ if (!(await beforeSend()))
67
+ return { outcome: 'not-capable' };
68
+ // Preview and the final gate spend the same absolute budget as the request.
69
+ if (controller.signal.aborted || Date.now() >= deadline)
70
+ return budgetUnavailable();
71
+ sent = true;
72
+ const response = await (this.opts.fetch ?? fetch)(config.url, {
73
+ method: 'POST', headers: config.headers, body, redirect: 'manual', signal: controller.signal,
74
+ });
75
+ // Status is the whole acknowledgement; never wait for or buffer a receiver body.
76
+ void response.body?.cancel().catch(() => undefined);
77
+ if (response.status >= 200 && response.status < 300)
78
+ return { outcome: 'accepted', signature: 'http_accepted' };
79
+ return {
80
+ outcome: 'failed', signature: response.status >= 500 && response.status < 600 ? 'http_status_transient' : 'http_status_rejected',
81
+ message: `HTTP wake returned status ${response.status}.`,
82
+ };
83
+ }
84
+ catch {
85
+ // URLs, query credentials and raw fetch exceptions must never reach evidence.
86
+ if (controller.signal.aborted)
87
+ return budgetUnavailable();
88
+ return sent
89
+ ? { outcome: 'unknown', signature: 'http_send_unknown', message: 'HTTP wake acceptance is unknown after a network error.' }
90
+ : { outcome: 'failed', signature: 'http_pre_send_failed', message: 'HTTP wake could not be prepared.' };
91
+ }
92
+ };
93
+ try {
94
+ return await Promise.race([send(), timeout]);
95
+ }
96
+ finally {
97
+ if (timer !== undefined)
98
+ clearTimeout(timer);
99
+ }
100
+ }
101
+ }
package/dist/inbox.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type Body, type LocatedSeat, type PendingMembership } from './model.js';
1
+ import { type Owner, type LocatedSeat, type PendingMembership } from './model.js';
2
2
  import type { SquareRead } from './ports.js';
3
3
  export interface PendingWaitOptions {
4
4
  signal?: AbortSignal;
@@ -14,15 +14,12 @@ export declare function pendingAtBoundary(inbox: readonly PendingMembership[]):
14
14
  export declare function projectBoundaryEligibility(inbox: readonly PendingMembership[]): Promise<PendingMembership[]>;
15
15
  export interface SessionBindingCandidate {
16
16
  readonly location: string;
17
- readonly body: Body;
17
+ readonly owner: Owner;
18
18
  }
19
19
  /** HOME is a candidate path index; current seats and delivery evidence live in the artifact. */
20
20
  export declare function projectSessionCandidates(input: SessionSeatQuery): Promise<readonly SessionBindingCandidate[]>;
21
21
  export interface SessionSeatQuery {
22
- readonly body?: Body;
23
- readonly sessionId?: string;
24
- readonly channel?: string;
25
- readonly agent?: string;
22
+ readonly owner?: Owner;
26
23
  readonly location?: string;
27
24
  readonly env?: NodeJS.ProcessEnv;
28
25
  }
@@ -36,31 +33,21 @@ export interface NativePendingPreview {
36
33
  }
37
34
  export type NativePendingPreviewInput = {
38
35
  readonly location: string;
39
- readonly sessionId: string;
36
+ readonly generation: import('./model.js').ActivityId;
40
37
  readonly participant: string;
41
38
  readonly actIndex: number;
42
39
  readonly cancelledThrough?: number;
43
40
  readonly now: number;
44
41
  } & ({
45
42
  readonly routeKind: 'claude-native';
46
- readonly address: {
47
- readonly endpoint: string;
48
- };
43
+ readonly address: Readonly<Record<string, string>>;
49
44
  } | {
50
45
  readonly routeKind: 'opencode-server';
51
- readonly address: {
52
- readonly sessionId: string;
53
- };
46
+ readonly address: Readonly<Record<string, string>>;
54
47
  });
55
- /** First matching current seat, exact body/generation/address and catch ownership. */
48
+ /** Current-generation native attention, owned by its exact wake address and catch state. */
56
49
  export declare function nativePendingPreview(facts: SquareRead, input: NativePendingPreviewInput): NativePendingPreview | undefined;
57
- export interface SessionIdentityQuery {
58
- readonly channel: string;
59
- readonly session: string;
60
- readonly allAgents?: true;
61
- }
62
- export type SessionIdentity = Body | SessionIdentityQuery;
63
- export declare function sessionInbox(identity: SessionIdentity, env?: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<PendingMembership[]>;
50
+ export declare function sessionInbox(owner: Owner, env?: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<PendingMembership[]>;
64
51
  export interface SessionPendingObserver {
65
52
  /** Read committed revisions before reusing content; filesystem hints are only for waiting. */
66
53
  read(options?: Pick<PendingWaitOptions, 'signal' | 'excludeKeys'>): Promise<PendingMembership[]>;
@@ -68,4 +55,4 @@ export interface SessionPendingObserver {
68
55
  close(): void;
69
56
  }
70
57
  /** Session-scoped projection over process-shared storage change detectors. */
71
- export declare function observeSessionPending(identity: SessionIdentity, suppliedEnv?: NodeJS.ProcessEnv): Promise<SessionPendingObserver>;
58
+ export declare function observeSessionPending(owner: Owner, suppliedEnv?: NodeJS.ProcessEnv): Promise<SessionPendingObserver>;
package/dist/inbox.js CHANGED
@@ -1,4 +1,3 @@
1
- import { sameBody } from './model.js';
2
1
  import { createArtifactPort, observeSquareDeliveryRevision, observeSquareRevision } from './artifact.js';
3
2
  import { indexedSquareEntries, observeSquareIndex } from './square-index.js';
4
3
  import { canonicalPath } from './canonical-path.js';
@@ -20,8 +19,8 @@ export function pendingAtBoundary(inbox) {
20
19
  }
21
20
  function eligibleNotifications(facts, membership, now = Date.now()) {
22
21
  const seat = facts.seat(membership.seat.participant);
23
- if (seat === undefined || seat.generation !== membership.seat.generation
24
- || !sameBody(seat.body, membership.seat.body))
22
+ if (seat === undefined || seat.attachment !== 'attached' || seat.generation !== membership.seat.generation
23
+ || seat.owner !== membership.seat.owner)
25
24
  return { notifications: [] };
26
25
  const cancelledThrough = facts.cancelledThrough(seat.participant, seat.generation);
27
26
  let expiry = Infinity;
@@ -68,22 +67,22 @@ export async function projectBoundaryEligibility(inbox) {
68
67
  /** HOME is a candidate path index; current seats and delivery evidence live in the artifact. */
69
68
  export async function projectSessionCandidates(input) {
70
69
  const location = input.location === undefined ? undefined : await canonicalPath(input.location);
71
- const entries = await indexedSquareEntries({ body: input.body, session: input.sessionId, channel: input.channel, agent: input.agent }, input.env ?? process.env);
70
+ const entries = await indexedSquareEntries({ owner: input.owner }, input.env ?? process.env);
72
71
  const candidates = new Map();
73
72
  for (const entry of entries) {
74
73
  if (location !== undefined && location !== entry.location)
75
74
  continue;
76
- const key = JSON.stringify([entry.location, entry.body.channel, entry.body.session, entry.body.agent ?? null]);
77
- candidates.set(key, { location: entry.location, body: entry.body });
75
+ const key = JSON.stringify([entry.location, entry.owner]);
76
+ candidates.set(key, { location: entry.location, owner: entry.owner });
78
77
  }
79
78
  return [...candidates.values()];
80
79
  }
81
80
  function groupCandidates(candidates) {
82
81
  const grouped = new Map();
83
- for (const { location, body } of candidates) {
84
- const bodies = grouped.get(location) ?? [];
85
- bodies.push(body);
86
- grouped.set(location, bodies);
82
+ for (const { location, owner } of candidates) {
83
+ const owners = grouped.get(location) ?? [];
84
+ owners.push(owner);
85
+ grouped.set(location, owners);
87
86
  }
88
87
  return grouped;
89
88
  }
@@ -97,11 +96,11 @@ function locatedSeats(facts, location, seats) {
97
96
  /** Lifecycle reads resolve seats and join times, never pending activity bodies. */
98
97
  export async function projectSessionSeats(input, signal) {
99
98
  const seats = [];
100
- for (const [location, bodies] of groupCandidates(await projectSessionCandidates(input))) {
99
+ for (const [location, owners] of groupCandidates(await projectSessionCandidates(input))) {
101
100
  signal?.throwIfAborted();
102
101
  const artifact = createArtifactPort(location, undefined, signal);
103
102
  try {
104
- seats.push(...(await artifact.read(facts => locatedSeats(facts, location, bodies.flatMap(body => facts.seatsForBody(body))), signal)).value);
103
+ seats.push(...(await artifact.read(facts => locatedSeats(facts, location, owners.flatMap(owner => facts.seatsForOwner(owner))), signal)).value);
105
104
  }
106
105
  catch (error) {
107
106
  if (signal?.aborted)
@@ -116,11 +115,11 @@ export async function projectSessionSeats(input, signal) {
116
115
  /** Public count boundary: one coherent read per artifact, without pending body hydration. */
117
116
  export async function sessionInboxCounts(input, signal) {
118
117
  const rows = [];
119
- for (const [location, bodies] of groupCandidates(await projectSessionCandidates(input))) {
118
+ for (const [location, owners] of groupCandidates(await projectSessionCandidates(input))) {
120
119
  signal?.throwIfAborted();
121
120
  const artifact = createArtifactPort(location, undefined, signal);
122
121
  try {
123
- rows.push(...(await artifact.read(facts => locatedSeats(facts, location, bodies.flatMap(body => facts.seatsForBody(body))).map(({ seat }) => ({
122
+ rows.push(...(await artifact.read(facts => locatedSeats(facts, location, owners.flatMap(owner => facts.seatsForOwner(owner).filter(seat => seat.attachment === 'attached'))).map(({ seat }) => ({
124
123
  name: seat.participant, squarePath: location, pending: facts.directedCount(seat.participant, { unseenOnly: true }),
125
124
  })), signal)).value);
126
125
  }
@@ -137,13 +136,13 @@ export async function sessionInboxCounts(input, signal) {
137
136
  /** One authoritative read per canonical artifact, even when discovery names several bodies. */
138
137
  async function projectPending(candidates, cache, signal) {
139
138
  const pending = [];
140
- for (const [location, bodies] of groupCandidates(candidates)) {
139
+ for (const [location, owners] of groupCandidates(candidates)) {
141
140
  signal?.throwIfAborted();
142
141
  const artifact = createArtifactPort(location, undefined, signal);
143
142
  try {
144
143
  const { value: current } = await artifact.read(facts => {
145
- const seats = bodies.flatMap(body => facts.seatsForBody(body));
146
- const coordinate = JSON.stringify([bodies, seats.map(seat => [seat.participant, seat.body, seat.generation])]);
144
+ const seats = owners.flatMap(owner => facts.seatsForOwner(owner).filter(seat => seat.attachment === 'attached'));
145
+ const coordinate = JSON.stringify([owners, seats.map(seat => [seat.participant, seat.owner, seat.generation])]);
147
146
  const header = facts.header();
148
147
  let cached = cache?.get(location);
149
148
  if (cached?.coordinate !== coordinate || cached.version !== header.revision) {
@@ -177,14 +176,14 @@ async function projectPending(candidates, cache, signal) {
177
176
  return pending;
178
177
  }
179
178
  function sameWakeAddress(address, kind, requested) {
180
- if (address?.kind !== kind)
179
+ if (address?.kind !== kind || Object.keys(address.address).length !== Object.keys(requested).length)
181
180
  return false;
182
181
  return Object.entries(requested).every(([key, value]) => address.address[key] === value);
183
182
  }
184
- /** First matching current seat, exact body/generation/address and catch ownership. */
183
+ /** Current-generation native attention, owned by its exact wake address and catch state. */
185
184
  export function nativePendingPreview(facts, input) {
186
185
  const seat = facts.seat(input.participant);
187
- if (seat === undefined || seat.body.session !== input.sessionId
186
+ if (seat === undefined || seat.attachment !== 'attached' || seat.generation !== input.generation
188
187
  || !sameWakeAddress(seat.wakeAddress, input.routeKind, input.address))
189
188
  return undefined;
190
189
  const cancelledThrough = facts.cancelledThrough(seat.participant, seat.generation);
@@ -200,10 +199,8 @@ export function nativePendingPreview(facts, input) {
200
199
  return { payload: renderSquareReminder([renderAttentionPreview({ squarePath: input.location, recipient: input.participant, actIndex: item.index,
201
200
  actor: item.actor, route: head.route, body: item.body ?? '(structured activity)' })]), clipped: attentionBodyIsClipped(item.body ?? '(structured activity)') };
202
201
  }
203
- export async function sessionInbox(identity, env = process.env, signal) {
204
- const query = 'allAgents' in identity ? { channel: identity.channel, sessionId: identity.session, env }
205
- : { body: identity, env };
206
- return projectPending(await projectSessionCandidates(query), undefined, signal);
202
+ export async function sessionInbox(owner, env = process.env, signal) {
203
+ return projectPending(await projectSessionCandidates({ owner, env }), undefined, signal);
207
204
  }
208
205
  async function capture(observer, kind, signal) {
209
206
  const generation = observer.generation;
@@ -234,10 +231,9 @@ async function capture(observer, kind, signal) {
234
231
  }
235
232
  }
236
233
  /** Session-scoped projection over process-shared storage change detectors. */
237
- export async function observeSessionPending(identity, suppliedEnv = process.env) {
234
+ export async function observeSessionPending(owner, suppliedEnv = process.env) {
238
235
  const env = { ...suppliedEnv };
239
- const query = 'allAgents' in identity ? { channel: identity.channel, sessionId: identity.session, env }
240
- : { body: identity, env };
236
+ const query = { owner, env };
241
237
  const index = await observeSquareIndex(env);
242
238
  const artifacts = new Map();
243
239
  const cache = new Map();
package/dist/index.d.ts CHANGED
@@ -1,8 +1,6 @@
1
1
  export { Square, squareLocation, squareClock, createSquareResourceOwner, discover, inbox, diagnoseSquare, migrateSquare } from './square-api.js';
2
2
  export { SquareError, ACTIVITY_METADATA_MAX_BYTES } from './model.js';
3
+ export { selectOwnSeat, requireOwner, localUserOwner, harnessOwner, decodeHarnessOwner } from './square-api.js';
3
4
  export type { WakeTransportPort, WakeRequest, WakeOutcome } from './ports.js';
4
5
  export type { WakeTransports } from './notifications.js';
5
- export { createDefaultWakeTransport } from './notifications.js';
6
- export type { PresentationSinkPort } from './ports.js';
7
- export type * from './square-backend.js';
8
6
  export type * from './square-facade.js';
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  export { Square, squareLocation, squareClock, createSquareResourceOwner, discover, inbox, diagnoseSquare, migrateSquare } from './square-api.js';
2
2
  export { SquareError, ACTIVITY_METADATA_MAX_BYTES } from './model.js';
3
- export { createDefaultWakeTransport } from './notifications.js';
3
+ export { selectOwnSeat, requireOwner, localUserOwner, harnessOwner, decodeHarnessOwner } from './square-api.js';
package/dist/mcp.d.ts CHANGED
@@ -1,7 +1,10 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { type Owner } from './square-api.js';
2
3
  import type { SquareAtInput } from './square-facade.js';
3
4
  /** The server owns its artifact connection lifetime, so callers cannot lend one. */
4
5
  export interface SquareMcpOptions extends Omit<SquareAtInput, 'resources'> {
6
+ readonly owner?: Owner;
7
+ readonly wakeAddress?: import('./model.js').WakeAddress;
5
8
  }
6
9
  export type SquareMcpServer = McpServer;
7
10
  export declare function createSquareMcpServer(options: SquareMcpOptions): SquareMcpServer;
package/dist/mcp.js CHANGED
@@ -6,8 +6,10 @@ import { codexWakeAddress } from './codex-address.js';
6
6
  import { readClaudeMcpCall } from './claude-mcp-context.js';
7
7
  import { SQUARE_IDENTITY } from './identity.js';
8
8
  import { Square, createSquareResourceOwner } from './square-api.js';
9
+ import { harnessOwner } from './square-api.js';
9
10
  export function createSquareMcpServer(options) {
10
- const baseEnv = { ...(options.env ?? process.env) };
11
+ const { owner: configuredOwner, wakeAddress, ...resourceOptions } = options;
12
+ const baseEnv = { ...(resourceOptions.env ?? process.env) };
11
13
  const resources = createSquareResourceOwner();
12
14
  // Calls to the configured square borrow one server-owned connection.
13
15
  const inFlight = new Set();
@@ -19,21 +21,20 @@ export function createSquareMcpServer(options) {
19
21
  const call = (async () => {
20
22
  let square;
21
23
  try {
22
- const { as: participant, location } = args;
24
+ const { location } = args;
23
25
  const threadId = extra._meta?.threadId;
24
26
  const opencodeSessionId = extra._meta?.['ai.opencode/sessionID'];
25
27
  // Claude's plugin endpoint requires the mod's call-local coordinate, never startup identity.
26
28
  const claude = baseEnv.SQUARE_CLAUDE_MCP_CONTEXT === '1'
27
29
  ? await readClaudeMcpCall(extra._meta?.['claudecode/toolUseId'], baseEnv)
28
30
  : undefined;
29
- const body = claude !== undefined
30
- ? { channel: 'claude-code', session: claude.sessionId,
31
- ...(claude.agentId === undefined ? {} : { agent: claude.agentId }) }
31
+ const owner = claude !== undefined
32
+ ? harnessOwner({ channel: 'claude-code', session: claude.sessionId, ...(claude.agentId === undefined ? {} : { agent: claude.agentId }) })
32
33
  : typeof threadId === 'string' && threadId.trim()
33
- ? { channel: 'codex', session: threadId }
34
+ ? harnessOwner({ channel: 'codex', session: threadId })
34
35
  : typeof opencodeSessionId === 'string' && opencodeSessionId.trim()
35
- ? { channel: 'opencode', session: opencodeSessionId }
36
- : options.body;
36
+ ? harnessOwner({ channel: 'opencode', session: opencodeSessionId })
37
+ : configuredOwner;
37
38
  const env = claude !== undefined
38
39
  ? { ...operationEnv('claude', claude.sessionId, baseEnv),
39
40
  // Preserve Claude's session + child flag semantics; an agent loop ID is not a session ID.
@@ -44,37 +45,35 @@ export function createSquareMcpServer(options) {
44
45
  ? operationEnv('opencode', opencodeSessionId, baseEnv)
45
46
  : baseEnv;
46
47
  square = await Square.at({
47
- ...options, env, body, cwd: claude?.cwd ?? options.cwd,
48
- // A call-local Codex thread uses its native queue even when managed by Paseo.
49
- wakeAddress: claude === undefined && typeof threadId === 'string' && threadId.trim()
50
- ? codexWakeAddress(threadId, env) : options.wakeAddress,
48
+ ...resourceOptions, env, cwd: claude?.cwd ?? resourceOptions.cwd,
51
49
  path: location ?? options.path,
52
- participant: participant ?? options.participant,
53
50
  // The server owner is bound to its configured square; overrides close after each call.
54
51
  resources: location === undefined && claude === undefined ? resources : undefined,
55
52
  }, { signal: extra.signal });
56
- let receipt;
57
- // The transport shows people the square, so it asks for the rendering facts
58
- // the CLI already gathers and keeps that request out of the tool schema.
53
+ const callWake = wakeAddress !== undefined && (owner === configuredOwner
54
+ || wakeAddress.kind === 'paseo' && wakeAddress.address.agentId === baseEnv.PASEO_AGENT_ID
55
+ || wakeAddress.kind === 'claude-native' && wakeAddress.address.sessionId === claude?.sessionId
56
+ || wakeAddress.kind === 'opencode-server' && wakeAddress.address.sessionId === opencodeSessionId)
57
+ ? wakeAddress : undefined;
58
+ let text;
59
59
  try {
60
- receipt = await tool.run(square, args, { signal: extra.signal, observe: true });
60
+ text = await tool.run(square, args, { signal: extra.signal }, {
61
+ owner, wakeAddress: claude === undefined && typeof threadId === 'string' && threadId.trim()
62
+ ? codexWakeAddress(threadId, env) : callWake,
63
+ });
61
64
  }
62
65
  finally {
63
66
  await square.close();
64
67
  }
65
- // A missing observation renders as nothing, never as raw JSON for a person to read.
66
- const text = tool.render(receipt, args) ?? '';
67
- // Observation facts are a rendering aid, not part of the receipt machines receive.
68
- const { observation: _observation, ...structuredContent } = receipt;
69
- return { content: [{ type: 'text', text }], structuredContent };
68
+ return { content: [{ type: 'text', text }] };
70
69
  }
71
70
  catch (error) {
72
71
  const failure = squareToolFailure(error, extra.signal);
73
72
  const participant = args.as;
74
73
  return { isError: true, content: [{ type: 'text', text: squareToolFailureText(failure, {
75
74
  location: square?.location ?? options.path,
76
- participant: typeof participant === 'string' ? participant : options.participant,
77
- }) }], structuredContent: { error: failure } };
75
+ participant: typeof participant === 'string' ? participant : undefined,
76
+ }) }] };
78
77
  }
79
78
  })();
80
79
  inFlight.add(call);