@astrosheep/square 0.3.105 → 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 (143) 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/paseo-timeline.js +1 -0
  86. package/dist/plugins/opencode/delivery.js +27 -42
  87. package/dist/ports.d.ts +46 -168
  88. package/dist/presentation-operations.d.ts +1 -14
  89. package/dist/presentation-operations.js +16 -16
  90. package/dist/presentation.d.ts +5 -5
  91. package/dist/presentation.js +1 -0
  92. package/dist/runtime.js +2 -2
  93. package/dist/search.js +1 -1
  94. package/dist/sqlite-channel.d.ts +15 -0
  95. package/dist/sqlite-channel.js +600 -0
  96. package/dist/sqlite-framing.d.ts +8 -0
  97. package/dist/sqlite-framing.js +31 -0
  98. package/dist/sqlite-square.d.ts +12 -13
  99. package/dist/sqlite-square.js +192 -248
  100. package/dist/sqlite-values.d.ts +21 -0
  101. package/dist/sqlite-values.js +78 -0
  102. package/dist/square-api.d.ts +1 -1
  103. package/dist/square-api.js +1 -1
  104. package/dist/square-core.d.ts +1 -1
  105. package/dist/square-domain.d.ts +21 -7
  106. package/dist/square-domain.js +93 -94
  107. package/dist/square-facade.d.ts +12 -66
  108. package/dist/square-index.d.ts +1 -1
  109. package/dist/square-index.js +1 -1
  110. package/dist/square-location.d.ts +6 -1
  111. package/dist/square-location.js +1 -1
  112. package/dist/square-receive.d.ts +2 -3
  113. package/dist/square-sqlite.d.ts +45 -44
  114. package/dist/square-sqlite.js +308 -764
  115. package/dist/square-tools.d.ts +8 -0
  116. package/dist/square-tools.js +39 -0
  117. package/dist/views.d.ts +6 -6
  118. package/dist/views.js +15 -9
  119. package/dist/wake-preview.d.ts +7 -0
  120. package/dist/wake-preview.js +15 -0
  121. package/dist/wake-sink.d.ts +2 -2
  122. package/dist/wake-sink.js +1 -1
  123. package/dist/wake-transports.d.ts +7 -0
  124. package/dist/wake-transports.js +42 -0
  125. package/guides/claude-native.md +13 -10
  126. package/guides/codex-delivery.md +3 -1
  127. package/guides/participant-ownership.md +1 -1
  128. package/guides/row-runtime-delivery-evidence.md +2 -0
  129. package/guides/row-storage-design.md +19 -44
  130. package/guides/square-api.md +137 -60
  131. package/package.json +3 -3
  132. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  133. package/plugins/claude/types.d.ts +1 -1
  134. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  135. package/plugins/opencode/delivery.ts +28 -41
  136. package/plugins/pi/square-pi.js +28 -51
  137. package/dist/square-legacy-framing.d.ts +0 -11
  138. package/dist/square-legacy-framing.js +0 -64
  139. package/dist/square-migration-host-ledger.d.ts +0 -26
  140. package/dist/square-migration-host-ledger.js +0 -169
  141. package/dist/square-migration.d.ts +0 -30
  142. package/dist/square-migration.js +0 -599
  143. package/guides/migration.md +0 -156
@@ -0,0 +1,23 @@
1
+ // Message coordinates belong to Channel alone; Agora activity ids are a separate coordinate.
2
+ import crypto from 'node:crypto';
3
+ export function formatMessageId(index) {
4
+ if (!Number.isSafeInteger(index) || index < 0)
5
+ throw new Error(`Invalid message index: ${index}`);
6
+ return `msg/${index}`;
7
+ }
8
+ export function parseMessageId(value) {
9
+ if (value === 'msg/0')
10
+ return 0;
11
+ if (typeof value !== 'string' || !/^msg\/[1-9]\d*$/.test(value))
12
+ return undefined;
13
+ const index = Number(value.slice(4));
14
+ return Number.isSafeInteger(index) ? index : undefined;
15
+ }
16
+ export function newEndpointGeneration() {
17
+ return `endpoint/${crypto.randomUUID()}`;
18
+ }
19
+ export function validateEndpointGeneration(value) {
20
+ if (typeof value !== 'string' || value.length === 0 || value.length > 512 || /[\u0000-\u001f\u007f-\u009f]/u.test(value))
21
+ throw new TypeError('Endpoint generation must be a non-empty opaque token without control characters.');
22
+ return value;
23
+ }
package/dist/model.d.ts CHANGED
@@ -1,33 +1,17 @@
1
1
  import type { Act, ActivityId } from './square-core.js';
2
- export { formatActivityId, parseActivityId } from './square-core.js';
2
+ import type { EndpointGeneration } from './message.js';
3
+ import type { Owner } from './neutral.js';
4
+ import type { MessageMetadata as ActivityMetadata, WakeAddress } from './channel-values.js';
5
+ export { WAKE_ROUTE_KINDS, MESSAGE_METADATA_MAX_BYTES as ACTIVITY_METADATA_MAX_BYTES, MESSAGE_METADATA_MAX_DEPTH as ACTIVITY_METADATA_MAX_DEPTH, validateMessageMetadata as validateActivityMetadata, messageMetadataHasContent as activityMetadataHasContent, isWakeRouteKind, sameName, NAME_GRAPHEME_SOURCE, validateName } from './channel-values.js';
6
+ export type { WakeRouteKind, JsonValue, MessageMetadata as ActivityMetadata, WakeAddress, WakeRoute } from './channel-values.js';
7
+ export { SquareErrorBase, SquareError, InternalSquareError, isSquareError, validateOwner, nameKey } from './neutral.js';
8
+ export type { Owner, SquareErrorCode, InternalSquareErrorCode, SquareErrorFacts } from './neutral.js';
3
9
  export type { Act, ActivityId, Audience, Reach } from './square-core.js';
4
- export declare const WAKE_ROUTE_KINDS: readonly ["opencode-server", "codex-queue", "claude-native", "paseo", "http"];
5
- export type WakeRouteKind = typeof WAKE_ROUTE_KINDS[number];
6
- export type JsonValue = null | boolean | number | string | readonly JsonValue[] | {
7
- readonly [key: string]: JsonValue;
8
- };
9
- export type ActivityMetadata = Readonly<Record<string, JsonValue>>;
10
- export interface ActivityDelivery {
11
- readonly mode: 'direct';
12
- readonly preview?: string;
13
- }
14
- export declare function validateActivityDelivery(value: unknown): ActivityDelivery | undefined;
15
- export declare const ACTIVITY_METADATA_MAX_BYTES: number;
16
- export declare const ACTIVITY_METADATA_MAX_DEPTH = 256;
17
- /** Validate the bounded, lossless JSON object carried by a say. */
18
- export declare function validateActivityMetadata(value: unknown): ActivityMetadata | undefined;
19
- export declare function activityMetadataHasContent(metadata: ActivityMetadata | undefined): boolean;
20
- /** Opaque custody identity. Harness coordinates are adapter-owned. */
21
- export type Owner = string;
22
- export interface WakeAddress {
23
- readonly kind: WakeRouteKind;
24
- readonly address: Readonly<Record<string, string>>;
25
- }
26
10
  /** Artifact-owned custody binding. Generation is the join activity that established it. */
27
11
  export interface Seat {
28
12
  readonly participant: string;
29
13
  readonly owner: Owner;
30
- readonly generation: ActivityId;
14
+ readonly generation: EndpointGeneration;
31
15
  readonly attachment: 'attached' | 'detached';
32
16
  readonly wakeAddress?: WakeAddress;
33
17
  }
@@ -35,48 +19,6 @@ export interface SeatWatchLease {
35
19
  readonly generation: ActivityId;
36
20
  readonly lease: WatchLease;
37
21
  }
38
- export declare function validateOwner(owner: Owner): Owner;
39
- export declare function isWakeRouteKind(value: unknown): value is WakeRouteKind;
40
- export interface WakeRoute {
41
- /** Ownership generation, when the receiver publishes a fenced route. */
42
- epoch?: number;
43
- location: string;
44
- participant: string;
45
- kind: WakeRouteKind;
46
- address: Record<string, string>;
47
- updatedAt: number;
48
- }
49
- export type SquareErrorCode = 'invalid_name' | 'invalid_args' | 'unknown_participant' | 'not_joined' | 'already_joined' | 'already_done' | 'capped' | 'throttled' | 'bell_quota' | 'behind' | 'io' | 'unavailable';
50
- export type InternalSquareErrorCode = SquareErrorCode | 'not_found' | 'cap_reached' | 'conflict' | 'pending_peer';
51
- export interface SquareErrorFacts {
52
- /** Copyable recovery for a missing, detached or foreign seat. */
53
- join?: string;
54
- /** Express admission rejected before any activity was committed. */
55
- activityUnsent?: true;
56
- /** Refusal reason, including absent participants, conflicting reach, and inaccessible activities. */
57
- reason?: 'never_joined' | 'not_standing' | 'bell_mention_conflict' | 'not_addressed' | 'migration_required' | 'invalid_reply' | 'invalid_done_body' | 'needs_seat';
58
- /** The activity id a `catch --id` refusal named. */
59
- activityId?: string;
60
- /** The host ledger requiring offline migration, rather than the selected artifact. */
61
- ledgerRoot?: string;
62
- /** Requested recipient spellings, without parsing presentation copy. */
63
- targets?: readonly string[];
64
- /** Whether the artifact holds that activity at all; an unknown id needs an ordinary page. */
65
- activityExists?: boolean;
66
- pending?: number;
67
- retryAfterMs?: number;
68
- }
69
- declare class SquareErrorBase<Code extends string> extends Error {
70
- code: Code;
71
- facts?: SquareErrorFacts | undefined;
72
- operation?: import('./square-facade.js').OperationFailureDetail;
73
- constructor(code: Code, message: string, facts?: SquareErrorFacts | undefined);
74
- }
75
- export declare class SquareError extends SquareErrorBase<SquareErrorCode> {
76
- }
77
- export declare class InternalSquareError extends SquareErrorBase<InternalSquareErrorCode> {
78
- }
79
- export declare function isSquareError(error: unknown): error is SquareError | InternalSquareError;
80
22
  export type HardCap = number | null;
81
23
  export interface BuildOptions {
82
24
  force: boolean;
@@ -84,9 +26,29 @@ export interface BuildOptions {
84
26
  template?: string;
85
27
  throttlePerMinute?: number;
86
28
  }
87
- export type StoredAct = Act & {
29
+ /** An upper publication draft: content and social facts before any index exists. */
30
+ export type ActDraft = Act & {
31
+ at: number;
32
+ };
33
+ /** Persisted say: identity and social facts only. Its content lives in the linked Channel message. */
34
+ export type SayRelation = Omit<Extract<Act, {
35
+ kind: 'say';
36
+ }>, 'body' | 'metadata'> & {
88
37
  index: number;
89
38
  at: number;
39
+ messageIndex: number;
40
+ };
41
+ /** A landed upper activity. A say is a relation; it carries no content of its own. */
42
+ export type StoredAct = Extract<Act, {
43
+ kind: 'join' | 'done' | 'read' | 'listen' | 'ignore' | 'unlisten';
44
+ }> & {
45
+ index: number;
46
+ at: number;
47
+ } | SayRelation;
48
+ /** Explicit upper projection: a stored say resolved against its authoritative message. */
49
+ export type HydratedAct = Exclude<StoredAct, SayRelation> | SayRelation & {
50
+ body?: string;
51
+ metadata?: ActivityMetadata;
90
52
  };
91
53
  export type ObservationState = 'seen';
92
54
  export interface ActivityObservation {
@@ -105,13 +67,20 @@ export interface WatchLease {
105
67
  }
106
68
  export type DirectedNotificationRoute = import('./attention.js').AudienceRoute;
107
69
  export interface InboxNotification {
108
- actIndex: number;
70
+ /** Lower message coordinate. */
71
+ messageIndex: number;
72
+ /** Upper Agora activity sharing this message, when one exists. */
73
+ actIndex?: number;
109
74
  actor: string;
110
75
  at: number;
111
- route: DirectedNotificationRoute;
76
+ route?: DirectedNotificationRoute;
112
77
  body: string;
113
- readonly delivery?: ActivityDelivery;
78
+ readonly preview?: string;
79
+ readonly description?: string;
80
+ readonly coverage?: 'full' | 'clipped';
114
81
  }
82
+ /** The one stored-content-to-notification field mapping used by inbox and native previews. */
83
+ export declare function notificationFacts(item: HydratedAct, route: DirectedNotificationRoute): InboxNotification;
115
84
  export interface LocatedSeat {
116
85
  readonly location: string;
117
86
  readonly seat: Seat;
@@ -127,16 +96,11 @@ export interface WatchOptions {
127
96
  mention?: string;
128
97
  replace?: boolean;
129
98
  }
130
- export type PublicAct = Extract<StoredAct, {
99
+ export type PublicAct = Extract<HydratedAct, {
131
100
  kind: 'say' | 'done';
132
101
  }>;
133
- export type RoomChangeAct = Extract<StoredAct, {
102
+ export type RoomChangeAct = Extract<HydratedAct, {
134
103
  kind: 'join' | 'done' | 'listen' | 'ignore' | 'unlisten';
135
104
  }>;
136
105
  export declare function formatHardCap(hardCap: HardCap): string;
137
106
  export declare function parseParticipantList(value: string): string[];
138
- export declare function nameKey(name: string): string;
139
- export declare function sameName(a: string, b: string): boolean;
140
- /** One name grapheme: Unicode letters, digits, marks, hyphens, or underscores (no isolated variation selectors), or a complete RGI emoji. Shared by name validation and body @mention scanning. */
141
- export declare const NAME_GRAPHEME_SOURCE: string;
142
- export declare function validateName(name: string): void;
package/dist/model.js CHANGED
@@ -1,126 +1,11 @@
1
1
  // Shared model and constants for Square.
2
- export { formatActivityId, parseActivityId } from './square-core.js';
3
- export const WAKE_ROUTE_KINDS = ['opencode-server', 'codex-queue', 'claude-native', 'paseo', 'http'];
4
- export function validateActivityDelivery(value) {
5
- if (value === undefined)
6
- return undefined;
7
- if (value !== null && typeof value === 'object'
8
- && [Object.prototype, null].includes(Object.getPrototypeOf(value))
9
- && Object.getOwnPropertySymbols(value).length === 0) {
10
- const keys = Object.getOwnPropertyNames(value);
11
- const mode = Object.getOwnPropertyDescriptor(value, 'mode');
12
- const preview = Object.getOwnPropertyDescriptor(value, 'preview');
13
- if (mode?.enumerable && 'value' in mode && mode.value === 'direct') {
14
- if (keys.length === 1)
15
- return { mode: 'direct' };
16
- if (keys.length === 2 && preview?.enumerable && 'value' in preview
17
- && typeof preview.value === 'string' && preview.value.trim() !== '')
18
- return { mode: 'direct', preview: preview.value };
19
- }
20
- }
21
- throw new SquareError('invalid_args', "Activity delivery must be { mode: 'direct', preview?: string }.");
22
- }
23
- export const ACTIVITY_METADATA_MAX_BYTES = 16 * 1024;
24
- export const ACTIVITY_METADATA_MAX_DEPTH = 256;
25
- /** Validate the bounded, lossless JSON object carried by a say. */
26
- export function validateActivityMetadata(value) {
27
- if (value === undefined)
28
- return undefined;
29
- if (value === null || typeof value !== 'object' || Array.isArray(value))
30
- throw new SquareError('invalid_args', 'Activity metadata must be a non-null JSON object.');
31
- const visiting = new Set();
32
- const stack = [{ value, depth: 0 }];
33
- while (stack.length > 0) {
34
- const frame = stack.pop();
35
- if (frame.exit !== undefined) {
36
- visiting.delete(frame.exit);
37
- continue;
38
- }
39
- const item = frame.value;
40
- if (item === null || typeof item === 'boolean' || typeof item === 'string')
41
- continue;
42
- if (typeof item === 'number') {
43
- if (!Number.isFinite(item))
44
- throw new SquareError('invalid_args', 'Activity metadata must contain only finite JSON values.');
45
- continue;
46
- }
47
- if (typeof item !== 'object')
48
- throw new SquareError('invalid_args', 'Activity metadata must contain only JSON values.');
49
- if (frame.depth > ACTIVITY_METADATA_MAX_DEPTH)
50
- throw new SquareError('invalid_args', `Activity metadata nesting cannot exceed ${ACTIVITY_METADATA_MAX_DEPTH} levels.`);
51
- if (visiting.has(item))
52
- throw new SquareError('invalid_args', 'Activity metadata cannot contain a cycle.');
53
- visiting.add(item);
54
- stack.push({ value: item, depth: frame.depth, exit: item });
55
- if (Array.isArray(item)) {
56
- if (Object.getOwnPropertySymbols(item).length > 0 || Object.keys(item).length !== item.length)
57
- throw new SquareError('invalid_args', 'Activity metadata arrays must be dense JSON arrays.');
58
- const names = Object.getOwnPropertyNames(item);
59
- if (names.some(name => name !== 'length' && (!/^(0|[1-9]\d*)$/.test(name) || Number(name) >= item.length)))
60
- throw new SquareError('invalid_args', 'Activity metadata arrays must contain only indexed values.');
61
- for (const key of Object.keys(item).reverse()) {
62
- const descriptor = Object.getOwnPropertyDescriptor(item, key);
63
- if (descriptor === undefined || !('value' in descriptor))
64
- throw new SquareError('invalid_args', 'Activity metadata cannot contain accessors.');
65
- stack.push({ value: descriptor.value, depth: frame.depth + 1 });
66
- }
67
- continue;
68
- }
69
- const prototype = Object.getPrototypeOf(item);
70
- if (prototype !== Object.prototype && prototype !== null)
71
- throw new SquareError('invalid_args', 'Activity metadata must contain only plain JSON objects.');
72
- if (Object.getOwnPropertySymbols(item).length > 0)
73
- throw new SquareError('invalid_args', 'Activity metadata cannot contain symbol keys.');
74
- const names = Object.getOwnPropertyNames(item);
75
- if (names.some(name => !Object.prototype.propertyIsEnumerable.call(item, name)))
76
- throw new SquareError('invalid_args', 'Activity metadata cannot contain non-enumerable properties.');
77
- for (const name of names.reverse()) {
78
- const descriptor = Object.getOwnPropertyDescriptor(item, name);
79
- if (!('value' in descriptor))
80
- throw new SquareError('invalid_args', 'Activity metadata cannot contain accessors.');
81
- stack.push({ value: descriptor.value, depth: frame.depth + 1 });
82
- }
83
- }
84
- let serialized;
85
- try {
86
- serialized = JSON.stringify(value);
87
- }
88
- catch {
89
- throw new SquareError('invalid_args', 'Activity metadata must contain only lossless JSON values.');
90
- }
91
- if (serialized === undefined || Buffer.byteLength(serialized, 'utf8') > ACTIVITY_METADATA_MAX_BYTES)
92
- throw new SquareError('invalid_args', `Activity metadata cannot exceed ${ACTIVITY_METADATA_MAX_BYTES} UTF-8 bytes.`);
93
- return value;
94
- }
95
- export function activityMetadataHasContent(metadata) {
96
- return metadata !== undefined && Object.keys(metadata).length > 0;
97
- }
98
- export function validateOwner(owner) {
99
- if (typeof owner !== 'string' || !owner || Buffer.byteLength(owner, 'utf8') > 512 || /[\u0000-\u001f\u007f-\u009f]/u.test(owner)) {
100
- throw new SquareError('invalid_args', 'A participant write requires a non-empty owner without control characters (at most 512 UTF-8 bytes).');
101
- }
102
- return owner;
103
- }
104
- export function isWakeRouteKind(value) {
105
- return typeof value === 'string' && WAKE_ROUTE_KINDS.includes(value);
106
- }
107
- class SquareErrorBase extends Error {
108
- code;
109
- facts;
110
- operation;
111
- constructor(code, message, facts) {
112
- super(message);
113
- this.code = code;
114
- this.facts = facts;
115
- this.name = 'SquareError';
116
- }
117
- }
118
- export class SquareError extends SquareErrorBase {
119
- }
120
- export class InternalSquareError extends SquareErrorBase {
121
- }
122
- export function isSquareError(error) {
123
- return error instanceof SquareError || error instanceof InternalSquareError;
2
+ export { WAKE_ROUTE_KINDS, MESSAGE_METADATA_MAX_BYTES as ACTIVITY_METADATA_MAX_BYTES, MESSAGE_METADATA_MAX_DEPTH as ACTIVITY_METADATA_MAX_DEPTH, validateMessageMetadata as validateActivityMetadata, messageMetadataHasContent as activityMetadataHasContent, isWakeRouteKind, sameName, NAME_GRAPHEME_SOURCE, validateName } from './channel-values.js';
3
+ export { SquareErrorBase, SquareError, InternalSquareError, isSquareError, validateOwner, nameKey } from './neutral.js';
4
+ /** The one stored-content-to-notification field mapping used by inbox and native previews. */
5
+ export function notificationFacts(item, route) {
6
+ if (item.kind !== 'say' || item.messageIndex === undefined)
7
+ throw new Error(`Activity ${item.index} is not conversation content.`);
8
+ return { messageIndex: item.messageIndex, actIndex: item.index, actor: item.actor, at: item.at, route, body: item.body ?? '(structured activity)' };
124
9
  }
125
10
  export function formatHardCap(hardCap) {
126
11
  return hardCap === null ? '-1' : String(hardCap);
@@ -131,20 +16,3 @@ export function parseParticipantList(value) {
131
16
  .map((name) => name.trim())
132
17
  .filter(Boolean);
133
18
  }
134
- export function nameKey(name) {
135
- return name.toLocaleLowerCase();
136
- }
137
- export function sameName(a, b) {
138
- return nameKey(a) === nameKey(b);
139
- }
140
- const TEXT_NAME_GRAPHEME_SOURCE = String.raw `[[\p{L}\p{N}\p{M}_\x2D]--[\p{Variation_Selector}]]`;
141
- /** One name grapheme: Unicode letters, digits, marks, hyphens, or underscores (no isolated variation selectors), or a complete RGI emoji. Shared by name validation and body @mention scanning. */
142
- export const NAME_GRAPHEME_SOURCE = String.raw `${TEXT_NAME_GRAPHEME_SOURCE}|\p{RGI_Emoji}`;
143
- // Ordinary Unicode names need not trigger the full RGI emoji matcher’s cold compilation.
144
- const TEXT_NAME_PATTERN = new RegExp(`^(?:${TEXT_NAME_GRAPHEME_SOURCE})+(?:/(?:${TEXT_NAME_GRAPHEME_SOURCE})+)*$`, 'v');
145
- const NAME_PATTERN = new RegExp(`^(?:${NAME_GRAPHEME_SOURCE})+(?:/(?:${NAME_GRAPHEME_SOURCE})+)*$`, 'v');
146
- export function validateName(name) {
147
- if (!name || (!TEXT_NAME_PATTERN.test(name) && !NAME_PATTERN.test(name))) {
148
- 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.');
149
- }
150
- }
@@ -0,0 +1,72 @@
1
+ /** Shared identity, error and paging types used below both Channel and Agora. */
2
+ export type Owner = string;
3
+ export type SquareErrorCode = 'invalid_name' | 'invalid_args' | 'unknown_participant' | 'not_joined' | 'already_joined' | 'already_done' | 'capped' | 'throttled' | 'bell_quota' | 'behind' | 'io' | 'unavailable';
4
+ export type InternalSquareErrorCode = SquareErrorCode | 'not_found' | 'cap_reached' | 'conflict' | 'pending_peer';
5
+ export interface SquareErrorFacts {
6
+ /** Copyable recovery for a missing, detached or foreign seat. */
7
+ join?: string;
8
+ /** Express admission rejected before any activity was committed. */
9
+ activityUnsent?: true;
10
+ /** Refusal reason, including absent participants, conflicting reach, and inaccessible activities. */
11
+ reason?: 'never_joined' | 'not_standing' | 'bell_mention_conflict' | 'not_addressed' | 'invalid_reply' | 'invalid_done_body' | 'needs_seat';
12
+ /** The activity id a `catch --id` refusal named. */
13
+ activityId?: string;
14
+ /** Requested recipient spellings, without parsing presentation copy. */
15
+ targets?: readonly string[];
16
+ /** Whether the artifact holds that activity at all; an unknown id needs an ordinary page. */
17
+ activityExists?: boolean;
18
+ pending?: number;
19
+ retryAfterMs?: number;
20
+ }
21
+ export interface CatchWaitStart {
22
+ readonly idleMs: number;
23
+ readonly replacementRequested: boolean;
24
+ readonly replaced: boolean;
25
+ }
26
+ export type OperationProgress = {
27
+ readonly kind: 'waiting';
28
+ readonly reason: 'throttled';
29
+ readonly delayMs?: number;
30
+ } | ({
31
+ readonly kind: 'catch-started';
32
+ readonly participant: string;
33
+ } & CatchWaitStart);
34
+ export interface OperationControl {
35
+ readonly signal?: AbortSignal;
36
+ readonly onProgress?: (progress: OperationProgress) => void;
37
+ }
38
+ export interface OperationFailureDetail {
39
+ readonly operation: 'express' | 'join' | 'catch' | 'history' | 'other';
40
+ readonly subject?: 'caller' | 'filter' | 'recipient';
41
+ readonly participant?: string;
42
+ readonly expressEffect?: 'unsent' | 'landed' | 'unknown';
43
+ readonly landedId?: string;
44
+ readonly historyBounds?: {
45
+ readonly maxLimit: number;
46
+ readonly totalMatches: number;
47
+ };
48
+ }
49
+ export interface OperationError extends Error {
50
+ readonly operation?: OperationFailureDetail;
51
+ }
52
+ export declare class SquareErrorBase<Code extends string> extends Error {
53
+ code: Code;
54
+ facts?: SquareErrorFacts | undefined;
55
+ operation?: OperationFailureDetail;
56
+ constructor(code: Code, message: string, facts?: SquareErrorFacts | undefined);
57
+ }
58
+ export declare class SquareError extends SquareErrorBase<SquareErrorCode> {
59
+ }
60
+ export declare class InternalSquareError extends SquareErrorBase<InternalSquareErrorCode> {
61
+ }
62
+ export declare function isSquareError(error: unknown): error is SquareError | InternalSquareError;
63
+ export declare function validateOwner(owner: Owner): Owner;
64
+ export declare function nameKey(name: string): string;
65
+ export interface IndexPage {
66
+ readonly edge: 'oldest' | 'newest';
67
+ readonly limit: number;
68
+ }
69
+ export interface FactPage<T> {
70
+ readonly items: readonly T[];
71
+ readonly hasMore: boolean;
72
+ }
@@ -0,0 +1,27 @@
1
+ export class SquareErrorBase extends Error {
2
+ code;
3
+ facts;
4
+ operation;
5
+ constructor(code, message, facts) {
6
+ super(message);
7
+ this.code = code;
8
+ this.facts = facts;
9
+ this.name = 'SquareError';
10
+ }
11
+ }
12
+ export class SquareError extends SquareErrorBase {
13
+ }
14
+ export class InternalSquareError extends SquareErrorBase {
15
+ }
16
+ export function isSquareError(error) {
17
+ return error instanceof SquareError || error instanceof InternalSquareError;
18
+ }
19
+ export function validateOwner(owner) {
20
+ if (typeof owner !== 'string' || !owner || Buffer.byteLength(owner, 'utf8') > 512 || /[\u0000-\u001f\u007f-\u009f]/u.test(owner)) {
21
+ throw new SquareError('invalid_args', 'A participant write requires a non-empty owner without control characters (at most 512 UTF-8 bytes).');
22
+ }
23
+ return owner;
24
+ }
25
+ export function nameKey(name) {
26
+ return name.toLocaleLowerCase();
27
+ }
@@ -1,17 +1,12 @@
1
- import { type WakeRouteKind } from './model.js';
2
- import type { WakeTransportPort } from './ports.js';
1
+ import { type WakeTransports } from './wake-transports.js';
2
+ export { createDefaultWakeTransport, createWakeTransport, type WakeTransports } from './wake-transports.js';
3
3
  export declare const PRIVILEGED_HOOK_BUDGET_MS = 3000;
4
4
  export declare function wakeGraceMs(env?: NodeJS.ProcessEnv): number;
5
- export type WakeTransports = Readonly<Partial<Record<WakeRouteKind, WakeTransportPort>>>;
6
5
  interface ProcessNotificationOptions {
7
6
  transports?: WakeTransports;
8
7
  env?: NodeJS.ProcessEnv;
9
8
  now?: () => number;
10
9
  }
11
- /** Concrete transports use wall time; semantic state time belongs to Square operations. */
12
- export declare function createDefaultWakeTransport(env?: NodeJS.ProcessEnv, suppliedTransports?: WakeTransports): Promise<WakeTransportPort>;
13
- /** Route selection only: all implementations receive the same request and final gate. */
14
- export declare function createWakeTransport(transports: WakeTransports): WakeTransportPort;
15
10
  export declare function processActNotificationsOnce(squarePath: string, actIndex: number, opts?: ProcessNotificationOptions): Promise<void>;
16
11
  /** Privileged hook fallback: sweep indexed squares plus the current cwd's local squares within one boundary budget. */
17
12
  export declare function sweepPrivilegedPending(cwd: string, env?: NodeJS.ProcessEnv, suppliedTransports?: WakeTransports, deadline?: number, signal?: AbortSignal): Promise<void>;
@@ -23,4 +18,3 @@ export interface SweepPendingNotificationsOptions {
23
18
  }
24
19
  /** Select old pending attention at a bounded action boundary for an explicit executor. */
25
20
  export declare function sweepPendingNotifications(squarePath: string, opts?: SweepPendingNotificationsOptions): Promise<number[]>;
26
- export {};
@@ -1,10 +1,14 @@
1
+ import { createDefaultWakeTransport } from './wake-transports.js';
2
+ export { createDefaultWakeTransport, createWakeTransport } from './wake-transports.js';
3
+ import { squareWakeProjection } from './wake-preview.js';
1
4
  import path from 'node:path';
2
5
  import { canonicalPath } from './canonical-path.js';
3
6
  import fs from 'node:fs';
4
7
  import { indexedSquareEntries } from './square-index.js';
5
8
  import { InternalSquareError, SquareError } from './model.js';
6
- import { createArtifactPort } from './artifact.js';
9
+ import { channelArtifact, createArtifactPort } from './artifact.js';
7
10
  import { deliverPending, sweepPending } from './delivery-operations.js';
11
+ import { formatMessageId } from './message.js';
8
12
  export const PRIVILEGED_HOOK_BUDGET_MS = 3000;
9
13
  export function wakeGraceMs(env = process.env) {
10
14
  const value = Number.parseInt(env.SQUARE_NOTIFY_DELIVERY_WAIT_MS ?? '5000', 10);
@@ -13,48 +17,6 @@ export function wakeGraceMs(env = process.env) {
13
17
  }
14
18
  return value;
15
19
  }
16
- /** Concrete transports use wall time; semantic state time belongs to Square operations. */
17
- export async function createDefaultWakeTransport(env = process.env, suppliedTransports) {
18
- if (suppliedTransports !== undefined)
19
- return createWakeTransport(suppliedTransports);
20
- const transports = {};
21
- try {
22
- const { CodexQueueTransport } = await import('./codex-queue.js');
23
- transports['codex-queue'] = new CodexQueueTransport({ env });
24
- }
25
- catch { /* core-only builds may omit Codex */ }
26
- if (env.SQUARE_DISABLE_PASEO_WAKE !== '1') {
27
- try {
28
- const { PaseoWakeTransport } = await import('./paseo-delivery.js');
29
- transports.paseo = new PaseoWakeTransport();
30
- }
31
- catch { /* Paseo is optional */ }
32
- }
33
- const { createClaudeWakeTransport } = await import('./claude-delivery.js');
34
- transports['claude-native'] = createClaudeWakeTransport(env);
35
- const { HttpWakeTransport } = await import('./http-delivery.js');
36
- transports.http = new HttpWakeTransport({ env });
37
- return createWakeTransport(transports);
38
- }
39
- /** Route selection only: all implementations receive the same request and final gate. */
40
- export function createWakeTransport(transports) {
41
- const missing = (kind) => ({ outcome: 'not-capable', diagnostic: `no transport for ${kind}` });
42
- return {
43
- async probe(route) {
44
- const transport = transports[route.kind];
45
- if (transport === undefined)
46
- return missing(route.kind);
47
- try {
48
- return await transport.probe?.(route) ?? true;
49
- }
50
- catch (error) {
51
- return { outcome: 'not-capable', diagnostic: error instanceof Error ? error.message : String(error) };
52
- }
53
- },
54
- attempt: (request, timeoutMs, beforeSend) => transports[request.route.kind]?.attempt(request, timeoutMs, beforeSend)
55
- ?? Promise.resolve(missing(request.route.kind)),
56
- };
57
- }
58
20
  export async function processActNotificationsOnce(squarePath, actIndex, opts = {}) {
59
21
  const env = opts.env ?? process.env;
60
22
  const now = opts.now ?? Date.now;
@@ -64,7 +26,10 @@ export async function processActNotificationsOnce(squarePath, actIndex, opts = {
64
26
  await artifact.read(facts => facts.header());
65
27
  const transport = await createDefaultWakeTransport(env, opts.transports);
66
28
  try {
67
- await deliverPending({ artifact, transport, location: squarePath, activity: actIndex, timeoutMs: Number(env.SQUARE_NOTIFY_DELIVERY_WAIT_MS ?? 5000), now: now() });
29
+ const message = (await artifact.read(facts => { const activity = facts.activity(actIndex); return activity?.kind === 'say' ? activity.messageIndex : undefined; })).value;
30
+ if (message === undefined)
31
+ return;
32
+ await deliverPending({ artifact: channelArtifact(artifact), preparePresentation: squareWakeProjection(artifact), transport, location: squarePath, message: formatMessageId(message), timeoutMs: Number(env.SQUARE_NOTIFY_DELIVERY_WAIT_MS ?? 5000), now: now() });
68
33
  }
69
34
  catch { /* Delivery failure does not undo the activity. */ }
70
35
  }
@@ -106,14 +71,14 @@ export async function sweepPrivilegedPending(cwd, env = process.env, suppliedTra
106
71
  break;
107
72
  const limit = Number.parseInt(env.SQUARE_NOTIFY_SWEEP_LIMIT ?? '8', 10);
108
73
  const graceMs = 0;
109
- const selected = await sweepPending({ artifact, location: squarePath, now: Date.now(), graceMs, limit: Number.isFinite(limit) && limit > 0 ? limit : 8 }).catch(() => []);
110
- for (const actIndex of selected) {
74
+ const selected = await sweepPending({ artifact: channelArtifact(artifact), location: squarePath, now: Date.now(), graceMs, limit: Number.isFinite(limit) && limit > 0 ? limit : 8 }).catch(() => []);
75
+ for (const messageIndex of selected) {
111
76
  const remaining = remainingMs();
112
77
  if (remaining === 0 || signal?.aborted)
113
78
  break;
114
79
  const configured = Number(env.SQUARE_NOTIFY_DELIVERY_WAIT_MS ?? 5000);
115
80
  const timeoutMs = Math.max(1, Math.min(Number.isFinite(configured) && configured > 0 ? configured : 5000, remaining));
116
- await deliverPending({ artifact, transport, location: squarePath, activity: actIndex, timeoutMs, now: Date.now() }).catch(() => undefined);
81
+ await deliverPending({ artifact: channelArtifact(artifact), preparePresentation: squareWakeProjection(artifact), transport, location: squarePath, message: formatMessageId(messageIndex), timeoutMs, now: Date.now() }).catch(() => undefined);
117
82
  }
118
83
  }
119
84
  finally {
@@ -134,7 +99,7 @@ export async function sweepPendingNotifications(squarePath, opts = {}) {
134
99
  let selected;
135
100
  try {
136
101
  await artifact.read(facts => facts.header());
137
- selected = await sweepPending({ artifact, location: squarePath, now, graceMs: wakeGraceMs(env), limit });
102
+ selected = await sweepPending({ artifact: channelArtifact(artifact), location: squarePath, now, graceMs: wakeGraceMs(env), limit });
138
103
  }
139
104
  catch (error) {
140
105
  if (error instanceof InternalSquareError && error.code === 'not_found')
@@ -145,8 +110,8 @@ export async function sweepPendingNotifications(squarePath, opts = {}) {
145
110
  await artifact.close();
146
111
  }
147
112
  if (opts.dispatchCandidate !== undefined) {
148
- for (const actIndex of selected)
149
- await opts.dispatchCandidate(actIndex);
113
+ for (const messageIndex of selected)
114
+ await opts.dispatchCandidate(messageIndex);
150
115
  }
151
116
  return selected;
152
117
  }
@@ -30,9 +30,8 @@ export interface FailureReport {
30
30
  readonly code: string;
31
31
  readonly message: string;
32
32
  readonly facts?: {
33
- readonly reason?: 'never_joined' | 'not_standing' | 'bell_mention_conflict' | 'not_addressed' | 'migration_required' | 'invalid_reply' | 'invalid_done_body' | 'needs_seat';
33
+ readonly reason?: 'never_joined' | 'not_standing' | 'bell_mention_conflict' | 'not_addressed' | 'invalid_reply' | 'invalid_done_body' | 'needs_seat';
34
34
  readonly activityId?: string;
35
- readonly ledgerRoot?: string;
36
35
  readonly activityExists?: boolean;
37
36
  };
38
37
  readonly operation?: OperationFailureDetail;
@@ -335,13 +335,7 @@ export function renderFailureLines(failure, options = {}) {
335
335
  const reason = facts?.reason;
336
336
  const marked = reason === 'never_joined' || reason === 'not_standing';
337
337
  const lines = marked ? failure.message.split('\n').map((line) => `✕ ${line}`) : [failure.message];
338
- if (reason === 'migration_required') {
339
- if (facts?.ledgerRoot !== undefined)
340
- lines.push('square ledger migrate', 'square ledger verify');
341
- else if (squarePath !== undefined)
342
- lines.push(`${commandPrefix(squarePath)} migrate`);
343
- }
344
- else if (squarePath !== undefined && name !== undefined && reason === 'needs_seat') {
338
+ if (reason === 'needs_seat' && squarePath !== undefined && name !== undefined) {
345
339
  lines.splice(0, 1, `✕ ${failure.message}`);
346
340
  lines.push(joinRecoveryCommand(squarePath, name, owner));
347
341
  }
package/dist/owner.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { type Owner } from './model.js';
2
- export type { Owner } from './model.js';
1
+ import { type Owner } from './neutral.js';
2
+ export type { Owner } from './neutral.js';
3
3
  export interface HarnessCoordinate {
4
4
  readonly channel: string;
5
5
  readonly session: string;
package/dist/owner.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import os from 'node:os';
2
- import { validateOwner } from './model.js';
2
+ import { validateOwner } from './neutral.js';
3
3
  export function harnessOwner(coordinate) {
4
4
  const channel = nonempty(coordinate.channel, 'channel');
5
5
  const session = nonempty(coordinate.session, 'session');
@@ -1,4 +1,4 @@
1
- import type { WakeRequest, WakeOutcome, WakeTransportPort } from './ports.js';
1
+ import type { WakeRequest, WakeOutcome, WakeTransportPort } from './channel-transport.js';
2
2
  import { discoverPaseoAgents, waitForPaseoWakeBoundary } from './paseo-state.js';
3
3
  import { sendPaseoWake } from './wake-sink.js';
4
4
  export interface PaseoWakeTransportOptions {