@astrosheep/square 0.3.106 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/dist/activity-feed.js +1 -1
  2. package/dist/artifact-store.d.ts +41 -0
  3. package/dist/artifact-store.js +272 -0
  4. package/dist/artifact.d.ts +8 -12
  5. package/dist/artifact.js +66 -343
  6. package/dist/attention-presentation.d.ts +33 -7
  7. package/dist/attention-presentation.js +33 -69
  8. package/dist/boundary-presentation.d.ts +0 -2
  9. package/dist/boundary-presentation.js +17 -22
  10. package/dist/catch-decisions.d.ts +2 -2
  11. package/dist/catch-decisions.js +14 -4
  12. package/dist/catch-selection.d.ts +8 -0
  13. package/dist/catch-selection.js +5 -0
  14. package/dist/channel-artifact.d.ts +10 -0
  15. package/dist/channel-artifact.js +55 -0
  16. package/dist/channel-delivery.d.ts +11 -0
  17. package/dist/channel-delivery.js +51 -0
  18. package/dist/channel-domain.d.ts +3 -0
  19. package/dist/channel-domain.js +83 -0
  20. package/dist/channel-facade.d.ts +76 -0
  21. package/dist/channel-facade.js +1 -0
  22. package/dist/channel-internal.d.ts +126 -0
  23. package/dist/channel-internal.js +440 -0
  24. package/dist/channel-metadata.d.ts +11 -0
  25. package/dist/channel-metadata.js +1 -0
  26. package/dist/channel-ports.d.ts +241 -0
  27. package/dist/channel-ports.js +1 -0
  28. package/dist/channel-presentation.d.ts +17 -0
  29. package/dist/channel-presentation.js +22 -0
  30. package/dist/channel-sqlite-schema.d.ts +13 -0
  31. package/dist/channel-sqlite-schema.js +81 -0
  32. package/dist/channel-transport.d.ts +62 -0
  33. package/dist/channel-transport.js +1 -0
  34. package/dist/channel-values.d.ts +27 -0
  35. package/dist/channel-values.js +90 -0
  36. package/dist/channel.d.ts +31 -21
  37. package/dist/channel.js +42 -74
  38. package/dist/claude-delivery-status.js +1 -1
  39. package/dist/claude-delivery.d.ts +2 -5
  40. package/dist/claude-delivery.js +56 -103
  41. package/dist/claude-mod.js +4 -4
  42. package/dist/claude-transport.d.ts +5 -0
  43. package/dist/claude-transport.js +58 -0
  44. package/dist/cli/context.d.ts +2 -2
  45. package/dist/cli/context.js +0 -1
  46. package/dist/cli/maintenance-commands.d.ts +0 -4
  47. package/dist/cli/maintenance-commands.js +2 -29
  48. package/dist/cli/program.js +1 -1
  49. package/dist/cli/registry.js +1 -2
  50. package/dist/codex-address.d.ts +1 -1
  51. package/dist/codex-queue.d.ts +2 -2
  52. package/dist/codex-queue.js +1 -3
  53. package/dist/decisions.js +3 -3
  54. package/dist/delivery-deferral.d.ts +9 -18
  55. package/dist/delivery-deferral.js +4 -4
  56. package/dist/delivery-health.d.ts +4 -4
  57. package/dist/delivery-health.js +11 -11
  58. package/dist/delivery-operations.d.ts +15 -2
  59. package/dist/delivery-operations.js +60 -30
  60. package/dist/delivery.d.ts +44 -33
  61. package/dist/delivery.js +49 -75
  62. package/dist/help.js +1 -6
  63. package/dist/http-delivery.d.ts +2 -2
  64. package/dist/http-delivery.js +2 -4
  65. package/dist/inbox.d.ts +10 -18
  66. package/dist/inbox.js +44 -53
  67. package/dist/index.d.ts +1 -1
  68. package/dist/index.js +1 -1
  69. package/dist/mcp.d.ts +2 -1
  70. package/dist/mcp.js +40 -70
  71. package/dist/message.d.ts +7 -0
  72. package/dist/message.js +23 -0
  73. package/dist/model.d.ts +41 -77
  74. package/dist/model.js +7 -139
  75. package/dist/neutral.d.ts +72 -0
  76. package/dist/neutral.js +27 -0
  77. package/dist/notifications.d.ts +2 -8
  78. package/dist/notifications.js +15 -50
  79. package/dist/operation-presentation.d.ts +1 -2
  80. package/dist/operation-presentation.js +1 -7
  81. package/dist/owner.d.ts +2 -2
  82. package/dist/owner.js +1 -1
  83. package/dist/paseo-delivery.d.ts +1 -1
  84. package/dist/paseo-delivery.js +2 -4
  85. package/dist/plugins/opencode/delivery.js +27 -42
  86. package/dist/ports.d.ts +46 -168
  87. package/dist/presentation-operations.d.ts +1 -14
  88. package/dist/presentation-operations.js +16 -16
  89. package/dist/presentation.d.ts +5 -5
  90. package/dist/presentation.js +1 -0
  91. package/dist/runtime.js +2 -2
  92. package/dist/search.js +1 -1
  93. package/dist/sqlite-channel.d.ts +15 -0
  94. package/dist/sqlite-channel.js +600 -0
  95. package/dist/sqlite-framing.d.ts +8 -0
  96. package/dist/sqlite-framing.js +31 -0
  97. package/dist/sqlite-square.d.ts +12 -13
  98. package/dist/sqlite-square.js +192 -248
  99. package/dist/sqlite-values.d.ts +21 -0
  100. package/dist/sqlite-values.js +78 -0
  101. package/dist/square-api.d.ts +1 -1
  102. package/dist/square-api.js +1 -1
  103. package/dist/square-core.d.ts +1 -1
  104. package/dist/square-domain.d.ts +21 -7
  105. package/dist/square-domain.js +93 -94
  106. package/dist/square-facade.d.ts +12 -66
  107. package/dist/square-index.d.ts +1 -1
  108. package/dist/square-index.js +1 -1
  109. package/dist/square-location.d.ts +6 -1
  110. package/dist/square-location.js +1 -1
  111. package/dist/square-receive.d.ts +2 -3
  112. package/dist/square-sqlite.d.ts +45 -44
  113. package/dist/square-sqlite.js +308 -764
  114. package/dist/square-tools.d.ts +8 -0
  115. package/dist/square-tools.js +39 -0
  116. package/dist/views.d.ts +6 -6
  117. package/dist/views.js +15 -9
  118. package/dist/wake-preview.d.ts +7 -0
  119. package/dist/wake-preview.js +15 -0
  120. package/dist/wake-sink.d.ts +2 -2
  121. package/dist/wake-sink.js +1 -1
  122. package/dist/wake-transports.d.ts +7 -0
  123. package/dist/wake-transports.js +42 -0
  124. package/guides/claude-native.md +13 -10
  125. package/guides/codex-delivery.md +3 -1
  126. package/guides/participant-ownership.md +1 -1
  127. package/guides/row-runtime-delivery-evidence.md +2 -0
  128. package/guides/row-storage-design.md +19 -44
  129. package/guides/square-api.md +137 -60
  130. package/package.json +1 -1
  131. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  132. package/plugins/claude/types.d.ts +1 -1
  133. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  134. package/plugins/opencode/delivery.ts +28 -41
  135. package/plugins/pi/square-pi.js +28 -51
  136. package/dist/square-legacy-framing.d.ts +0 -11
  137. package/dist/square-legacy-framing.js +0 -64
  138. package/dist/square-migration-host-ledger.d.ts +0 -26
  139. package/dist/square-migration-host-ledger.js +0 -169
  140. package/dist/square-migration.d.ts +0 -30
  141. package/dist/square-migration.js +0 -599
  142. package/guides/migration.md +0 -156
@@ -0,0 +1,241 @@
1
+ import type { Owner } from './neutral.js';
2
+ import type { MessageMetadata, JsonValue, WakeAddress } from './channel-values.js';
3
+ import type { MetadataMatch, DeliveryDeferralFact } from './delivery-deferral.js';
4
+ import type { AttemptOutcome } from './delivery-attempts.js';
5
+ import type { MessageId } from './message.js';
6
+ import type { EndpointGeneration } from './message.js';
7
+ export type { MessageId, EndpointGeneration };
8
+ /** One lower write's custody claim. Every Channel mutation carries exactly one. */
9
+ export type ChannelWriteAuthority = {
10
+ readonly kind: 'send';
11
+ readonly actor: string;
12
+ } | {
13
+ readonly kind: 'endpoint';
14
+ readonly participant: string;
15
+ readonly owner: Owner;
16
+ } | {
17
+ readonly kind: 'endpoint-cleanup';
18
+ readonly seat: EndpointRef;
19
+ } | {
20
+ readonly kind: 'participant';
21
+ readonly seat: EndpointRef;
22
+ };
23
+ /** The only storage surface a Channel is allowed to see: messages, endpoints and their custody. */
24
+ export interface ChannelArtifact {
25
+ read<T>(select: (facts: ChannelRead) => T, signal?: AbortSignal): Promise<{
26
+ readonly value: T;
27
+ readonly version: number;
28
+ }>;
29
+ transactAs<T>(authority: ChannelWriteAuthority, change: (facts: ChannelWrite) => T, signal?: AbortSignal): Promise<{
30
+ readonly value: T;
31
+ readonly changed: boolean;
32
+ }>;
33
+ claimDelivery(input: ChannelDeliveryClaimInput, signal?: AbortSignal): Promise<ChannelDeliveryClaimResult>;
34
+ prepareDelivery(input: ChannelDeliveryPreparation, signal?: AbortSignal): Promise<boolean>;
35
+ completeDelivery(input: ChannelDeliveryCompletion, signal?: AbortSignal): Promise<boolean>;
36
+ cancelDeliveries(seat: EndpointRef, through?: number, signal?: AbortSignal): Promise<boolean>;
37
+ changed(sinceVersion: number, timeoutMs: number, signal?: AbortSignal): Promise<boolean>;
38
+ close(): Promise<void>;
39
+ }
40
+ export interface ChannelHeader {
41
+ readonly revision: number;
42
+ readonly deliveryRevision: number;
43
+ readonly nextMessageIndex: number;
44
+ readonly lastMessageIndex: number;
45
+ }
46
+ /** One Channel message. Body and caller metadata have exactly one authoritative copy. */
47
+ export interface MessageFact {
48
+ readonly index: number;
49
+ readonly at: number;
50
+ readonly from: string;
51
+ readonly body: string;
52
+ readonly metadata?: MessageMetadata;
53
+ /** Sender-chosen preview projection; the stored body remains full. */
54
+ readonly preview?: string;
55
+ }
56
+ export interface MessageAddressFact {
57
+ readonly recipient: string;
58
+ readonly generation?: EndpointGeneration;
59
+ }
60
+ export interface MessageHead {
61
+ readonly index: number;
62
+ readonly at: number;
63
+ readonly from: string;
64
+ readonly recipient: string;
65
+ readonly generation?: EndpointGeneration;
66
+ }
67
+ export interface MessageFilter {
68
+ readonly afterIndex?: number;
69
+ readonly beforeIndex?: number;
70
+ readonly throughIndex?: number;
71
+ readonly indexes?: readonly number[];
72
+ readonly senders?: readonly string[];
73
+ readonly senderMatch?: 'identity' | 'exact';
74
+ readonly afterAt?: number;
75
+ readonly metadataMatch?: MetadataMatch;
76
+ readonly search?: {
77
+ readonly text: string;
78
+ readonly mode: 'literal' | 'regex';
79
+ };
80
+ readonly unseenOnly?: true;
81
+ readonly currentGeneration?: true;
82
+ }
83
+ export interface MessageDraft {
84
+ readonly from: string;
85
+ readonly at: number;
86
+ readonly body: string;
87
+ readonly metadata?: MessageMetadata;
88
+ readonly preview?: string;
89
+ readonly to: readonly MessageAddressFact[];
90
+ }
91
+ export interface EndpointFact {
92
+ readonly participant: string;
93
+ readonly owner: Owner;
94
+ readonly generation: EndpointGeneration;
95
+ readonly attachment: 'attached' | 'detached';
96
+ readonly wakeAddress?: WakeAddress;
97
+ }
98
+ export type EndpointRef = Pick<EndpointFact, 'participant' | 'owner' | 'generation'>;
99
+ /** Upper-owned namespace association; Channel never interprets its selector. */
100
+ export interface ReceiveClaimFact {
101
+ readonly namespace: string;
102
+ readonly id: string;
103
+ readonly owner: string;
104
+ readonly generation: EndpointGeneration;
105
+ readonly selector: JsonValue;
106
+ /** Message-scoped suppression coverage; Channel owns this list, the selector stays opaque. */
107
+ readonly suppressed: readonly number[];
108
+ readonly heartbeatAt: number;
109
+ readonly expiresAt: number;
110
+ }
111
+ export interface ChannelDeliveryEvidence {
112
+ readonly participant: string;
113
+ readonly generation: EndpointGeneration;
114
+ readonly message: MessageId;
115
+ readonly kind: 'wake' | 'presentation';
116
+ readonly outcome: AttemptOutcome;
117
+ readonly attemptN: number;
118
+ readonly at: number;
119
+ readonly expiresAt?: number;
120
+ readonly claimToken?: string;
121
+ readonly route?: WakeAddress;
122
+ readonly signature?: string;
123
+ readonly messageText?: string;
124
+ readonly diagnostic?: JsonValue;
125
+ readonly correlation?: {
126
+ readonly harness: 'claude' | 'opencode' | 'pi';
127
+ readonly token?: string;
128
+ readonly messageId?: string;
129
+ readonly payloadHash?: string;
130
+ };
131
+ /** Exact bytes prepared for this attempt; declared before the external effect. */
132
+ readonly coverage?: 'full' | 'clipped';
133
+ readonly description?: string;
134
+ readonly nativeObservations?: readonly {
135
+ readonly eventId: string;
136
+ readonly messageId: string;
137
+ readonly state: 'held' | 'released' | 'dropped';
138
+ readonly reason?: string;
139
+ readonly observedAt: number;
140
+ }[];
141
+ }
142
+ export interface ChannelDeliveryClaimInput {
143
+ readonly seat: EndpointRef;
144
+ readonly message: MessageId;
145
+ readonly kind: 'wake' | 'presentation';
146
+ readonly observationOf?: string;
147
+ readonly route?: WakeAddress;
148
+ readonly at: number;
149
+ readonly leaseMs: number;
150
+ readonly claimToken: string;
151
+ }
152
+ export type ChannelDeliveryClaimResult = {
153
+ readonly status: 'acquired';
154
+ readonly claimToken: string;
155
+ readonly attemptN: number;
156
+ } | {
157
+ readonly status: 'busy' | 'terminal' | 'stale' | 'deferred';
158
+ readonly evidence?: ChannelDeliveryEvidence;
159
+ };
160
+ export interface ChannelDeliveryPreparation {
161
+ readonly seat: EndpointRef;
162
+ readonly claimToken: string;
163
+ readonly correlation: NonNullable<ChannelDeliveryEvidence['correlation']>;
164
+ readonly coverage: 'full' | 'clipped';
165
+ readonly description?: string;
166
+ }
167
+ export interface ChannelDeliveryCompletion {
168
+ readonly seat: EndpointRef;
169
+ readonly message: MessageId;
170
+ readonly claimToken: string;
171
+ readonly outcome: Exclude<ChannelDeliveryEvidence['outcome'], 'dispatching'>;
172
+ readonly at: number;
173
+ readonly signature?: string;
174
+ readonly messageText?: string;
175
+ readonly diagnostic?: JsonValue;
176
+ readonly correlation?: ChannelDeliveryEvidence['correlation'];
177
+ }
178
+ /** Lower read capabilities. Nothing here describes Agora events, routes or attention. */
179
+ export interface ChannelRead {
180
+ header(): ChannelHeader;
181
+ message(index: number): MessageFact | undefined;
182
+ messages(indexes: readonly number[]): readonly MessageFact[];
183
+ messageHeads(filter: MessageFilter, page: {
184
+ readonly edge: 'oldest' | 'newest';
185
+ readonly limit: number;
186
+ }): {
187
+ readonly items: readonly MessageFact[];
188
+ readonly hasMore: boolean;
189
+ };
190
+ messageCount(filter: MessageFilter): number;
191
+ addresses(index: number): readonly MessageAddressFact[];
192
+ receipt(owner: string, index: number): {
193
+ readonly at: number;
194
+ } | undefined;
195
+ receiptFacts(owner: string, afterIndex: number): {
196
+ readonly latestAt?: number;
197
+ readonly latestActorMessageIndex?: number;
198
+ };
199
+ directed(recipient: string, filter: MessageFilter, page: {
200
+ readonly edge: 'oldest' | 'newest';
201
+ readonly limit: number;
202
+ }): {
203
+ readonly items: readonly MessageHead[];
204
+ readonly hasMore: boolean;
205
+ };
206
+ directedCount(recipient: string, filter: MessageFilter): number;
207
+ endpoint(requested: string): EndpointFact | undefined;
208
+ endpointsForOwner(owner: Owner): readonly EndpointFact[];
209
+ endpoints(): readonly EndpointFact[];
210
+ deliveryEvidence(participant: string, generation: EndpointGeneration, message?: MessageId): readonly ChannelDeliveryEvidence[];
211
+ deliveryByToken(claimToken: string): ChannelDeliveryEvidence | undefined;
212
+ cancelledThrough(participant: string, generation: EndpointGeneration): number | undefined;
213
+ deliveryDeferrals(participant: string, generation: EndpointGeneration): readonly DeliveryDeferralFact[];
214
+ receiveClaims(owner: string, namespace?: string): readonly ReceiveClaimFact[];
215
+ claimLease(owner: string, namespace?: string): ReceiveClaimFact | undefined;
216
+ /** Any live claim of this endpoint covering the message suppresses its pending eligibility. */
217
+ suppressed(owner: string, generation: EndpointGeneration, index: number, now: number): boolean;
218
+ }
219
+ export interface ChannelWrite extends ChannelRead {
220
+ publishMessage(draft: MessageDraft): MessageFact;
221
+ storeEndpoint(endpoint: EndpointFact): boolean;
222
+ updateWakeAddress(endpoint: EndpointRef, wakeAddress?: WakeAddress): boolean;
223
+ reattachEndpoint(endpoint: EndpointRef, wakeAddress?: WakeAddress): boolean;
224
+ detachEndpoint(endpoint: EndpointRef): boolean;
225
+ removeEndpoint(endpoint: EndpointRef): boolean;
226
+ cancelThrough(endpoint: EndpointRef, through: number): boolean;
227
+ markSeen(owner: string, indexes: readonly number[], at: number): number;
228
+ claimDelivery(input: ChannelDeliveryClaimInput): ChannelDeliveryClaimResult;
229
+ prepareDelivery(input: ChannelDeliveryPreparation): boolean;
230
+ completeDelivery(input: ChannelDeliveryCompletion): boolean;
231
+ observeNativeDelivery(input: {
232
+ readonly seat: EndpointRef;
233
+ readonly claimToken: string;
234
+ readonly observation: NonNullable<ChannelDeliveryEvidence['nativeObservations']>[number];
235
+ }): boolean;
236
+ putDeliveryDeferral(seat: EndpointRef, id: string, match: MetadataMatch, expiresAt: number, now: number): boolean;
237
+ renewDeliveryDeferral(seat: EndpointRef, id: string, expiresAt: number, now: number): boolean;
238
+ releaseDeliveryDeferral(seat: EndpointRef, id: string, now: number): boolean;
239
+ putReceiveClaim(claim: ReceiveClaimFact): boolean;
240
+ removeReceiveClaim(owner: string, namespace: string, id: string): boolean;
241
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ export declare function escapeSquareTags(text: string): string;
2
+ /** A Channel message has no Agora route or activity record. */
3
+ export declare function projectChannelNotification({ actor, body, preview }: {
4
+ squarePath: string;
5
+ recipient: string;
6
+ actor: string;
7
+ body: string;
8
+ preview?: string;
9
+ }): {
10
+ text: string;
11
+ full: string;
12
+ preview: string;
13
+ description: string;
14
+ clipped: boolean;
15
+ };
16
+ /** The fixed model-facing outer frame, independent of transport and source. */
17
+ export declare function renderSquareReminder(previews: readonly string[]): string;
@@ -0,0 +1,22 @@
1
+ export function escapeSquareTags(text) {
2
+ return text.replace(/<(\/?)(square-|channel-|system-reminder)/gi, '<\\$1$2');
3
+ }
4
+ /** A Channel message has no Agora route or activity record. */
5
+ export function projectChannelNotification({ actor, body, preview }) {
6
+ const compact = body.replace(/\r\n/g, '\n');
7
+ const projected = preview ?? compact;
8
+ return {
9
+ // Channel's default notification is the full (or explicitly previewed) escaped body.
10
+ text: escapeSquareTags(projected),
11
+ // Human expansion is the stored full body, never a sender-owned preview.
12
+ full: compact,
13
+ preview: projected, description: `${actor} sent you a message`, clipped: projected !== compact,
14
+ };
15
+ }
16
+ /** The fixed model-facing outer frame, independent of transport and source. */
17
+ export function renderSquareReminder(previews) {
18
+ if (previews.length === 0)
19
+ return '';
20
+ return ['', '<system-reminder>', 'This is an external notification, not a direct message from the user.', '',
21
+ previews.join('\n\n'), '</system-reminder>', ''].join('\n');
22
+ }
@@ -0,0 +1,13 @@
1
+ import type { DatabaseSync } from 'node:sqlite';
2
+ import type { StoreFormat } from './artifact-store.js';
3
+ /** Channel owns this schema in both standalone and composed artifacts. */
4
+ export declare const CHANNEL_SCHEMA_SQL = "CREATE TABLE channel_meta (\n id INTEGER PRIMARY KEY CHECK (id = 1),\n next_msg_index INTEGER NOT NULL CHECK (next_msg_index BETWEEN 0 AND 9007199254740991),\n description TEXT NOT NULL\n);\nCREATE TABLE messages (\n msg_index INTEGER PRIMARY KEY CHECK (msg_index BETWEEN 0 AND 9007199254740991),\n at REAL NOT NULL, from_json TEXT NOT NULL, from_key_json TEXT NOT NULL,\n body_json TEXT NOT NULL CHECK (json_valid(body_json)),\n metadata_json TEXT CHECK (metadata_json IS NULL OR json_valid(metadata_json)), preview TEXT\n);\nCREATE INDEX message_sender ON messages(from_key_json,msg_index);\nCREATE TABLE message_addresses (\n msg_index INTEGER NOT NULL, recipient_key_json TEXT NOT NULL, recipient_json TEXT NOT NULL,\n recipient_order INTEGER NOT NULL CHECK (recipient_order >= 0), generation_json TEXT,\n PRIMARY KEY (msg_index,recipient_key_json), UNIQUE(msg_index,recipient_order)\n) WITHOUT ROWID;\nCREATE INDEX message_recipient ON message_addresses(recipient_key_json,msg_index);\nCREATE TABLE receipts (\n owner_key_json TEXT NOT NULL, msg_index INTEGER NOT NULL, at REAL NOT NULL,\n PRIMARY KEY (owner_key_json,msg_index)\n) WITHOUT ROWID;\nCREATE INDEX receipt_owner_time ON receipts(owner_key_json,at);\nCREATE TABLE endpoints (\n participant_key_json TEXT PRIMARY KEY NOT NULL, participant_json TEXT NOT NULL,\n owner_json TEXT NOT NULL, generation_json TEXT NOT NULL,\n wake_address_json TEXT CHECK (wake_address_json IS NULL OR json_valid(wake_address_json)),\n attachment TEXT NOT NULL CHECK (attachment IN ('attached','detached'))\n) WITHOUT ROWID;\nCREATE INDEX endpoint_owner ON endpoints(owner_json);\nCREATE TABLE delivery_evidence (\n participant_key_json TEXT NOT NULL, participant_json TEXT NOT NULL, generation_json TEXT NOT NULL,\n msg_index INTEGER NOT NULL, kind TEXT NOT NULL CHECK (kind IN ('wake','presentation')),\n attempt_n INTEGER NOT NULL CHECK (attempt_n >= 0), outcome TEXT NOT NULL,\n at REAL NOT NULL, expires_at REAL, claim_token_json TEXT,\n payload_json TEXT NOT NULL CHECK (json_valid(payload_json)),\n PRIMARY KEY (participant_key_json,generation_json,msg_index,kind,attempt_n)\n) WITHOUT ROWID;\nCREATE UNIQUE INDEX delivery_claim_token ON delivery_evidence(claim_token_json) WHERE claim_token_json IS NOT NULL;\nCREATE TABLE delivery_cancellations (\n participant_key_json TEXT NOT NULL, generation_json TEXT NOT NULL, through_index INTEGER NOT NULL,\n PRIMARY KEY (participant_key_json,generation_json)\n) WITHOUT ROWID;\nCREATE TABLE delivery_deferrals (\n participant_key_json TEXT NOT NULL, participant_json TEXT NOT NULL, generation_json TEXT NOT NULL,\n deferral_id_json TEXT NOT NULL, match_json TEXT NOT NULL CHECK (json_valid(match_json)), expires_at REAL NOT NULL,\n PRIMARY KEY (participant_key_json,generation_json,deferral_id_json)\n) WITHOUT ROWID;\nCREATE INDEX delivery_deferral_expiry ON delivery_deferrals(participant_key_json,generation_json,expires_at);\nCREATE TABLE receive_claims (\n namespace_key_json TEXT NOT NULL, owner_key_json TEXT NOT NULL, generation_json TEXT NOT NULL,\n claim_id_json TEXT NOT NULL, heartbeat_at REAL NOT NULL, expires_at REAL NOT NULL CHECK (expires_at >= heartbeat_at),\n selector_json TEXT NOT NULL CHECK (json_valid(selector_json)), suppressed_json TEXT NOT NULL CHECK (json_valid(suppressed_json)),\n PRIMARY KEY (namespace_key_json,owner_key_json)\n) WITHOUT ROWID;";
5
+ export declare function initializeChannelSqlite(db: DatabaseSync, description?: string): void;
6
+ export declare function resetChannelSqlite(db: DatabaseSync): void;
7
+ export declare const channelLowerSchema: {
8
+ schemaSql: string;
9
+ initialize: typeof initializeChannelSqlite;
10
+ reset: typeof resetChannelSqlite;
11
+ };
12
+ export declare const channelStoreFormat: StoreFormat;
13
+ export declare function initializeStandaloneChannel(db: DatabaseSync, revision?: number, description?: string): void;
@@ -0,0 +1,81 @@
1
+ import { initializeStorageMeta, resetStorageMeta, STORAGE_META_SQL, validateSchema } from './sqlite-framing.js';
2
+ import { requireFact } from './sqlite-values.js';
3
+ /** Channel owns this schema in both standalone and composed artifacts. */
4
+ export const CHANNEL_SCHEMA_SQL = `CREATE TABLE channel_meta (
5
+ id INTEGER PRIMARY KEY CHECK (id = 1),
6
+ next_msg_index INTEGER NOT NULL CHECK (next_msg_index BETWEEN 0 AND 9007199254740991),
7
+ description TEXT NOT NULL
8
+ );
9
+ CREATE TABLE messages (
10
+ msg_index INTEGER PRIMARY KEY CHECK (msg_index BETWEEN 0 AND 9007199254740991),
11
+ at REAL NOT NULL, from_json TEXT NOT NULL, from_key_json TEXT NOT NULL,
12
+ body_json TEXT NOT NULL CHECK (json_valid(body_json)),
13
+ metadata_json TEXT CHECK (metadata_json IS NULL OR json_valid(metadata_json)), preview TEXT
14
+ );
15
+ CREATE INDEX message_sender ON messages(from_key_json,msg_index);
16
+ CREATE TABLE message_addresses (
17
+ msg_index INTEGER NOT NULL, recipient_key_json TEXT NOT NULL, recipient_json TEXT NOT NULL,
18
+ recipient_order INTEGER NOT NULL CHECK (recipient_order >= 0), generation_json TEXT,
19
+ PRIMARY KEY (msg_index,recipient_key_json), UNIQUE(msg_index,recipient_order)
20
+ ) WITHOUT ROWID;
21
+ CREATE INDEX message_recipient ON message_addresses(recipient_key_json,msg_index);
22
+ CREATE TABLE receipts (
23
+ owner_key_json TEXT NOT NULL, msg_index INTEGER NOT NULL, at REAL NOT NULL,
24
+ PRIMARY KEY (owner_key_json,msg_index)
25
+ ) WITHOUT ROWID;
26
+ CREATE INDEX receipt_owner_time ON receipts(owner_key_json,at);
27
+ CREATE TABLE endpoints (
28
+ participant_key_json TEXT PRIMARY KEY NOT NULL, participant_json TEXT NOT NULL,
29
+ owner_json TEXT NOT NULL, generation_json TEXT NOT NULL,
30
+ wake_address_json TEXT CHECK (wake_address_json IS NULL OR json_valid(wake_address_json)),
31
+ attachment TEXT NOT NULL CHECK (attachment IN ('attached','detached'))
32
+ ) WITHOUT ROWID;
33
+ CREATE INDEX endpoint_owner ON endpoints(owner_json);
34
+ CREATE TABLE delivery_evidence (
35
+ participant_key_json TEXT NOT NULL, participant_json TEXT NOT NULL, generation_json TEXT NOT NULL,
36
+ msg_index INTEGER NOT NULL, kind TEXT NOT NULL CHECK (kind IN ('wake','presentation')),
37
+ attempt_n INTEGER NOT NULL CHECK (attempt_n >= 0), outcome TEXT NOT NULL,
38
+ at REAL NOT NULL, expires_at REAL, claim_token_json TEXT,
39
+ payload_json TEXT NOT NULL CHECK (json_valid(payload_json)),
40
+ PRIMARY KEY (participant_key_json,generation_json,msg_index,kind,attempt_n)
41
+ ) WITHOUT ROWID;
42
+ CREATE UNIQUE INDEX delivery_claim_token ON delivery_evidence(claim_token_json) WHERE claim_token_json IS NOT NULL;
43
+ CREATE TABLE delivery_cancellations (
44
+ participant_key_json TEXT NOT NULL, generation_json TEXT NOT NULL, through_index INTEGER NOT NULL,
45
+ PRIMARY KEY (participant_key_json,generation_json)
46
+ ) WITHOUT ROWID;
47
+ CREATE TABLE delivery_deferrals (
48
+ participant_key_json TEXT NOT NULL, participant_json TEXT NOT NULL, generation_json TEXT NOT NULL,
49
+ deferral_id_json TEXT NOT NULL, match_json TEXT NOT NULL CHECK (json_valid(match_json)), expires_at REAL NOT NULL,
50
+ PRIMARY KEY (participant_key_json,generation_json,deferral_id_json)
51
+ ) WITHOUT ROWID;
52
+ CREATE INDEX delivery_deferral_expiry ON delivery_deferrals(participant_key_json,generation_json,expires_at);
53
+ CREATE TABLE receive_claims (
54
+ namespace_key_json TEXT NOT NULL, owner_key_json TEXT NOT NULL, generation_json TEXT NOT NULL,
55
+ claim_id_json TEXT NOT NULL, heartbeat_at REAL NOT NULL, expires_at REAL NOT NULL CHECK (expires_at >= heartbeat_at),
56
+ selector_json TEXT NOT NULL CHECK (json_valid(selector_json)), suppressed_json TEXT NOT NULL CHECK (json_valid(suppressed_json)),
57
+ PRIMARY KEY (namespace_key_json,owner_key_json)
58
+ ) WITHOUT ROWID;`;
59
+ export function initializeChannelSqlite(db, description = '# Channel\n') {
60
+ db.exec(CHANNEL_SCHEMA_SQL);
61
+ db.prepare('INSERT INTO channel_meta(id,next_msg_index,description) VALUES(1,0,?)').run(description);
62
+ }
63
+ export function resetChannelSqlite(db) {
64
+ for (const table of ['receive_claims', 'delivery_deferrals', 'delivery_cancellations', 'delivery_evidence', 'endpoints', 'receipts', 'message_addresses', 'messages', 'channel_meta'])
65
+ db.exec(`DROP TABLE ${table}`);
66
+ }
67
+ export const channelLowerSchema = { schemaSql: CHANNEL_SCHEMA_SQL, initialize: initializeChannelSqlite, reset: resetChannelSqlite };
68
+ export const channelStoreFormat = {
69
+ applicationId: 0x43484e4c, userVersion: 1, observerKey: 'channel',
70
+ validate(db, location) {
71
+ const identity = db.prepare('SELECT application_id, user_version FROM pragma_application_id, pragma_user_version').get();
72
+ requireFact(identity.application_id === this.applicationId, 'unrelated SQLite database.');
73
+ requireFact(identity.user_version === this.userVersion, 'unsupported Channel SQLite format version.');
74
+ validateSchema(db, `${STORAGE_META_SQL}\n${CHANNEL_SCHEMA_SQL}`, location);
75
+ },
76
+ reset(db) { resetChannelSqlite(db); resetStorageMeta(db); },
77
+ };
78
+ export function initializeStandaloneChannel(db, revision = 0, description = '# Channel\n') {
79
+ initializeStorageMeta(db, revision);
80
+ initializeChannelSqlite(db, description);
81
+ }
@@ -0,0 +1,62 @@
1
+ import type { WakeRoute } from './channel-values.js';
2
+ import type { MessageId } from './message.js';
3
+ /** One wake executor; final send must use the supplied custody gate. */
4
+ export interface WakeTransportPort {
5
+ probe?(route: WakeRoute): Promise<boolean | {
6
+ readonly outcome: 'not-capable';
7
+ readonly diagnostic?: string;
8
+ }>;
9
+ attempt(request: WakeRequest, timeoutMs: number, beforeSend: () => Promise<boolean>): Promise<WakeOutcome>;
10
+ }
11
+ export interface WakePresentation {
12
+ readonly payload: string;
13
+ readonly coverage: 'full' | 'clipped';
14
+ readonly description?: string;
15
+ }
16
+ export interface WakeProjectionContext {
17
+ readonly location: string;
18
+ readonly participant: string;
19
+ readonly message: MessageId;
20
+ readonly actor: string;
21
+ readonly seat: {
22
+ readonly participant: string;
23
+ readonly owner: string;
24
+ readonly generation: string;
25
+ };
26
+ readonly route: WakeRoute;
27
+ }
28
+ export type WakeProjection = (context: WakeProjectionContext) => Promise<WakePresentation | undefined>;
29
+ export interface WakeRequest extends WakeProjectionContext {
30
+ readonly claimToken?: string;
31
+ readonly attemptN?: number;
32
+ readonly channel: {
33
+ prepare(input: {
34
+ readonly correlation: {
35
+ readonly harness: 'claude' | 'opencode' | 'pi';
36
+ readonly token?: string;
37
+ readonly messageId?: string;
38
+ readonly payloadHash?: string;
39
+ };
40
+ readonly coverage: 'full' | 'clipped';
41
+ readonly description?: string;
42
+ }, signal?: AbortSignal): Promise<boolean>;
43
+ };
44
+ readonly presentation?: WakePresentation;
45
+ }
46
+ export type WakeOutcome = {
47
+ readonly outcome: 'accepted';
48
+ readonly signature?: string;
49
+ } | {
50
+ readonly outcome: 'failed';
51
+ readonly message?: string;
52
+ readonly signature?: string;
53
+ readonly diagnostic?: unknown;
54
+ } | {
55
+ readonly outcome: 'not-capable';
56
+ readonly diagnostic?: string;
57
+ } | {
58
+ readonly outcome: 'unknown';
59
+ readonly signature?: string;
60
+ readonly message?: string;
61
+ readonly diagnostic?: unknown;
62
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,27 @@
1
+ export declare const WAKE_ROUTE_KINDS: readonly ["opencode-server", "codex-queue", "claude-native", "paseo", "http"];
2
+ export type WakeRouteKind = typeof WAKE_ROUTE_KINDS[number];
3
+ export type JsonValue = null | boolean | number | string | readonly JsonValue[] | {
4
+ readonly [key: string]: JsonValue;
5
+ };
6
+ export type MessageMetadata = Readonly<Record<string, JsonValue>>;
7
+ export declare const MESSAGE_METADATA_MAX_BYTES: number;
8
+ export declare const MESSAGE_METADATA_MAX_DEPTH = 256;
9
+ /** Validate the bounded, lossless JSON object carried by a message. */
10
+ export declare function validateMessageMetadata(value: unknown): MessageMetadata | undefined;
11
+ export declare function messageMetadataHasContent(metadata: MessageMetadata | undefined): boolean;
12
+ export interface WakeAddress {
13
+ readonly kind: WakeRouteKind;
14
+ readonly address: Readonly<Record<string, string>>;
15
+ }
16
+ export declare function isWakeRouteKind(value: unknown): value is WakeRouteKind;
17
+ export interface WakeRoute {
18
+ epoch?: number;
19
+ location: string;
20
+ participant: string;
21
+ kind: WakeRouteKind;
22
+ address: Record<string, string>;
23
+ updatedAt: number;
24
+ }
25
+ export declare function sameName(a: string, b: string): boolean;
26
+ export declare const NAME_GRAPHEME_SOURCE: string;
27
+ export declare function validateName(name: string): void;
@@ -0,0 +1,90 @@
1
+ import { SquareError, nameKey } from './neutral.js';
2
+ export const WAKE_ROUTE_KINDS = ['opencode-server', 'codex-queue', 'claude-native', 'paseo', 'http'];
3
+ export const MESSAGE_METADATA_MAX_BYTES = 16 * 1024;
4
+ export const MESSAGE_METADATA_MAX_DEPTH = 256;
5
+ /** Validate the bounded, lossless JSON object carried by a message. */
6
+ export function validateMessageMetadata(value) {
7
+ if (value === undefined)
8
+ return undefined;
9
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
10
+ throw new SquareError('invalid_args', 'Activity metadata must be a non-null JSON object.');
11
+ const visiting = new Set();
12
+ const stack = [{ value, depth: 0 }];
13
+ while (stack.length > 0) {
14
+ const frame = stack.pop();
15
+ if (frame.exit !== undefined) {
16
+ visiting.delete(frame.exit);
17
+ continue;
18
+ }
19
+ const item = frame.value;
20
+ if (item === null || typeof item === 'boolean' || typeof item === 'string')
21
+ continue;
22
+ if (typeof item === 'number') {
23
+ if (!Number.isFinite(item))
24
+ throw new SquareError('invalid_args', 'Activity metadata must contain only finite JSON values.');
25
+ continue;
26
+ }
27
+ if (typeof item !== 'object')
28
+ throw new SquareError('invalid_args', 'Activity metadata must contain only JSON values.');
29
+ if (frame.depth > MESSAGE_METADATA_MAX_DEPTH)
30
+ throw new SquareError('invalid_args', `Activity metadata nesting cannot exceed ${MESSAGE_METADATA_MAX_DEPTH} levels.`);
31
+ if (visiting.has(item))
32
+ throw new SquareError('invalid_args', 'Activity metadata cannot contain a cycle.');
33
+ visiting.add(item);
34
+ stack.push({ value: item, depth: frame.depth, exit: item });
35
+ if (Array.isArray(item)) {
36
+ if (Object.getOwnPropertySymbols(item).length > 0 || Object.keys(item).length !== item.length)
37
+ throw new SquareError('invalid_args', 'Activity metadata arrays must be dense JSON arrays.');
38
+ const names = Object.getOwnPropertyNames(item);
39
+ if (names.some(name => name !== 'length' && (!/^(0|[1-9]\d*)$/.test(name) || Number(name) >= item.length)))
40
+ throw new SquareError('invalid_args', 'Activity metadata arrays must contain only indexed values.');
41
+ for (const key of Object.keys(item).reverse()) {
42
+ const descriptor = Object.getOwnPropertyDescriptor(item, key);
43
+ if (descriptor === undefined || !('value' in descriptor))
44
+ throw new SquareError('invalid_args', 'Activity metadata cannot contain accessors.');
45
+ stack.push({ value: descriptor.value, depth: frame.depth + 1 });
46
+ }
47
+ continue;
48
+ }
49
+ const prototype = Object.getPrototypeOf(item);
50
+ if (prototype !== Object.prototype && prototype !== null)
51
+ throw new SquareError('invalid_args', 'Activity metadata must contain only plain JSON objects.');
52
+ if (Object.getOwnPropertySymbols(item).length > 0)
53
+ throw new SquareError('invalid_args', 'Activity metadata cannot contain symbol keys.');
54
+ const names = Object.getOwnPropertyNames(item);
55
+ if (names.some(name => !Object.prototype.propertyIsEnumerable.call(item, name)))
56
+ throw new SquareError('invalid_args', 'Activity metadata cannot contain non-enumerable properties.');
57
+ for (const name of names.reverse()) {
58
+ const descriptor = Object.getOwnPropertyDescriptor(item, name);
59
+ if (!('value' in descriptor))
60
+ throw new SquareError('invalid_args', 'Activity metadata cannot contain accessors.');
61
+ stack.push({ value: descriptor.value, depth: frame.depth + 1 });
62
+ }
63
+ }
64
+ let serialized;
65
+ try {
66
+ serialized = JSON.stringify(value);
67
+ }
68
+ catch {
69
+ throw new SquareError('invalid_args', 'Activity metadata must contain only lossless JSON values.');
70
+ }
71
+ if (serialized === undefined || Buffer.byteLength(serialized, 'utf8') > MESSAGE_METADATA_MAX_BYTES)
72
+ throw new SquareError('invalid_args', `Activity metadata cannot exceed ${MESSAGE_METADATA_MAX_BYTES} UTF-8 bytes.`);
73
+ return value;
74
+ }
75
+ export function messageMetadataHasContent(metadata) {
76
+ return metadata !== undefined && Object.keys(metadata).length > 0;
77
+ }
78
+ export function isWakeRouteKind(value) {
79
+ return typeof value === 'string' && WAKE_ROUTE_KINDS.includes(value);
80
+ }
81
+ export function sameName(a, b) { return nameKey(a) === nameKey(b); }
82
+ const TEXT_NAME_GRAPHEME_SOURCE = String.raw `[[\p{L}\p{N}\p{M}_\x2D]--[\p{Variation_Selector}]]`;
83
+ export const NAME_GRAPHEME_SOURCE = String.raw `${TEXT_NAME_GRAPHEME_SOURCE}|\p{RGI_Emoji}`;
84
+ const TEXT_NAME_PATTERN = new RegExp(`^(?:${TEXT_NAME_GRAPHEME_SOURCE})+(?:/(?:${TEXT_NAME_GRAPHEME_SOURCE})+)*$`, 'v');
85
+ const NAME_PATTERN = new RegExp(`^(?:${NAME_GRAPHEME_SOURCE})+(?:/(?:${NAME_GRAPHEME_SOURCE})+)*$`, 'v');
86
+ export function validateName(name) {
87
+ if (!name || (!TEXT_NAME_PATTERN.test(name) && !NAME_PATTERN.test(name))) {
88
+ throw new SquareError('invalid_name', 'Invalid name: names must contain non-empty slash-separated segments using Unicode letters, digits, marks, hyphens, underscores, or complete RGI emoji graphemes.');
89
+ }
90
+ }