@astrosheep/square 0.3.62 → 0.3.64

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 (168) hide show
  1. package/claude-plugin/.claude-plugin/plugin.json +3 -2
  2. package/claude-plugin/hooks/hooks.json +1 -22
  3. package/claude-plugin/hooks/register.js +119 -0
  4. package/claude-plugin/types.d.ts +7 -0
  5. package/codex-plugin/.codex-plugin/plugin.json +1 -1
  6. package/dist/activity-feed.d.ts +1 -10
  7. package/dist/activity-feed.js +1 -27
  8. package/dist/activity.js +57 -25
  9. package/dist/artifact.js +12 -24
  10. package/dist/attention-presentation.js +1 -0
  11. package/dist/automatic-session.d.ts +3 -1
  12. package/dist/automatic-session.js +21 -34
  13. package/dist/boundary-presentation.js +4 -13
  14. package/dist/canonical-path.d.ts +11 -0
  15. package/dist/canonical-path.js +47 -0
  16. package/dist/catch-decisions.js +14 -10
  17. package/dist/catch-selection.d.ts +6 -0
  18. package/dist/catch-selection.js +14 -0
  19. package/dist/claude-delivery.d.ts +11 -0
  20. package/dist/claude-delivery.js +145 -0
  21. package/dist/claude-mod.d.ts +8 -0
  22. package/dist/claude-mod.js +164 -0
  23. package/dist/cli/context.d.ts +0 -1
  24. package/dist/cli/context.js +9 -21
  25. package/dist/cli/harness-command.d.ts +0 -1
  26. package/dist/cli/harness-command.js +0 -6
  27. package/dist/cli/observation-commands.d.ts +1 -1
  28. package/dist/cli/observation-commands.js +31 -58
  29. package/dist/cli/program.js +20 -18
  30. package/dist/cli/registry.js +2 -2
  31. package/dist/cli/square-commands.js +10 -6
  32. package/dist/codex-hook.js +9 -12
  33. package/dist/codex-queue.d.ts +2 -2
  34. package/dist/codex-queue.js +7 -5
  35. package/dist/decisions.d.ts +2 -6
  36. package/dist/decisions.js +16 -17
  37. package/dist/delivery-health.d.ts +1 -2
  38. package/dist/delivery-health.js +4 -5
  39. package/dist/delivery-operations.d.ts +2 -5
  40. package/dist/delivery-operations.js +77 -214
  41. package/dist/delivery.d.ts +25 -47
  42. package/dist/delivery.js +14 -46
  43. package/dist/diagnostic-redaction.js +7 -9
  44. package/dist/file-changes.d.ts +0 -1
  45. package/dist/file-changes.js +2 -19
  46. package/dist/harness-claude.js +8 -6
  47. package/dist/harness-codex.d.ts +0 -2
  48. package/dist/harness-codex.js +0 -2
  49. package/dist/harness-links.js +13 -17
  50. package/dist/harness.js +3 -7
  51. package/dist/help.js +4 -3
  52. package/dist/host-ledger-file-adapter.d.ts +17 -8
  53. package/dist/host-ledger-file-adapter.js +164 -80
  54. package/dist/host-ledger.d.ts +55 -50
  55. package/dist/inbox.d.ts +0 -3
  56. package/dist/inbox.js +13 -30
  57. package/dist/index.d.ts +1 -1
  58. package/dist/index.js +1 -1
  59. package/dist/list.js +1 -4
  60. package/dist/mcp.js +18 -40
  61. package/dist/mention-parse.js +3 -9
  62. package/dist/model.d.ts +11 -2
  63. package/dist/model.js +1 -1
  64. package/dist/notifications.d.ts +3 -16
  65. package/dist/notifications.js +41 -122
  66. package/dist/opencode-delivery.d.ts +14 -0
  67. package/dist/opencode-delivery.js +171 -0
  68. package/dist/opencode.d.ts +39 -18
  69. package/dist/opencode.js +145 -40
  70. package/dist/packages/agent-delivery/src/claude-auth.d.ts +13 -0
  71. package/dist/packages/agent-delivery/src/claude-auth.js +70 -0
  72. package/dist/packages/agent-delivery/src/claude-native.d.ts +28 -0
  73. package/dist/packages/agent-delivery/src/claude-native.js +65 -0
  74. package/dist/packages/agent-delivery/src/claude.d.ts +14 -0
  75. package/dist/packages/agent-delivery/src/claude.js +66 -0
  76. package/dist/packages/agent-delivery/src/deadline.d.ts +11 -0
  77. package/dist/packages/agent-delivery/src/deadline.js +41 -0
  78. package/dist/packages/agent-delivery/src/errors.d.ts +16 -0
  79. package/dist/packages/agent-delivery/src/errors.js +41 -0
  80. package/dist/packages/agent-delivery/src/guards.d.ts +6 -0
  81. package/dist/packages/agent-delivery/src/guards.js +6 -0
  82. package/dist/packages/agent-delivery/src/index.d.ts +83 -0
  83. package/dist/packages/agent-delivery/src/index.js +110 -0
  84. package/dist/packages/agent-delivery/src/local-endpoint.d.ts +26 -0
  85. package/dist/packages/agent-delivery/src/local-endpoint.js +114 -0
  86. package/dist/packages/agent-delivery/src/opencode-native.d.ts +33 -0
  87. package/dist/packages/agent-delivery/src/opencode-native.js +75 -0
  88. package/dist/packages/agent-delivery/src/opencode.d.ts +14 -0
  89. package/dist/packages/agent-delivery/src/opencode.js +127 -0
  90. package/dist/packages/agent-delivery/src/paseo-native.d.ts +58 -0
  91. package/dist/packages/agent-delivery/src/paseo-native.js +122 -0
  92. package/dist/packages/agent-delivery/src/paseo.d.ts +19 -0
  93. package/dist/packages/agent-delivery/src/paseo.js +106 -0
  94. package/dist/packages/agent-delivery/src/pi-client.d.ts +10 -0
  95. package/dist/packages/agent-delivery/src/pi-client.js +106 -0
  96. package/dist/packages/agent-delivery/src/pi-protocol.d.ts +15 -0
  97. package/dist/packages/agent-delivery/src/pi-protocol.js +50 -0
  98. package/dist/packages/agent-delivery/src/pi-send.d.ts +23 -0
  99. package/dist/packages/agent-delivery/src/pi-send.js +4 -0
  100. package/dist/packages/agent-delivery/src/pi.d.ts +25 -0
  101. package/dist/packages/agent-delivery/src/pi.js +209 -0
  102. package/dist/packages/agent-delivery/src/types.d.ts +120 -0
  103. package/dist/participant-host.js +12 -13
  104. package/dist/participant-identity.d.ts +21 -0
  105. package/dist/participant-identity.js +32 -11
  106. package/dist/paseo-delivery.d.ts +2 -2
  107. package/dist/paseo-delivery.js +35 -27
  108. package/dist/paseo-timeline.js +25 -21
  109. package/dist/paseo.d.ts +2 -2
  110. package/dist/paseo.js +1 -2
  111. package/dist/ports.d.ts +7 -20
  112. package/dist/presentation-operations.js +16 -4
  113. package/dist/presentation.d.ts +19 -10
  114. package/dist/presentation.js +99 -177
  115. package/dist/registry.d.ts +15 -66
  116. package/dist/registry.js +45 -123
  117. package/dist/routes.d.ts +1 -28
  118. package/dist/routes.js +22 -104
  119. package/dist/runtime.d.ts +3 -9
  120. package/dist/runtime.js +14 -67
  121. package/dist/search.d.ts +0 -2
  122. package/dist/search.js +3 -10
  123. package/dist/square-actions.js +48 -72
  124. package/dist/square-application.js +25 -23
  125. package/dist/square-core.d.ts +3 -0
  126. package/dist/square-core.js +22 -5
  127. package/dist/square-facade.d.ts +2 -0
  128. package/dist/square-file-adapter.js +4 -7
  129. package/dist/square-projections.d.ts +40 -45
  130. package/dist/square-projections.js +43 -73
  131. package/dist/square-state.js +1 -4
  132. package/dist/square-storage.d.ts +5 -8
  133. package/dist/square-storage.js +13 -36
  134. package/dist/square-wiring.d.ts +0 -5
  135. package/dist/square-wiring.js +3 -21
  136. package/dist/stream.js +4 -6
  137. package/dist/time.js +2 -7
  138. package/dist/views.d.ts +4 -16
  139. package/dist/views.js +12 -19
  140. package/dist/wake-attempts.d.ts +2 -56
  141. package/dist/wake-attempts.js +15 -110
  142. package/dist/wake-eligibility.d.ts +43 -0
  143. package/dist/wake-eligibility.js +106 -0
  144. package/dist/wake-evidence.d.ts +32 -9
  145. package/dist/wake-evidence.js +29 -24
  146. package/dist/wake-sink.d.ts +13 -9
  147. package/dist/wake-sink.js +35 -54
  148. package/dist/watch.js +13 -21
  149. package/extensions/square-pi.js +10 -5
  150. package/guides/architect.md +77 -77
  151. package/guides/brainstorm.md +197 -197
  152. package/guides/claude-native.md +101 -0
  153. package/guides/host-ledger.md +3 -1
  154. package/guides/participant.md +14 -7
  155. package/guides/pi-native-delivery.md +43 -0
  156. package/package.json +4 -3
  157. package/dist/claude-hook.d.ts +0 -10
  158. package/dist/claude-hook.js +0 -60
  159. package/dist/paseo-connection.d.ts +0 -15
  160. package/dist/paseo-connection.js +0 -141
  161. package/dist/presence.d.ts +0 -2
  162. package/dist/presence.js +0 -24
  163. package/dist/presented.d.ts +0 -2
  164. package/dist/presented.js +0 -10
  165. package/dist/state-cell.d.ts +0 -14
  166. package/dist/wake-port.d.ts +0 -23
  167. package/dist/wake-port.js +0 -27
  168. /package/dist/{state-cell.js → packages/agent-delivery/src/types.js} +0 -0
@@ -1,16 +1,14 @@
1
1
  import type { WakeRouteKind } from './model.js';
2
- export type PresenceChannel = 'claude-code' | 'codex' | 'opencode' | 'pi' | 'paseo' | 'unknown';
2
+ import type { HarnessSessionChannel } from './participant-identity.js';
3
+ export type PresenceChannel = HarnessSessionChannel | 'unknown';
3
4
  export interface PresenceRecord {
4
5
  readonly location: string;
5
6
  readonly participant: string;
6
7
  readonly session: string;
7
8
  readonly channel: PresenceChannel;
8
- readonly route?: {
9
- readonly kind: WakeRouteKind;
10
- readonly address: Readonly<Record<string, string>>;
11
- };
12
9
  readonly updatedAt?: number;
13
10
  readonly epoch?: number;
11
+ readonly cancelledThrough?: number;
14
12
  }
15
13
  export type PresenceKey = Pick<PresenceRecord, 'location' | 'participant' | 'session' | 'channel'>;
16
14
  export interface PresenceLookup {
@@ -38,12 +36,24 @@ export type PresenceClaimResult = {
38
36
  readonly record: PresenceRecord;
39
37
  readonly error: unknown;
40
38
  };
39
+ export type NativeDeliveryEvidence = {
40
+ readonly payload: string;
41
+ readonly epoch: number;
42
+ } & ({
43
+ readonly harness: 'claude';
44
+ readonly endpoint: string;
45
+ } | {
46
+ readonly harness: 'opencode';
47
+ readonly inputId: string;
48
+ });
41
49
  export interface EvidenceRecord {
50
+ readonly bindingEpoch?: number;
51
+ readonly nativeDelivery?: NativeDeliveryEvidence;
42
52
  readonly location: string;
43
53
  readonly participant: string;
44
54
  readonly session: string;
45
55
  readonly activity: string;
46
- readonly kind: 'wake' | 'presentation';
56
+ readonly kind: 'wake' | 'presentation' | 'lifecycle';
47
57
  readonly outcome: string;
48
58
  readonly at?: number;
49
59
  readonly expiresAt?: number;
@@ -53,25 +63,33 @@ export interface EvidenceRecord {
53
63
  readonly message?: string;
54
64
  readonly diagnostic?: unknown;
55
65
  readonly claimToken?: string;
66
+ readonly ownerPid?: number;
67
+ readonly unknownSource?: 'interrupted' | 'transport';
56
68
  }
69
+ /** Presentation and lifecycle claims only: wake attempts are acquired by claimWakeAttempt. */
57
70
  export interface EvidenceClaim {
58
71
  readonly location: string;
59
72
  readonly participant: string;
60
73
  readonly session: string;
61
74
  readonly activity: string;
62
- readonly kind: EvidenceRecord['kind'];
75
+ readonly kind: 'presentation' | 'lifecycle';
63
76
  readonly leaseMs: number;
64
- readonly claimToken?: string;
65
77
  readonly now?: number;
66
78
  }
67
79
  /** Release metadata and includeReleased are diagnostic-only; default evidence reads stay behavior-safe. */
68
- export interface EvidenceRelease extends Omit<EvidenceClaim, 'leaseMs'> {
80
+ export interface EvidenceRelease {
81
+ readonly location: string;
82
+ readonly participant: string;
83
+ readonly session: string;
84
+ readonly activity: string;
85
+ readonly kind: EvidenceRecord['kind'];
69
86
  readonly claimToken: string;
70
87
  readonly routeKind?: WakeRouteKind;
71
88
  readonly attemptN?: number;
72
89
  readonly signature?: string;
73
90
  readonly message?: string;
74
91
  readonly diagnostic?: unknown;
92
+ readonly now?: number;
75
93
  }
76
94
  export interface EvidenceLookup {
77
95
  readonly location?: string;
@@ -91,46 +109,28 @@ export interface WakeAttention {
91
109
  readonly actIndex: number;
92
110
  readonly recipient: string;
93
111
  }
94
- export interface WakeDispatchLease {
95
- readonly leaseId: string;
96
- readonly expiresAt: number;
97
- readonly phase: 'claimed' | 'dispatching';
98
- readonly routeKind?: WakeRouteKind;
99
- readonly attemptN?: number;
100
- readonly session?: string;
112
+ /** One wake attempt row carries attention-wide exclusion and session-specific history. */
113
+ export interface WakeAttemptClaimInput {
114
+ readonly attention: WakeAttention;
115
+ readonly session: string;
116
+ readonly routeKind: WakeRouteKind;
117
+ readonly leaseMs: number;
118
+ readonly now?: number;
101
119
  }
102
- export type WakeDispatchClaim = {
103
- readonly type: 'acquired';
104
- readonly leaseId: string;
120
+ export type WakeAttemptClaim = {
121
+ readonly status: 'acquired';
122
+ readonly claimToken: string;
123
+ readonly attemptN: number;
124
+ } | {
125
+ readonly status: 'busy';
126
+ readonly record: EvidenceRecord;
105
127
  } | {
106
- readonly type: 'busy';
128
+ readonly status: 'terminal';
129
+ readonly record: EvidenceRecord;
107
130
  } | {
108
- readonly type: 'ambiguous';
109
- readonly lease: WakeDispatchLease;
131
+ readonly status: 'degraded';
132
+ readonly error: unknown;
110
133
  };
111
- export interface WakeDispatchClaimInput {
112
- readonly attention: WakeAttention;
113
- readonly leaseId: string;
114
- readonly leaseMs: number;
115
- readonly session?: string;
116
- readonly at?: number;
117
- }
118
- export interface WakeDispatchTransitionInput {
119
- readonly attention: WakeAttention;
120
- readonly leaseId: string;
121
- readonly phase: WakeDispatchLease['phase'];
122
- readonly leaseMs: number;
123
- readonly routeKind?: WakeRouteKind;
124
- readonly attemptN?: number;
125
- readonly session?: string;
126
- readonly at?: number;
127
- }
128
- export interface WakeDispatchReleaseInput {
129
- readonly attention: WakeAttention;
130
- readonly leaseId: string;
131
- readonly session?: string;
132
- readonly at?: number;
133
- }
134
134
  export interface WakeAttemptLookup {
135
135
  readonly attention?: WakeAttention;
136
136
  readonly session?: string;
@@ -147,8 +147,10 @@ export type ClaimResult = {
147
147
  readonly error: unknown;
148
148
  };
149
149
  export interface HostLedgerPort {
150
+ withClaimLock<T>(fn: () => Promise<T>, signal?: AbortSignal): Promise<T>;
150
151
  claimPresence(input: PresenceRecord, signal?: AbortSignal): Promise<PresenceClaimResult>;
151
152
  ensurePresence(input: PresenceRecord): Promise<PresenceResult>;
153
+ suppressPresence(input: PresenceRecord, through: number): Promise<boolean>;
152
154
  removePresence(input: PresenceKey): Promise<void>;
153
155
  removePresenceIfUnchanged(input: PresenceRecord): Promise<boolean>;
154
156
  listPresence(input: PresenceLookup): Promise<readonly PresenceRecord[]>;
@@ -159,11 +161,14 @@ export interface HostLedgerPort {
159
161
  }): Promise<void>;
160
162
  listEvidence(input: EvidenceLookup): Promise<readonly EvidenceRecord[]>;
161
163
  listWakeAttempts(input?: WakeAttemptLookup): Promise<readonly EvidenceRecord[]>;
162
- appendWakeAttempt(input: EvidenceRecord & {
164
+ prepareNativeWake(input: EvidenceRecord & {
163
165
  readonly claimToken: string;
164
- }): Promise<void>;
165
- claimWakeDispatch(input: WakeDispatchClaimInput): Promise<WakeDispatchClaim>;
166
- transitionWakeDispatch(input: WakeDispatchTransitionInput): Promise<boolean>;
167
- releaseWakeDispatch(input: WakeDispatchReleaseInput): Promise<void>;
166
+ readonly routeKind: WakeRouteKind;
167
+ readonly nativeDelivery: NativeDeliveryEvidence;
168
+ }): Promise<boolean>;
169
+ confirmWakeAdmission(input: EvidenceRecord & {
170
+ readonly claimToken: string;
171
+ }): Promise<boolean>;
172
+ claimWakeAttempt(input: WakeAttemptClaimInput): Promise<WakeAttemptClaim>;
168
173
  gcEvidence(input: EvidenceGc): Promise<void>;
169
174
  }
package/dist/inbox.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { type InboxMembership } from './model.js';
2
- export declare function hostLedgerForEnv(env: NodeJS.ProcessEnv): import("./host-ledger-file-adapter.js").FileHostLedgerPort;
3
2
  export interface PendingWaitOptions {
4
3
  signal?: AbortSignal;
5
4
  /** Local delivery suppression, never persisted as evidence of receipt. */
@@ -15,5 +14,3 @@ export interface SessionPendingObserver {
15
14
  }
16
15
  /** Session-scoped projection over process-shared storage change detectors. */
17
16
  export declare function observeSessionPending(sessionId: string, suppliedEnv?: NodeJS.ProcessEnv): Promise<SessionPendingObserver>;
18
- /** Finite one-shot consumers retain their API; Pi owns a long-lived observer instead. */
19
- export declare function waitForSessionPending(sessionId: string, timeoutMs: number, options?: PendingWaitOptions, env?: NodeJS.ProcessEnv): Promise<InboxMembership[]>;
package/dist/inbox.js CHANGED
@@ -1,13 +1,8 @@
1
- import { hostLedgerRoot } from './host-ledger-root.js';
2
1
  import { openSquareArtifact, observeSquareChanges } from './square-file-adapter.js';
3
- import { createHostLedgerPort } from './host-ledger-file-adapter.js';
4
- import { canonicalFilePath } from './file-changes.js';
5
- import { pendingAtBoundary, presentationSuppressesWake, projectPresentation, projectPresentationEvidence, projectSessionBindings } from './square-projections.js';
6
- import { formatActivityId } from './square-core.js';
2
+ import { hostLedgerForEnv } from './registry.js';
3
+ import { canonicalPath } from './canonical-path.js';
4
+ import { projectBoundaryEligibility, projectPresentation, projectSessionBindings } from './square-projections.js';
7
5
  import { WATCH_STALE_MS } from './runtime.js';
8
- export function hostLedgerForEnv(env) {
9
- return createHostLedgerPort({ rootPath: hostLedgerRoot(env) });
10
- }
11
6
  function notificationKey(membership, actIndex) {
12
7
  return membership.squarePath + '\0' + membership.name.toLocaleLowerCase() + '\0' + actIndex;
13
8
  }
@@ -72,7 +67,7 @@ export async function observeSessionPending(sessionId, suppliedEnv = process.env
72
67
  // Subscribe/capture the ledger BEFORE discovering bindings; a concurrent join cannot fall in a gap.
73
68
  const sources = await Promise.all([capture(ledger.presence, 'presence', signal), capture(ledger.evidence, 'evidence', signal)]);
74
69
  const bindings = await projectSessionBindings({ hostLedger, sessionId });
75
- const paths = new Set(await Promise.all(bindings.map((binding) => canonicalFilePath(binding.location))));
70
+ const paths = new Set(await Promise.all(bindings.map((binding) => canonicalPath(binding.location))));
76
71
  for (const [file, observer] of artifacts) {
77
72
  if (!paths.has(file)) {
78
73
  observer.close();
@@ -91,18 +86,16 @@ export async function observeSessionPending(sessionId, suppliedEnv = process.env
91
86
  }
92
87
  sources.push(...await Promise.all([...artifacts.values()].map((observer) => capture(observer, 'artifact', signal))));
93
88
  const inbox = await sessionInbox(sessionId, env, signal);
94
- const pending = [];
95
- for (const membership of pendingAtBoundary(inbox)) {
96
- const evidence = await projectPresentationEvidence({ hostLedger, location: membership.squarePath, participant: membership.name, sessionId });
97
- const notifications = membership.notifications.filter((notification) => !options.excludeKeys?.has(notificationKey(membership, notification.actIndex)) &&
98
- !presentationSuppressesWake(evidence.filter((row) => row.activity === formatActivityId(notification.actIndex))));
99
- if (notifications.length > 0)
100
- pending.push({ ...membership, notifications });
101
- }
102
- const eligible = JSON.stringify(pending.flatMap((membership) => membership.notifications.map((note) => notificationKey(membership, note.actIndex))).sort());
103
- if (previousEligible !== undefined && previousEligible !== eligible)
89
+ const eligible = await projectBoundaryEligibility({ hostLedger, sessionId, inbox });
90
+ const excludeKeys = options.excludeKeys;
91
+ const pending = excludeKeys === undefined ? eligible : eligible.flatMap((membership) => {
92
+ const notifications = membership.notifications.filter((notification) => !excludeKeys.has(notificationKey(membership, notification.actIndex)));
93
+ return notifications.length === 0 ? [] : [{ ...membership, notifications }];
94
+ });
95
+ const eligibleKeys = JSON.stringify(pending.flatMap((membership) => membership.notifications.map((note) => notificationKey(membership, note.actIndex))).sort());
96
+ if (previousEligible !== undefined && previousEligible !== eligibleKeys)
104
97
  canReturn = true;
105
- previousEligible = eligible;
98
+ previousEligible = eligibleKeys;
106
99
  if (signal.aborted)
107
100
  return [];
108
101
  if (canReturn && pending.length > 0)
@@ -167,13 +160,3 @@ export async function observeSessionPending(sessionId, suppliedEnv = process.env
167
160
  },
168
161
  };
169
162
  }
170
- /** Finite one-shot consumers retain their API; Pi owns a long-lived observer instead. */
171
- export async function waitForSessionPending(sessionId, timeoutMs, options = {}, env = process.env) {
172
- const observer = await observeSessionPending(sessionId, env);
173
- try {
174
- return await observer.wait(timeoutMs, options);
175
- }
176
- finally {
177
- observer.close();
178
- }
179
- }
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { Square } from './square-wiring.js';
2
2
  export { createSquareApplication } from './square-application.js';
3
3
  export { SquareError } from './model.js';
4
- export { bindCurrentParticipant, squareAssignedParticipantName, unbindCurrentParticipant } from './registry.js';
4
+ export { squareAssignedParticipantName } from './registry.js';
5
5
  export { createDefaultWakeTransport } from './notifications.js';
6
6
  export { createHostLedgerPort, FileHostLedgerPort } from './host-ledger-file-adapter.js';
7
7
  export type { HostLedgerPort, PresenceRecord, EvidenceRecord } from './host-ledger.js';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export { Square } from './square-wiring.js';
2
2
  export { createSquareApplication } from './square-application.js';
3
3
  export { SquareError } from './model.js';
4
- export { bindCurrentParticipant, squareAssignedParticipantName, unbindCurrentParticipant } from './registry.js';
4
+ export { squareAssignedParticipantName } from './registry.js';
5
5
  export { createDefaultWakeTransport } from './notifications.js';
6
6
  export { createHostLedgerPort, FileHostLedgerPort } from './host-ledger-file-adapter.js';
package/dist/list.js CHANGED
@@ -15,9 +15,6 @@ const CONTEXT_PREVIEW_LINES = 2;
15
15
  const PARTICIPANT_PREVIEW_COUNT = 3;
16
16
  const DISPLAY_CHARACTER_LIMIT = 160;
17
17
  const LIST_SKIP_DIRS = new Set(['.git', 'node_modules', 'dist']);
18
- function contextLines(lines) {
19
- return lines.map((line) => line.trim()).filter(Boolean);
20
- }
21
18
  async function readSquareListItem(filePath, root) {
22
19
  let stat;
23
20
  try {
@@ -34,7 +31,7 @@ async function readSquareListItem(filePath, root) {
34
31
  return {
35
32
  path: relative,
36
33
  lastActiveAt: stat.mtimeMs,
37
- context: contextLines([...projection.context]),
34
+ context: [...projection.context].map((line) => line.trim()).filter(Boolean),
38
35
  participants: [...projection.participants],
39
36
  activities: projection.activities,
40
37
  };
package/dist/mcp.js CHANGED
@@ -3,45 +3,23 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
3
3
  import { z } from 'zod/v4';
4
4
  import { SquareError, isSquareError } from './model.js';
5
5
  import { createSquareApplication } from './square-application.js';
6
- const activityIdSchema = { type: 'string', pattern: '^act/(0|[1-9][0-9]*)$' };
7
- const stringArraySchema = { type: 'array', items: { type: 'string' } };
8
- function objectSchema(properties, required = []) { return { properties, required }; }
6
+ import { toPublicActivity } from './views.js';
7
+ const activityId = z.string().regex(/^act\/(0|[1-9][0-9]*)$/);
8
+ const stringArray = z.array(z.string());
9
9
  const TOOL_DEFINITIONS = [
10
- { name: 'join', description: 'Join the square as the configured participant.', inputSchema: objectSchema({ takeover: { type: 'boolean' } }) },
11
- { name: 'express', description: 'Express a message or embodied action in the square.', inputSchema: objectSchema({ body: { type: 'string' }, mentions: stringArraySchema, reach: { type: 'string', enum: ['bell'] }, reply: activityIdSchema, force: { type: 'boolean' }, noWait: { type: 'boolean' } }, ['body']) },
12
- { name: 'catch', description: 'Catch and consume activities perceptible to the configured participant.', inputSchema: objectSchema({ id: activityIdSchema, idle: { type: 'number', minimum: 0 }, from: stringArraySchema, mention: { type: 'boolean' }, limit: { type: 'integer', minimum: 1 } }) },
13
- { name: 'history', description: 'Read the activity stream using stable activity-id cursors.', inputSchema: objectSchema({ limit: { type: 'integer', minimum: 1 }, order: { type: 'string', enum: ['asc', 'desc'] }, before: activityIdSchema, after: activityIdSchema, grep: { type: 'string' }, fixed: { type: 'string' }, from: stringArraySchema, mention: { type: 'string' } }) },
14
- { name: 'listen', description: 'Listen for future activity from a participant.', inputSchema: objectSchema({ target: { type: 'string' } }, ['target']) },
15
- { name: 'ignore', description: 'Stop listening for future activity from a participant.', inputSchema: objectSchema({ target: { type: 'string' } }, ['target']) },
16
- { name: 'listening', description: 'List participants this participant is listening to.', inputSchema: objectSchema({}) },
17
- { name: 'hold', description: 'Raise a hand to hold the square.', inputSchema: objectSchema({ reason: { type: 'string' } }) },
18
- { name: 'resume', description: 'Release the current hold.', inputSchema: objectSchema({}) },
19
- { name: 'done', description: 'Leave the square without adding a message.', inputSchema: objectSchema({}) },
20
- { name: 'status', description: 'Read the public status of the square.', inputSchema: objectSchema({}) },
21
- { name: 'participants', description: 'List participants standing in or done with the square.', inputSchema: objectSchema({}) },
10
+ { name: 'join', description: 'Join the square as the configured participant.', inputSchema: z.object({ takeover: z.boolean().optional() }).strict() },
11
+ { name: 'express', description: 'Express a message or embodied action in the square.', inputSchema: z.object({ body: z.string(), mentions: stringArray.optional(), reach: z.enum(['bell']).optional(), reply: activityId.optional(), force: z.boolean().optional(), noWait: z.boolean().optional() }).strict() },
12
+ { name: 'catch', description: 'Catch and consume activities perceptible to the configured participant.', inputSchema: z.object({ id: activityId.optional(), idle: z.number().finite().min(0).optional(), from: stringArray.optional(), mention: z.boolean().optional(), limit: z.number().int().min(1).optional() }).strict() },
13
+ { name: 'history', description: 'Read the activity stream using stable activity-id cursors.', inputSchema: z.object({ limit: z.number().int().min(1).optional(), order: z.enum(['asc', 'desc']).optional(), before: activityId.optional(), after: activityId.optional(), grep: z.string().optional(), fixed: z.string().optional(), from: stringArray.optional(), mention: z.string().optional() }).strict() },
14
+ { name: 'listen', description: 'Listen for future activity from a participant.', inputSchema: z.object({ target: z.string() }).strict() },
15
+ { name: 'ignore', description: 'Stop listening for future activity from a participant.', inputSchema: z.object({ target: z.string() }).strict() },
16
+ { name: 'listening', description: 'List participants this participant is listening to.', inputSchema: z.object({}).strict() },
17
+ { name: 'hold', description: 'Raise a hand to hold the square.', inputSchema: z.object({ reason: z.string().optional() }).strict() },
18
+ { name: 'resume', description: 'Release the current hold.', inputSchema: z.object({}).strict() },
19
+ { name: 'done', description: 'Leave the square without adding a message.', inputSchema: z.object({}).strict() },
20
+ { name: 'status', description: 'Read the public status of the square.', inputSchema: z.object({}).strict() },
21
+ { name: 'participants', description: 'List participants standing in or done with the square.', inputSchema: z.object({}).strict() },
22
22
  ];
23
- function zodInputSchema(schema) {
24
- const shape = {};
25
- for (const [key, property] of Object.entries(schema.properties)) {
26
- let field;
27
- if (property.type === 'string' && property.enum !== undefined)
28
- field = z.enum(property.enum);
29
- else if (property.type === 'string')
30
- field = property.pattern === undefined ? z.string() : z.string().regex(new RegExp(property.pattern));
31
- else if (property.type === 'boolean')
32
- field = z.boolean();
33
- else if (property.type === 'number')
34
- field = z.number().finite().min(property.minimum ?? -Infinity);
35
- else if (property.type === 'integer')
36
- field = z.number().int().min(property.minimum ?? -Infinity);
37
- else if (property.type === 'array')
38
- field = z.array(z.string());
39
- else
40
- throw new Error(`Unsupported input schema type: ${property.type}`);
41
- shape[key] = schema.required.includes(key) ? field : field.optional();
42
- }
43
- return z.object(shape).strict();
44
- }
45
23
  function isObject(value) { return value !== null && typeof value === 'object' && !Array.isArray(value); }
46
24
  function publicActivity(activity) { return { ...activity }; }
47
25
  function publicResult(name, value) {
@@ -56,13 +34,13 @@ function publicResult(name, value) {
56
34
  doneCount: rest.doneCount,
57
35
  hold: rest.holdActive ? { by: rest.holdActor, ...(rest.holdReason === undefined ? {} : { reason: rest.holdReason }), ...(rest.holdAt === undefined ? {} : { at: rest.holdAt }) } : null,
58
36
  participants: participants.filter((participant) => participant.state !== 'not joined').map((participant) => ({ name: participant.name, state: participant.state, presence: participant.presence, activityCount: participant.activityCount, ...(participant.lastActiveAt === undefined ? {} : { lastActiveAt: participant.lastActiveAt }), unreadActivityCount: participant.unreadActivityCount, pendingMentionCount: participant.pendingMentionCount, listening: participant.listening })),
59
- ...(latestAct === undefined ? {} : { latestActivity: publicActivity({ id: `act/${latestAct.index}`, at: latestAct.at, kind: latestAct.kind, actor: latestAct.actor ?? '', ...('body' in latestAct && latestAct.body !== undefined ? { body: latestAct.body } : {}), mentions: 'mentions' in latestAct ? latestAct.mentions ?? [] : [], ...('target' in latestAct ? { target: latestAct.target } : {}), ...(latestAct.kind === 'say' && latestAct.reach !== undefined ? { reach: latestAct.reach } : {}), ...(latestAct.kind === 'say' && latestAct.reply !== undefined ? { reply: `act/${latestAct.reply}` } : {}) }) }),
37
+ ...(latestAct === undefined ? {} : { latestActivity: publicActivity(toPublicActivity(latestAct)) }),
60
38
  now: rest.now,
61
39
  };
62
40
  }
63
41
  if (name === 'express' || name === 'hold' || name === 'resume' || name === 'done') {
64
42
  const result = value;
65
- return { ...(result.activity === undefined ? {} : { activity: result.activity === null ? null : publicActivity(result.activity) }), ...(result.waited === undefined ? {} : { waited: result.waited }) };
43
+ return { ...(result.activity === undefined ? {} : { activity: result.activity === null ? null : publicActivity(result.activity) }), ...(result.waited === undefined ? {} : { waited: result.waited }), ...(result.replyAuthorNotHere === undefined ? {} : { replyAuthorNotHere: result.replyAuthorNotHere }) };
66
44
  }
67
45
  if (name === 'history')
68
46
  return { activities: value };
@@ -98,7 +76,7 @@ export function createSquareMcpServer(options) {
98
76
  instructions: 'The catch tool consumes activities. Use history with act/<index> cursors for read-only archive access.',
99
77
  });
100
78
  for (const tool of TOOL_DEFINITIONS) {
101
- server.registerTool(tool.name, { description: tool.description, inputSchema: zodInputSchema(tool.inputSchema) }, async (args, extra) => {
79
+ server.registerTool(tool.name, { description: tool.description, inputSchema: tool.inputSchema }, async (args, extra) => {
102
80
  try {
103
81
  const value = await operation(app, tool.name, args, { signal: extra.signal });
104
82
  const projected = publicResult(tool.name, value);
@@ -54,14 +54,6 @@ function codeSpanRanges(body) {
54
54
  }
55
55
  return ranges;
56
56
  }
57
- function codePointBefore(body, index) {
58
- if (index <= 0)
59
- return undefined;
60
- const unit = body.charCodeAt(index - 1);
61
- if (unit >= 0xdc00 && unit <= 0xdfff && index >= 2)
62
- return String.fromCodePoint(body.codePointAt(index - 2));
63
- return String.fromCodePoint(unit);
64
- }
65
57
  export function scanMentionCandidates(body) {
66
58
  const spans = codeSpanRanges(body);
67
59
  const found = [];
@@ -70,7 +62,9 @@ export function scanMentionCandidates(body) {
70
62
  const start = match.index;
71
63
  if (spans.some(([from, to]) => start >= from && start < to))
72
64
  continue;
73
- const previous = codePointBefore(body, start);
65
+ const unit = start <= 0 ? undefined : body.charCodeAt(start - 1);
66
+ const previous = unit === undefined ? undefined
67
+ : unit >= 0xdc00 && unit <= 0xdfff && start >= 2 ? String.fromCodePoint(body.codePointAt(start - 2)) : String.fromCodePoint(unit);
74
68
  if (previous !== undefined && BOUNDARY_BLOCKER.test(previous))
75
69
  continue;
76
70
  const token = match[1];
package/dist/model.d.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  import type { Act, ActivityId } from './square-core.js';
2
2
  export { formatActivityId, parseActivityId } from './square-core.js';
3
3
  export type { Act, ActivityId, Audience, Reach } from './square-core.js';
4
- export declare const WAKE_ROUTE_KINDS: readonly ["opencode-server", "codex-queue", "claude-native", "pi-extension", "paseo"];
4
+ export declare const WAKE_ROUTE_KINDS: readonly ["opencode-server", "codex-queue", "claude-native", "paseo"];
5
5
  export type WakeRouteKind = typeof WAKE_ROUTE_KINDS[number];
6
6
  export declare function isWakeRouteKind(value: unknown): value is WakeRouteKind;
7
7
  export interface WakeRoute {
8
+ /** Ownership generation, when the receiver publishes a fenced route. */
9
+ epoch?: number;
8
10
  location: string;
9
11
  participant: string;
10
12
  sessionId: string;
@@ -20,6 +22,13 @@ export type InternalSquareErrorCode = SquareErrorCode | 'not_found' | 'cap_reach
20
22
  export interface SquareErrorFacts {
21
23
  /** Express admission rejected before any activity was committed. */
22
24
  activityUnsent?: true;
25
+ /** Why a name could not be resolved: never in the roster, no longer standing in it, a bell
26
+ * colliding with a mention, an @name that matches nobody, or an activity catch cannot address. */
27
+ reason?: 'never_joined' | 'not_standing' | 'bell_mention_conflict' | 'unmatched_mention' | 'not_addressed';
28
+ /** The activity id a `catch --id` refusal named. */
29
+ activityId?: string;
30
+ /** Whether the artifact holds that activity at all; an unknown id needs an ordinary page. */
31
+ activityExists?: boolean;
23
32
  pending?: number;
24
33
  holder?: string;
25
34
  holdReason?: string;
@@ -62,7 +71,7 @@ export interface WatchLease {
62
71
  expiresAt: number;
63
72
  filter?: WatchLeaseFilter;
64
73
  }
65
- export type DirectedNotificationRoute = 'mention' | 'attention' | 'bell';
74
+ export type DirectedNotificationRoute = 'mention' | 'reply' | 'attention' | 'bell';
66
75
  export interface InboxNotification {
67
76
  actIndex: number;
68
77
  actor: string;
package/dist/model.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // Shared model and constants for Square.
2
2
  export { formatActivityId, parseActivityId } from './square-core.js';
3
- export const WAKE_ROUTE_KINDS = ['opencode-server', 'codex-queue', 'claude-native', 'pi-extension', 'paseo'];
3
+ export const WAKE_ROUTE_KINDS = ['opencode-server', 'codex-queue', 'claude-native', 'paseo'];
4
4
  export function isWakeRouteKind(value) {
5
5
  return typeof value === 'string' && WAKE_ROUTE_KINDS.includes(value);
6
6
  }
@@ -1,27 +1,16 @@
1
- import { planActNotifications, type WakeAdapter } from './delivery.js';
2
- import { type SquareState } from './model.js';
3
- import { matchesMentionTarget } from './runtime.js';
4
- import { type ActivityId } from './square-core.js';
1
+ import { type WakeAdapter } from './delivery.js';
5
2
  import type { WakeTransportPort } from './ports.js';
6
3
  export type { PlannedNotification } from './delivery.js';
7
- export { planActNotifications, matchesMentionTarget };
8
- export { notificationMessageId } from './delivery.js';
9
4
  export declare const PRIVILEGED_HOOK_BUDGET_MS = 3000;
10
5
  export declare function wakeGraceMs(env?: NodeJS.ProcessEnv): number;
11
- export declare function hasDeliveredNotification(squarePath: string, name: string, ref: number | ActivityId): Promise<boolean>;
12
- export declare function hasAttentionNotification(squarePath: string, name: string, ref: number | ActivityId, env?: NodeJS.ProcessEnv): Promise<boolean>;
13
- export declare function waitForDeliveredNotification(squarePath: string, name: string, ref: number | ActivityId, opts?: {
14
- timeoutMs?: number;
15
- signal?: AbortSignal;
16
- }): Promise<boolean>;
17
6
  interface ProcessNotificationOptions {
18
7
  adapters?: WakeAdapter[];
19
8
  env?: NodeJS.ProcessEnv;
20
9
  now?: () => number;
21
10
  }
22
11
  export declare function defaultWakeAdapters(): Promise<WakeAdapter[]>;
23
- export declare function createDefaultWakeTransport(hostLedger: import('./host-ledger.js').HostLedgerPort, clock: () => number, env?: NodeJS.ProcessEnv): Promise<WakeTransportPort>;
24
- export declare function createWakeTransport(adapters: readonly WakeAdapter[], hostLedger: import('./host-ledger.js').HostLedgerPort, clock: () => number): WakeTransportPort;
12
+ export declare function createDefaultWakeTransport(hostLedger: import('./host-ledger.js').HostLedgerPort, clock: () => number, env?: NodeJS.ProcessEnv, suppliedAdapters?: readonly WakeAdapter[]): Promise<WakeTransportPort>;
13
+ export declare function createWakeTransport(adapters: readonly WakeAdapter[], hostLedger: import('./host-ledger.js').HostLedgerPort, clock: () => number, nativePorts?: Readonly<Partial<Record<import('./model.js').WakeRouteKind, WakeTransportPort>>>): WakeTransportPort;
25
14
  export declare function processActNotificationsOnce(squarePath: string, actIndex: number, opts?: ProcessNotificationOptions): Promise<import("./ports.js").DeliveryResult>;
26
15
  /** Privileged hook fallback: sweep indexed squares plus the current cwd's local squares within one boundary budget. */
27
16
  export declare function sweepPrivilegedPending(cwd: string, env?: NodeJS.ProcessEnv, suppliedAdapters?: WakeAdapter[], deadline?: number, signal?: AbortSignal): Promise<void>;
@@ -31,7 +20,5 @@ export interface SweepPendingNotificationsOptions {
31
20
  limit?: number;
32
21
  dispatchCandidate?: (actIndex: number) => void | Promise<void>;
33
22
  }
34
- /** Select sweep candidates from one frozen snapshot and one delivery replay. */
35
- export declare function pendingNotificationSweepFromState(squarePath: string, state: SquareState, now: number, env: NodeJS.ProcessEnv, limit: number, deriveDelivery?: (snapshot: import('./model.js').SquareState) => ReturnType<typeof import('./delivery.js').deriveDeliveryModel>): Promise<number[]>;
36
23
  /** Select old pending attention at a bounded action boundary for an explicit executor. */
37
24
  export declare function sweepPendingNotifications(squarePath: string, opts?: SweepPendingNotificationsOptions): Promise<number[]>;