@astrosheep/square 0.4.0 → 0.4.1
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.
- package/dist/artifact.d.ts +1 -3
- package/dist/artifact.js +12 -36
- package/dist/attention-presentation.d.ts +2 -22
- package/dist/attention-presentation.js +9 -12
- package/dist/boundary-presentation.d.ts +3 -14
- package/dist/boundary-presentation.js +29 -104
- package/dist/card-presentation.d.ts +1 -0
- package/dist/card-presentation.js +2 -0
- package/dist/catch-decisions.js +1 -7
- package/dist/claude-delivery.d.ts +13 -8
- package/dist/claude-delivery.js +40 -93
- package/dist/claude-mcp-context.js +2 -2
- package/dist/claude-mod.js +28 -140
- package/dist/claude-transport.d.ts +3 -3
- package/dist/claude-transport.js +20 -44
- package/dist/cli/context.js +7 -9
- package/dist/cli/observation-commands.d.ts +2 -0
- package/dist/cli/observation-commands.js +12 -2
- package/dist/cli/registry.js +3 -1
- package/dist/codex-address.d.ts +2 -2
- package/dist/codex-boundary-state.d.ts +1 -5
- package/dist/codex-boundary-state.js +0 -4
- package/dist/codex-hook.d.ts +7 -10
- package/dist/codex-hook.js +69 -33
- package/dist/codex-queue.d.ts +10 -11
- package/dist/codex-queue.js +32 -32
- package/dist/codex-watch.d.ts +2 -0
- package/dist/codex-watch.js +100 -0
- package/dist/decisions.js +2 -2
- package/dist/delivery-deferral.d.ts +8 -17
- package/dist/delivery-deferral.js +0 -28
- package/dist/delivery-frame.d.ts +7 -0
- package/dist/delivery-frame.js +11 -0
- package/dist/delivery-ledger-sqlite.d.ts +97 -0
- package/dist/delivery-ledger-sqlite.js +496 -0
- package/dist/delivery-ledger.d.ts +196 -0
- package/dist/delivery-ledger.js +268 -0
- package/dist/delivery-lifetime.d.ts +6 -0
- package/dist/delivery-lifetime.js +45 -0
- package/dist/harness-links.js +61 -7
- package/dist/harness.js +12 -17
- package/dist/help.js +2 -0
- package/dist/http-delivery.d.ts +5 -9
- package/dist/http-delivery.js +20 -77
- package/dist/inbox.d.ts +6 -49
- package/dist/inbox.js +24 -371
- package/dist/index.d.ts +6 -3
- package/dist/index.js +4 -1
- package/dist/mcp.js +1 -1
- package/dist/model.d.ts +7 -47
- package/dist/model.js +1 -7
- package/dist/notifications.d.ts +24 -20
- package/dist/notifications.js +39 -108
- package/dist/operation-control.d.ts +4 -0
- package/dist/operation-control.js +20 -0
- package/dist/packages/agent-delivery/src/claude-native.d.ts +2 -0
- package/dist/packages/agent-delivery/src/claude-native.js +5 -0
- package/dist/packages/agent-delivery/src/codex-boundary.d.ts +11 -6
- package/dist/packages/agent-delivery/src/codex-boundary.js +31 -40
- package/dist/packages/agent-delivery/src/codex-hook.d.ts +17 -12
- package/dist/packages/agent-delivery/src/codex-hook.js +26 -39
- package/dist/packages/agent-delivery/src/codex.d.ts +3 -0
- package/dist/packages/agent-delivery/src/codex.js +41 -4
- package/dist/packages/agent-delivery/src/index.d.ts +3 -1
- package/dist/packages/agent-delivery/src/index.js +1 -1
- package/dist/packages/agent-delivery/src/paseo.js +11 -2
- package/dist/packages/agent-delivery/src/types.d.ts +1 -0
- package/dist/paseo-delivery.d.ts +5 -5
- package/dist/paseo-delivery.js +19 -97
- package/dist/paseo.d.ts +1 -1
- package/dist/paseo.js +1 -1
- package/dist/plugins/opencode/index.d.ts +5 -3
- package/dist/plugins/opencode/index.js +99 -21
- package/dist/plugins/opencode-shared/delivery.d.ts +23 -0
- package/dist/plugins/opencode-shared/delivery.js +109 -0
- package/dist/plugins/opencode-v1/index.d.ts +28 -0
- package/dist/plugins/opencode-v1/index.js +41 -0
- package/dist/plugins/opencode-v1/runtime.d.ts +56 -0
- package/dist/plugins/opencode-v1/runtime.js +152 -0
- package/dist/plugins/opencode-v1/transport.d.ts +13 -0
- package/dist/plugins/opencode-v1/transport.js +50 -0
- package/dist/plugins/paseo/shared/card-presentation.d.ts +10 -0
- package/dist/plugins/paseo/shared/card-presentation.js +7 -0
- package/dist/ports.d.ts +31 -90
- package/dist/session-lifecycle.d.ts +8 -7
- package/dist/session-lifecycle.js +40 -42
- package/dist/sqlite-square.d.ts +14 -23
- package/dist/sqlite-square.js +132 -139
- package/dist/square-api.js +4 -7
- package/dist/square-core.d.ts +0 -1
- package/dist/square-domain.d.ts +5 -17
- package/dist/square-domain.js +104 -221
- package/dist/square-facade.d.ts +18 -15
- package/dist/square-post.d.ts +53 -0
- package/dist/square-post.js +4 -0
- package/dist/square-posting.d.ts +13 -0
- package/dist/square-posting.js +174 -0
- package/dist/square-producer-sqlite.d.ts +11 -0
- package/dist/square-producer-sqlite.js +210 -0
- package/dist/square-sqlite.d.ts +15 -25
- package/dist/square-sqlite.js +150 -226
- package/dist/views.d.ts +2 -2
- package/dist/views.js +0 -1
- package/dist/wake-sink.d.ts +4 -11
- package/dist/wake-sink.js +8 -32
- package/dist/wake-transports.d.ts +4 -7
- package/dist/wake-transports.js +8 -39
- package/examples/symmetric-domains.mjs +29 -249
- package/guides/change-waits.md +13 -53
- package/guides/claude-native.md +19 -175
- package/guides/codex-delivery.md +55 -80
- package/guides/delivery-primitives.md +48 -207
- package/guides/operation-cancellation.md +2 -5
- package/guides/participant-ownership.md +25 -89
- package/guides/pi-native-delivery.md +19 -56
- package/guides/pi-tools.md +6 -8
- package/guides/square-api.md +113 -615
- package/package.json +8 -8
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/hooks/hooks.json +1 -1
- package/plugins/claude/{hooks/register.js → mods/square.js} +4 -7
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/codex/hooks/hooks.json +11 -23
- package/plugins/opencode/index.ts +91 -23
- package/plugins/opencode-shared/delivery.ts +91 -0
- package/plugins/opencode-v1/index.ts +36 -0
- package/plugins/opencode-v1/runtime.ts +134 -0
- package/plugins/opencode-v1/transport.ts +41 -0
- package/plugins/paseo/OVERVIEW.md +5 -5
- package/plugins/paseo/README.md +7 -6
- package/plugins/paseo/client/markdown.tsx +5 -5
- package/plugins/paseo/client/square-message.tsx +28 -24
- package/plugins/paseo/client/transform.ts +8 -5
- package/plugins/paseo/shared/card-presentation.ts +16 -0
- package/plugins/paseo/shared/message.ts +6 -11
- package/plugins/pi/square-pi.js +156 -417
- package/dist/channel-artifact.d.ts +0 -10
- package/dist/channel-artifact.js +0 -55
- package/dist/channel-delivery.d.ts +0 -11
- package/dist/channel-delivery.js +0 -51
- package/dist/channel-domain.d.ts +0 -3
- package/dist/channel-domain.js +0 -83
- package/dist/channel-facade.d.ts +0 -76
- package/dist/channel-facade.js +0 -1
- package/dist/channel-internal.d.ts +0 -126
- package/dist/channel-internal.js +0 -440
- package/dist/channel-metadata.d.ts +0 -11
- package/dist/channel-metadata.js +0 -1
- package/dist/channel-ports.d.ts +0 -241
- package/dist/channel-ports.js +0 -1
- package/dist/channel-presentation.d.ts +0 -17
- package/dist/channel-presentation.js +0 -22
- package/dist/channel-sqlite-schema.d.ts +0 -13
- package/dist/channel-sqlite-schema.js +0 -81
- package/dist/channel-transport.d.ts +0 -62
- package/dist/channel-transport.js +0 -1
- package/dist/channel.d.ts +0 -84
- package/dist/channel.js +0 -60
- package/dist/claude-delivery-status.d.ts +0 -13
- package/dist/claude-delivery-status.js +0 -46
- package/dist/cmd/notify-once.d.ts +0 -2
- package/dist/cmd/notify-once.js +0 -29
- package/dist/delivery-attempts.d.ts +0 -52
- package/dist/delivery-attempts.js +0 -73
- package/dist/delivery-effects.d.ts +0 -139
- package/dist/delivery-effects.js +0 -163
- package/dist/delivery-health.d.ts +0 -51
- package/dist/delivery-health.js +0 -173
- package/dist/delivery-operations.d.ts +0 -22
- package/dist/delivery-operations.js +0 -198
- package/dist/delivery-primitives.d.ts +0 -5
- package/dist/delivery-primitives.js +0 -3
- package/dist/delivery.d.ts +0 -56
- package/dist/delivery.js +0 -80
- package/dist/plugins/opencode/delivery.d.ts +0 -19
- package/dist/plugins/opencode/delivery.js +0 -223
- package/dist/presentation-operations.d.ts +0 -5
- package/dist/presentation-operations.js +0 -86
- package/dist/sqlite-channel.d.ts +0 -15
- package/dist/sqlite-channel.js +0 -600
- package/dist/square-index.d.ts +0 -16
- package/dist/square-index.js +0 -106
- package/dist/wake-preview.d.ts +0 -7
- package/dist/wake-preview.js +0 -15
- package/plugins/opencode/delivery.ts +0 -198
- /package/dist/{channel-values.d.ts → square-values.d.ts} +0 -0
- /package/dist/{channel-values.js → square-values.js} +0 -0
package/dist/inbox.js
CHANGED
|
@@ -1,382 +1,35 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { createArtifactPort } from './artifact.js';
|
|
3
|
+
import { discoverSquares, MAX_LIST_LIMIT } from './list.js';
|
|
3
4
|
import { canonicalPath } from './canonical-path.js';
|
|
4
|
-
|
|
5
|
-
import { freshWatchLease, WATCH_STALE_MS } from './runtime.js';
|
|
6
|
-
import { nativePendingHead, pendingHeads } from './delivery.js';
|
|
7
|
-
import { deliveryDeferralExpiry } from './delivery-deferral.js';
|
|
8
|
-
import { projectMessageNotification, renderSquareReminder } from './attention-presentation.js';
|
|
9
|
-
export function pendingKey(membership, messageIndex) {
|
|
10
|
-
return membership.location + '\0' + membership.seat.participant.toLocaleLowerCase() + '\0' + messageIndex;
|
|
11
|
-
}
|
|
12
|
-
function eligibleNotifications(facts, membership, now = Date.now()) {
|
|
13
|
-
const seat = facts.seat(membership.seat.participant);
|
|
14
|
-
if (seat === undefined || seat.attachment !== 'attached' || seat.generation !== membership.seat.generation
|
|
15
|
-
|| seat.owner !== membership.seat.owner)
|
|
16
|
-
return { notifications: [] };
|
|
17
|
-
const cancelledThrough = facts.channel.cancelledThrough(seat.participant, seat.generation);
|
|
18
|
-
let expiry = Infinity;
|
|
19
|
-
const notifications = membership.notifications.filter(notification => {
|
|
20
|
-
if (cancelledThrough !== undefined && notification.messageIndex <= cancelledThrough
|
|
21
|
-
|| facts.deliveryEvidence(seat.participant, seat.generation, formatMessageId(notification.messageIndex))
|
|
22
|
-
.some(row => row.outcome !== 'failed' && row.outcome !== 'released'))
|
|
23
|
-
return false;
|
|
24
|
-
const deferredUntil = deliveryDeferralExpiry(facts.channel, seat, notification.messageIndex, now);
|
|
25
|
-
if (deferredUntil === undefined)
|
|
26
|
-
return true;
|
|
27
|
-
expiry = Math.min(expiry, deferredUntil);
|
|
28
|
-
return false;
|
|
29
|
-
});
|
|
30
|
-
return { notifications, ...(expiry === Infinity ? {} : { deliveryDeferralExpiry: expiry }) };
|
|
31
|
-
}
|
|
32
|
-
/** Revalidate a supplied batch immediately before the boundary's independent claim/send checks. */
|
|
33
|
-
export async function projectBoundaryEligibility(inbox) {
|
|
34
|
-
const grouped = new Map();
|
|
35
|
-
for (const membership of inbox.filter(candidate => candidate.notifications.length > 0)) {
|
|
36
|
-
const entries = grouped.get(membership.location) ?? [];
|
|
37
|
-
entries.push(membership);
|
|
38
|
-
grouped.set(membership.location, entries);
|
|
39
|
-
}
|
|
40
|
-
const pending = [];
|
|
41
|
-
for (const [location, memberships] of grouped) {
|
|
42
|
-
const artifact = createArtifactPort(location);
|
|
43
|
-
try {
|
|
44
|
-
const { value } = await artifact.read(facts => memberships.flatMap(membership => {
|
|
45
|
-
const eligibility = eligibleNotifications(facts, membership);
|
|
46
|
-
return eligibility.notifications.length === 0 ? [] : [{ ...membership, ...eligibility }];
|
|
47
|
-
}));
|
|
48
|
-
pending.push(...value);
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
// An unreadable or stale candidate cannot authorize boundary presentation.
|
|
52
|
-
}
|
|
53
|
-
finally {
|
|
54
|
-
await artifact.close();
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return pending;
|
|
58
|
-
}
|
|
59
|
-
/** HOME is a candidate path index; current seats and delivery evidence live in the artifact. */
|
|
60
|
-
export async function projectSessionCandidates(input) {
|
|
61
|
-
const location = input.location === undefined ? undefined : await canonicalPath(input.location);
|
|
62
|
-
const entries = await indexedSquareEntries({ owner: input.owner }, input.env ?? process.env);
|
|
63
|
-
const candidates = new Map();
|
|
64
|
-
for (const entry of entries) {
|
|
65
|
-
if (location !== undefined && location !== entry.location)
|
|
66
|
-
continue;
|
|
67
|
-
const key = JSON.stringify([entry.location, entry.owner]);
|
|
68
|
-
candidates.set(key, { location: entry.location, owner: entry.owner });
|
|
69
|
-
}
|
|
70
|
-
return [...candidates.values()];
|
|
71
|
-
}
|
|
72
|
-
function groupCandidates(candidates) {
|
|
73
|
-
const grouped = new Map();
|
|
74
|
-
for (const { location, owner } of candidates) {
|
|
75
|
-
const owners = grouped.get(location) ?? [];
|
|
76
|
-
owners.push(owner);
|
|
77
|
-
grouped.set(location, owners);
|
|
78
|
-
}
|
|
79
|
-
return grouped;
|
|
80
|
-
}
|
|
81
|
-
function locatedSeats(facts, location, seats) {
|
|
82
|
-
return seats.flatMap(seat => {
|
|
83
|
-
const participant = facts.participant(seat.participant);
|
|
84
|
-
const index = participant?.lastJoinIndex;
|
|
85
|
-
const joined = index === undefined ? undefined : facts.activity(index);
|
|
86
|
-
return joined?.kind === 'join' ? [{ location, seat, joinedAt: joined.at }] : [{ location, seat, joinedAt: 0 }];
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
/** Lifecycle reads resolve seats and join times, never pending activity bodies. */
|
|
90
|
-
export async function projectSessionSeats(input, signal) {
|
|
91
|
-
const seats = [];
|
|
92
|
-
for (const [location, owners] of groupCandidates(await projectSessionCandidates(input))) {
|
|
93
|
-
signal?.throwIfAborted();
|
|
94
|
-
const artifact = createArtifactPort(location, undefined, signal);
|
|
95
|
-
try {
|
|
96
|
-
seats.push(...(await artifact.read(facts => locatedSeats(facts, location, owners.flatMap(owner => facts.seatsForOwner(owner))), signal)).value);
|
|
97
|
-
}
|
|
98
|
-
catch (error) {
|
|
99
|
-
if (signal?.aborted)
|
|
100
|
-
throw signal.reason ?? error;
|
|
101
|
-
}
|
|
102
|
-
finally {
|
|
103
|
-
await artifact.close();
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return seats;
|
|
107
|
-
}
|
|
108
|
-
/** Public count boundary: one coherent read per artifact, without pending body hydration. */
|
|
5
|
+
/** Filesystem-scoped membership counts. This is a World read, not a delivery scan. */
|
|
109
6
|
export async function sessionInboxCounts(input, signal) {
|
|
7
|
+
const root = path.resolve(input.cwd ?? process.cwd());
|
|
110
8
|
const rows = [];
|
|
111
|
-
|
|
9
|
+
let after;
|
|
10
|
+
do {
|
|
112
11
|
signal?.throwIfAborted();
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
catch (error) {
|
|
120
|
-
if (signal?.aborted)
|
|
121
|
-
throw signal.reason ?? error;
|
|
122
|
-
}
|
|
123
|
-
finally {
|
|
124
|
-
await artifact.close();
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return rows;
|
|
128
|
-
}
|
|
129
|
-
/** One authoritative read per canonical artifact, even when discovery names several bodies. */
|
|
130
|
-
async function projectPending(candidates, cache, signal) {
|
|
131
|
-
const pending = [];
|
|
132
|
-
for (const [location, owners] of groupCandidates(candidates)) {
|
|
133
|
-
signal?.throwIfAborted();
|
|
134
|
-
const artifact = createArtifactPort(location, undefined, signal);
|
|
135
|
-
try {
|
|
136
|
-
const { value: current } = await artifact.read(facts => {
|
|
137
|
-
const seats = owners.flatMap(owner => facts.seatsForOwner(owner).filter(seat => seat.attachment === 'attached'));
|
|
138
|
-
const coordinate = JSON.stringify([owners, seats.map(seat => [seat.participant, seat.owner, seat.generation])]);
|
|
139
|
-
const header = facts.header();
|
|
140
|
-
let cached = cache?.get(location);
|
|
141
|
-
if (cached?.coordinate !== coordinate || cached.version !== header.revision) {
|
|
142
|
-
cached = { coordinate, version: header.revision, value: locatedSeats(facts, location, seats).map(membership => {
|
|
143
|
-
const heads = pendingHeads(facts, membership.seat.participant, Date.now(), false);
|
|
144
|
-
const messages = new Map(facts.channel.messages(heads.map(head => head.index)).map(item => [item.index, item]));
|
|
145
|
-
return { ...membership, notifications: heads.flatMap(head => {
|
|
146
|
-
const item = messages.get(head.index);
|
|
147
|
-
if (item === undefined)
|
|
148
|
-
return [];
|
|
149
|
-
return [{ messageIndex: item.index, ...(head.activityIndex === undefined ? {} : { actIndex: head.activityIndex }),
|
|
150
|
-
actor: item.from, at: item.at, body: item.body, ...(item.preview === undefined ? {} : { preview: item.preview }), ...(head.route === undefined ? {} : { route: head.route }) }];
|
|
151
|
-
}) };
|
|
152
|
-
}) };
|
|
153
|
-
}
|
|
154
|
-
// Cache conversation content, never leases or delivery eligibility: evidence can change
|
|
155
|
-
// without a conversation revision and must not force body hydration or self-wake retries.
|
|
156
|
-
return { cached, pending: cached.value.map(membership => {
|
|
157
|
-
const catchLease = freshWatchLease(facts, membership.seat.participant);
|
|
158
|
-
// Cached conversation content, but claim coverage and deferrals are re-read on every projection.
|
|
159
|
-
const observed = { ...membership, notifications: membership.notifications.filter(note => !facts.channel.suppressed(membership.seat.participant, membership.seat.generation, note.messageIndex, Date.now())) };
|
|
160
|
-
return { ...observed, ...eligibleNotifications(facts, observed),
|
|
161
|
-
...(catchLease === undefined ? {} : { catchLease }) };
|
|
162
|
-
}) };
|
|
163
|
-
}, signal);
|
|
164
|
-
cache?.set(location, current.cached);
|
|
165
|
-
pending.push(...current.pending);
|
|
166
|
-
}
|
|
167
|
-
catch (error) {
|
|
168
|
-
cache?.delete(location);
|
|
169
|
-
if (signal?.aborted)
|
|
170
|
-
throw signal.reason ?? error;
|
|
171
|
-
// Unreadable candidates remain watch targets, but never authorize cached content.
|
|
172
|
-
}
|
|
173
|
-
finally {
|
|
174
|
-
await artifact.close();
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return pending;
|
|
178
|
-
}
|
|
179
|
-
/** One current eligibility + source projection read; senders use text, ACK coverage uses clipped. */
|
|
180
|
-
export function nativePendingSource(facts, input) {
|
|
181
|
-
const head = nativePendingHead(facts, input);
|
|
182
|
-
if (head === undefined)
|
|
183
|
-
return undefined;
|
|
184
|
-
const item = facts.channel.message(input.index);
|
|
185
|
-
if (item === undefined)
|
|
186
|
-
return undefined;
|
|
187
|
-
const projected = projectMessageNotification({ squarePath: input.location, recipient: input.participant,
|
|
188
|
-
actor: item.from, body: item.body, ...(item.preview === undefined ? {} : { preview: item.preview }),
|
|
189
|
-
...(head.activityIndex === undefined || head.route === undefined ? {} : { agora: { actIndex: head.activityIndex, route: head.route } }) });
|
|
190
|
-
return { text: projected.text, clipped: projected.clipped };
|
|
191
|
-
}
|
|
192
|
-
/** The sender-facing fixed reminder over the shared source projection. */
|
|
193
|
-
export function nativePendingPreview(facts, input) {
|
|
194
|
-
const source = nativePendingSource(facts, input);
|
|
195
|
-
return source === undefined ? undefined : { payload: renderSquareReminder([source.text]), clipped: source.clipped };
|
|
196
|
-
}
|
|
197
|
-
export async function sessionInbox(owner, env = process.env, signal) {
|
|
198
|
-
return projectPending(await projectSessionCandidates({ owner, env }), undefined, signal);
|
|
199
|
-
}
|
|
200
|
-
async function capture(observer, kind, signal) {
|
|
201
|
-
const generation = observer.generation;
|
|
202
|
-
try {
|
|
203
|
-
const value = await observer.read(signal);
|
|
204
|
-
return { kind, value, wait: (timeout, abort) => observer.changed(value, timeout, abort) };
|
|
205
|
-
}
|
|
206
|
-
catch (error) {
|
|
207
|
-
if (signal.aborted)
|
|
208
|
-
throw signal.reason ?? error;
|
|
209
|
-
// Failed revision reads are not changes; only a successful retry wakes projection.
|
|
210
|
-
return { kind, wait: async (timeout, abort) => {
|
|
211
|
-
const deadline = Date.now() + Math.max(0, timeout);
|
|
212
|
-
let since = generation;
|
|
213
|
-
while (await observer.hinted(since, Math.max(0, deadline - Date.now()), abort)) {
|
|
214
|
-
since = observer.generation;
|
|
215
|
-
try {
|
|
216
|
-
await observer.read(abort);
|
|
217
|
-
return true;
|
|
218
|
-
}
|
|
219
|
-
catch (error) {
|
|
220
|
-
if (abort.aborted)
|
|
221
|
-
throw abort.reason ?? error;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
return false;
|
|
225
|
-
} };
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
/** Session-scoped projection over process-shared storage change detectors. */
|
|
229
|
-
export async function observeSessionPending(owner, suppliedEnv = process.env) {
|
|
230
|
-
const env = { ...suppliedEnv };
|
|
231
|
-
const query = { owner, env };
|
|
232
|
-
const index = await observeSquareIndex(env);
|
|
233
|
-
const artifacts = new Map();
|
|
234
|
-
const cache = new Map();
|
|
235
|
-
const lifetime = new AbortController();
|
|
236
|
-
async function currentInbox(candidates, signal) {
|
|
237
|
-
const inbox = await projectPending(candidates, cache, signal);
|
|
238
|
-
const now = Date.now();
|
|
239
|
-
return inbox.map((membership) => {
|
|
240
|
-
const { catchLease, ...rest } = membership;
|
|
241
|
-
return catchLease !== undefined && catchLease.expiresAt > now && now - catchLease.heartbeatAt <= WATCH_STALE_MS
|
|
242
|
-
? membership : rest;
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
function eligibleInbox(inbox, excludeKeys) {
|
|
246
|
-
const eligible = inbox.filter(membership => membership.notifications.length > 0);
|
|
247
|
-
return excludeKeys === undefined ? eligible : eligible.flatMap((membership) => {
|
|
248
|
-
const notifications = membership.notifications.filter((notification) => !excludeKeys.has(pendingKey(membership, notification.messageIndex)));
|
|
249
|
-
return notifications.length === 0 ? [] : [{ ...membership, notifications }];
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
return {
|
|
253
|
-
async read(options = {}) {
|
|
254
|
-
const signal = AbortSignal.any([lifetime.signal, ...(options.signal === undefined ? [] : [options.signal])]);
|
|
255
|
-
signal.throwIfAborted();
|
|
256
|
-
const inbox = await currentInbox(await projectSessionCandidates(query), signal);
|
|
257
|
-
const pending = eligibleInbox(inbox, options.excludeKeys);
|
|
258
|
-
signal.throwIfAborted();
|
|
259
|
-
return pending;
|
|
260
|
-
},
|
|
261
|
-
async wait(timeoutMs, options = {}) {
|
|
262
|
-
const deadline = Date.now() + Math.max(0, timeoutMs);
|
|
263
|
-
const expired = new AbortController();
|
|
264
|
-
const timer = Number.isFinite(timeoutMs) && timeoutMs > 0
|
|
265
|
-
? setTimeout(() => expired.abort(), Math.min(timeoutMs, 2_147_483_647)) : undefined;
|
|
266
|
-
const signal = AbortSignal.any([lifetime.signal, expired.signal, ...(options.signal === undefined ? [] : [options.signal])]);
|
|
267
|
-
let canReturn = !options.skipImmediate;
|
|
268
|
-
let previousEligible;
|
|
269
|
-
let armed = false;
|
|
270
|
-
let candidates = [];
|
|
271
|
-
let candidatePresence;
|
|
272
|
-
let discoveredCandidates = false;
|
|
12
|
+
const page = await discoverSquares(root, { limit: MAX_LIST_LIMIT, ...(after === undefined ? {} : { after }) }, { signal });
|
|
13
|
+
for (const item of page.items) {
|
|
14
|
+
signal?.throwIfAborted();
|
|
15
|
+
const location = await canonicalPath(path.resolve(root, item.path));
|
|
16
|
+
const artifact = createArtifactPort(location, undefined, signal);
|
|
273
17
|
try {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const presence = sources[0];
|
|
279
|
-
const currentPresence = presence.value;
|
|
280
|
-
if (!discoveredCandidates || currentPresence !== undefined && !Object.is(currentPresence, candidatePresence)) {
|
|
281
|
-
candidates = await projectSessionCandidates(query);
|
|
282
|
-
candidatePresence = currentPresence;
|
|
283
|
-
discoveredCandidates = true;
|
|
284
|
-
}
|
|
285
|
-
const paths = new Set(candidates.map((candidate) => candidate.location));
|
|
286
|
-
for (const [file, entry] of artifacts) {
|
|
287
|
-
if (!paths.has(file)) {
|
|
288
|
-
entry.observer.close();
|
|
289
|
-
entry.delivery.close();
|
|
290
|
-
artifacts.delete(file);
|
|
291
|
-
cache.delete(file);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
for (const file of paths) {
|
|
295
|
-
if (artifacts.has(file))
|
|
296
|
-
continue;
|
|
297
|
-
const observer = await observeSquareRevision(file);
|
|
298
|
-
const delivery = await observeSquareDeliveryRevision(file);
|
|
299
|
-
if (signal.aborted) {
|
|
300
|
-
observer.close();
|
|
301
|
-
delivery.close();
|
|
302
|
-
return [];
|
|
303
|
-
}
|
|
304
|
-
artifacts.set(file, { observer, delivery });
|
|
305
|
-
}
|
|
306
|
-
// Capture both revision baselines before selecting seats and pending facts. A takeover
|
|
307
|
-
// during an empty selection must remain visible to the already armed observers.
|
|
308
|
-
for (const entry of artifacts.values()) {
|
|
309
|
-
sources.push(await capture(entry.observer, 'artifact', signal));
|
|
310
|
-
sources.push(await capture(entry.delivery, 'delivery', signal));
|
|
311
|
-
}
|
|
312
|
-
const inbox = await currentInbox(candidates, signal);
|
|
313
|
-
const pending = eligibleInbox(inbox, options.excludeKeys);
|
|
314
|
-
const eligibleKeys = JSON.stringify(pending.flatMap((membership) => membership.notifications.map((note) => pendingKey(membership, note.messageIndex))).sort());
|
|
315
|
-
if (previousEligible !== undefined && previousEligible !== eligibleKeys)
|
|
316
|
-
canReturn = true;
|
|
317
|
-
previousEligible = eligibleKeys;
|
|
318
|
-
if (signal.aborted)
|
|
319
|
-
return [];
|
|
320
|
-
if (canReturn && pending.length > 0)
|
|
321
|
-
return pending;
|
|
322
|
-
const remaining = deadline - Date.now();
|
|
323
|
-
if (remaining <= 0)
|
|
324
|
-
return [];
|
|
325
|
-
// Eligibility may change at lease expiry without any storage write.
|
|
326
|
-
const leaseExpiry = Math.min(Infinity, ...inbox.flatMap((membership) => membership.catchLease === undefined ? [] : [Math.min(membership.catchLease.expiresAt, membership.catchLease.heartbeatAt + WATCH_STALE_MS + 1)]));
|
|
327
|
-
const nextExpiry = Math.min(leaseExpiry, ...inbox.map(membership => membership.deliveryDeferralExpiry ?? Infinity));
|
|
328
|
-
const duration = Math.min(remaining, Math.max(1, nextExpiry - Date.now()));
|
|
329
|
-
const cancelled = new AbortController();
|
|
330
|
-
const waitSignal = AbortSignal.any([signal, cancelled.signal]);
|
|
331
|
-
const waits = sources.map(async (source) => {
|
|
332
|
-
try {
|
|
333
|
-
return await source.wait(duration, waitSignal) ? source.kind : 'deadline';
|
|
334
|
-
}
|
|
335
|
-
catch (error) {
|
|
336
|
-
if (waitSignal.aborted)
|
|
337
|
-
throw error;
|
|
338
|
-
return source.kind;
|
|
339
|
-
}
|
|
340
|
-
});
|
|
341
|
-
if (!armed) {
|
|
342
|
-
armed = true;
|
|
343
|
-
options.onChangeArmed?.(true);
|
|
344
|
-
}
|
|
345
|
-
try {
|
|
346
|
-
const change = await Promise.race(waits);
|
|
347
|
-
if (change !== 'delivery')
|
|
348
|
-
canReturn = true;
|
|
349
|
-
}
|
|
350
|
-
finally {
|
|
351
|
-
cancelled.abort();
|
|
352
|
-
await Promise.allSettled(waits);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
return [];
|
|
18
|
+
rows.push(...(await artifact.read(facts => facts.seatsForOwner(input.owner)
|
|
19
|
+
.filter(seat => facts.participant(seat.participant)?.joined)
|
|
20
|
+
.map(seat => ({ name: seat.participant, squarePath: location,
|
|
21
|
+
pending: facts.directedCount(seat.participant, { unseenOnly: true, currentGeneration: true }) })), signal)).value);
|
|
356
22
|
}
|
|
357
23
|
catch (error) {
|
|
358
|
-
if (signal
|
|
359
|
-
|
|
360
|
-
|
|
24
|
+
if (signal?.aborted)
|
|
25
|
+
throw signal.reason ?? error;
|
|
26
|
+
// Discovery candidates may disappear or become unreadable before this read.
|
|
361
27
|
}
|
|
362
28
|
finally {
|
|
363
|
-
|
|
364
|
-
clearTimeout(timer);
|
|
365
|
-
if (!armed)
|
|
366
|
-
options.onChangeArmed?.(false);
|
|
29
|
+
await artifact.close();
|
|
367
30
|
}
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
lifetime.abort();
|
|
373
|
-
index.close();
|
|
374
|
-
for (const entry of artifacts.values()) {
|
|
375
|
-
entry.observer.close();
|
|
376
|
-
entry.delivery.close();
|
|
377
|
-
}
|
|
378
|
-
artifacts.clear();
|
|
379
|
-
cache.clear();
|
|
380
|
-
},
|
|
381
|
-
};
|
|
31
|
+
}
|
|
32
|
+
after = page.next;
|
|
33
|
+
} while (after !== undefined);
|
|
34
|
+
return rows;
|
|
382
35
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export { Square, squareLocation, squareClock, createSquareResourceOwner, discover, inbox, diagnoseSquare } from './square-api.js';
|
|
2
2
|
export { SquareError, ACTIVITY_METADATA_MAX_BYTES } from './model.js';
|
|
3
|
-
export { selectOwnSeat, requireOwner, localUserOwner, harnessOwner, decodeHarnessOwner } from './square-api.js';
|
|
4
|
-
export
|
|
5
|
-
export
|
|
3
|
+
export { selectOwnSeat, requireOwner, requireJoinName, localUserOwner, harnessOwner, decodeHarnessOwner } from './square-api.js';
|
|
4
|
+
export { postAndDispatch, dispatchEligibleCards } from './notifications.js';
|
|
5
|
+
export { DeliveryLedger, deliveryLedgerLocation } from './delivery-ledger.js';
|
|
6
|
+
export type * from './delivery-ledger.js';
|
|
7
|
+
export { isCardPresentation } from './card-presentation.js';
|
|
8
|
+
export type { CardPresentation } from './card-presentation.js';
|
|
6
9
|
export type * from './square-facade.js';
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
export { Square, squareLocation, squareClock, createSquareResourceOwner, discover, inbox, diagnoseSquare } from './square-api.js';
|
|
2
2
|
export { SquareError, ACTIVITY_METADATA_MAX_BYTES } from './model.js';
|
|
3
|
-
export { selectOwnSeat, requireOwner, localUserOwner, harnessOwner, decodeHarnessOwner } from './square-api.js';
|
|
3
|
+
export { selectOwnSeat, requireOwner, requireJoinName, localUserOwner, harnessOwner, decodeHarnessOwner } from './square-api.js';
|
|
4
|
+
export { postAndDispatch, dispatchEligibleCards } from './notifications.js';
|
|
5
|
+
export { DeliveryLedger, deliveryLedgerLocation } from './delivery-ledger.js';
|
|
6
|
+
export { isCardPresentation } from './card-presentation.js';
|
package/dist/mcp.js
CHANGED
|
@@ -51,7 +51,7 @@ export function createSquareMcpServer(options) {
|
|
|
51
51
|
? wakeAddress : undefined;
|
|
52
52
|
return {
|
|
53
53
|
identity: { owner, wakeAddress: claude === undefined && typeof threadId === 'string' && threadId.trim()
|
|
54
|
-
? codexWakeAddress(threadId, env) : callWake },
|
|
54
|
+
? { ...codexWakeAddress(threadId, env), kind: 'codex-queue' } : callWake },
|
|
55
55
|
open: (location, control) => Square.at({
|
|
56
56
|
...resourceOptions, env, cwd: claude?.cwd ?? resourceOptions.cwd,
|
|
57
57
|
path: location ?? options.path,
|
package/dist/model.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import type { Act, ActivityId } from './square-core.js';
|
|
2
|
-
import type { EndpointGeneration } from './message.js';
|
|
3
2
|
import type { Owner } from './neutral.js';
|
|
4
|
-
import type {
|
|
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 './
|
|
6
|
-
export type { WakeRouteKind, JsonValue, MessageMetadata as ActivityMetadata, WakeAddress, WakeRoute } from './
|
|
3
|
+
import type { WakeAddress } from './square-values.js';
|
|
4
|
+
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 './square-values.js';
|
|
5
|
+
export type { WakeRouteKind, JsonValue, MessageMetadata as ActivityMetadata, WakeAddress, WakeRoute } from './square-values.js';
|
|
7
6
|
export { SquareErrorBase, SquareError, InternalSquareError, isSquareError, validateOwner, nameKey } from './neutral.js';
|
|
8
7
|
export type { Owner, SquareErrorCode, InternalSquareErrorCode, SquareErrorFacts } from './neutral.js';
|
|
9
8
|
export type { Act, ActivityId, Audience, Reach } from './square-core.js';
|
|
@@ -11,7 +10,7 @@ export type { Act, ActivityId, Audience, Reach } from './square-core.js';
|
|
|
11
10
|
export interface Seat {
|
|
12
11
|
readonly participant: string;
|
|
13
12
|
readonly owner: Owner;
|
|
14
|
-
readonly generation:
|
|
13
|
+
readonly generation: string;
|
|
15
14
|
readonly attachment: 'attached' | 'detached';
|
|
16
15
|
readonly wakeAddress?: WakeAddress;
|
|
17
16
|
}
|
|
@@ -30,26 +29,12 @@ export interface BuildOptions {
|
|
|
30
29
|
export type ActDraft = Act & {
|
|
31
30
|
at: number;
|
|
32
31
|
};
|
|
33
|
-
/**
|
|
34
|
-
export type
|
|
35
|
-
kind: 'say';
|
|
36
|
-
}>, 'body' | 'metadata'> & {
|
|
32
|
+
/** A landed activity owns its content and social facts. */
|
|
33
|
+
export type StoredAct = Act & {
|
|
37
34
|
index: number;
|
|
38
35
|
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;
|
|
52
36
|
};
|
|
37
|
+
export type HydratedAct = StoredAct;
|
|
53
38
|
export type ObservationState = 'seen';
|
|
54
39
|
export interface ActivityObservation {
|
|
55
40
|
state: ObservationState;
|
|
@@ -66,31 +51,6 @@ export interface WatchLease {
|
|
|
66
51
|
filter?: WatchLeaseFilter;
|
|
67
52
|
}
|
|
68
53
|
export type DirectedNotificationRoute = import('./attention.js').AudienceRoute;
|
|
69
|
-
export interface InboxNotification {
|
|
70
|
-
/** Lower message coordinate. */
|
|
71
|
-
messageIndex: number;
|
|
72
|
-
/** Upper Agora activity sharing this message, when one exists. */
|
|
73
|
-
actIndex?: number;
|
|
74
|
-
actor: string;
|
|
75
|
-
at: number;
|
|
76
|
-
route?: DirectedNotificationRoute;
|
|
77
|
-
body: string;
|
|
78
|
-
readonly preview?: string;
|
|
79
|
-
readonly description?: string;
|
|
80
|
-
readonly coverage?: 'full' | 'clipped';
|
|
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;
|
|
84
|
-
export interface LocatedSeat {
|
|
85
|
-
readonly location: string;
|
|
86
|
-
readonly seat: Seat;
|
|
87
|
-
readonly joinedAt: number;
|
|
88
|
-
}
|
|
89
|
-
export interface PendingMembership extends LocatedSeat {
|
|
90
|
-
readonly notifications: InboxNotification[];
|
|
91
|
-
readonly catchLease?: WatchLease;
|
|
92
|
-
readonly deliveryDeferralExpiry?: number;
|
|
93
|
-
}
|
|
94
54
|
export interface WatchOptions {
|
|
95
55
|
participants?: string[];
|
|
96
56
|
mention?: string;
|
package/dist/model.js
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
// Shared model and constants for Square.
|
|
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 './
|
|
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 './square-values.js';
|
|
3
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)' };
|
|
9
|
-
}
|
|
10
4
|
export function formatHardCap(hardCap) {
|
|
11
5
|
return hardCap === null ? '-1' : String(hardCap);
|
|
12
6
|
}
|
package/dist/notifications.d.ts
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
export declare function
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
import type { DeliveryLedger, DeliveryTransport, HandoffResult, PostResult } from './delivery-ledger.js';
|
|
2
|
+
import type { Owner } from './owner.js';
|
|
3
|
+
export { createDefaultDeliveryTransports } from './wake-transports.js';
|
|
4
|
+
/** Best-effort dispatch for currently eligible cards, including cards restored by release. */
|
|
5
|
+
export declare function dispatchEligibleCards(ledger: DeliveryLedger, owner: Owner, options?: {
|
|
6
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
7
|
+
readonly signal?: AbortSignal;
|
|
8
|
+
readonly track?: (result: HandoffResult) => void;
|
|
9
|
+
readonly transports?: readonly DeliveryTransport[];
|
|
10
|
+
}): Promise<void>;
|
|
11
|
+
/** Post, then best-effort dispatch without consulting producer storage. */
|
|
12
|
+
export declare function postAndDispatch(ledger: DeliveryLedger, input: {
|
|
13
|
+
readonly owner: Owner;
|
|
14
|
+
readonly key: string;
|
|
15
|
+
readonly text: string;
|
|
16
|
+
readonly details?: Readonly<Record<string, unknown>>;
|
|
17
|
+
}, options?: {
|
|
18
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
19
|
+
readonly signal?: AbortSignal;
|
|
20
|
+
readonly track?: (result: HandoffResult) => void;
|
|
21
|
+
readonly transports?: readonly DeliveryTransport[];
|
|
22
|
+
}): Promise<PostResult>;
|
|
23
|
+
/** Best-effort dispatch after a card has been posted; never inspect producer storage. */
|
|
24
|
+
export declare function dispatchPostedCard(ledger: DeliveryLedger, owner: Owner, key: string, track: (result: HandoffResult) => void, env?: NodeJS.ProcessEnv, signal?: AbortSignal, suppliedTransports?: readonly DeliveryTransport[]): Promise<void>;
|