@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
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
import { squareWakeProjection } from '../../wake-preview.js';
|
|
3
|
-
import { channelArtifact, createArtifactPort } from '../../artifact.js';
|
|
4
|
-
import { deliverPending } from '../../delivery-operations.js';
|
|
5
|
-
import { nativePendingSource, observeSessionPending, projectSessionSeats } from '../../inbox.js';
|
|
6
|
-
import { automaticWakeEligible, nativePendingHead, presentationBlocksWake } from '../../delivery.js';
|
|
7
|
-
import { harnessOwner, decodeHarnessOwner } from '../../owner.js';
|
|
8
|
-
import { formatMessageId, parseMessageId } from '../../message.js';
|
|
9
|
-
import { indexedSquareEntries } from '../../square-index.js';
|
|
10
|
-
import { asSeatRef } from '../../seats.js';
|
|
11
|
-
import { restoreSessionSeats } from '../../session-lifecycle.js';
|
|
12
|
-
import { presentPending } from '../../presentation-operations.js';
|
|
13
|
-
import { createNativeInputId, sendNativeText } from '../../packages/agent-delivery/src/opencode-native.js';
|
|
14
|
-
const routeKind = 'opencode-server';
|
|
15
|
-
const ownerFor = (sessionId) => harnessOwner({ channel: 'opencode', session: sessionId });
|
|
16
|
-
const payloadHash = (payload) => createHash('sha256').update(payload).digest('hex');
|
|
17
|
-
export async function indexedOpenCodeSessions(env) {
|
|
18
|
-
return [...new Set((await indexedSquareEntries({}, env)).flatMap((entry) => {
|
|
19
|
-
const coordinate = decodeHarnessOwner(entry.owner);
|
|
20
|
-
return coordinate?.channel === 'opencode' ? [coordinate.session] : [];
|
|
21
|
-
}))];
|
|
22
|
-
}
|
|
23
|
-
export async function discoverOpenCodeSeats(sessionId, env, signal) {
|
|
24
|
-
return projectSessionSeats({ owner: ownerFor(sessionId), env }, signal);
|
|
25
|
-
}
|
|
26
|
-
function sameSeat(left, right) {
|
|
27
|
-
return left !== undefined && left.attachment === 'attached' && left.generation === right.generation && left.owner === right.owner;
|
|
28
|
-
}
|
|
29
|
-
function createOpenCodeWakeTransport(target, lifetime) {
|
|
30
|
-
return {
|
|
31
|
-
probe: async (route) => !lifetime.aborted && route.kind === routeKind
|
|
32
|
-
&& route.address.sessionId === target.sessionId,
|
|
33
|
-
async attempt(request, timeoutMs, beforeSend) {
|
|
34
|
-
const presentation = request.presentation;
|
|
35
|
-
if (request.claimToken === undefined || parseMessageId(request.message) === undefined || presentation === undefined
|
|
36
|
-
|| request.route.kind !== routeKind || request.route.address.sessionId !== target.sessionId) {
|
|
37
|
-
return { outcome: 'not-capable', diagnostic: 'OpenCode attention has no matching artifact claim or session.' };
|
|
38
|
-
}
|
|
39
|
-
const signal = AbortSignal.any([lifetime, AbortSignal.timeout(timeoutMs)]);
|
|
40
|
-
const started = Date.now();
|
|
41
|
-
if (signal.aborted || !await beforeSend())
|
|
42
|
-
return { outcome: 'not-capable', diagnostic: 'OpenCode attention changed before send.' };
|
|
43
|
-
const inputId = createNativeInputId();
|
|
44
|
-
const correlation = { harness: 'opencode', token: inputId, payloadHash: payloadHash(presentation.payload) };
|
|
45
|
-
if (!await request.channel.prepare({ correlation,
|
|
46
|
-
coverage: presentation.coverage, ...(presentation.description === undefined ? {} : { description: presentation.description }) }, signal))
|
|
47
|
-
return { outcome: 'not-capable', diagnostic: 'OpenCode delivery claim is no longer current.' };
|
|
48
|
-
if (signal.aborted || !await beforeSend())
|
|
49
|
-
return { outcome: 'not-capable', diagnostic: 'OpenCode attention was consumed before send.' };
|
|
50
|
-
const result = await sendNativeText(target, presentation.payload, {
|
|
51
|
-
inputId, delivery: 'steer', timeoutMs: Math.max(1, timeoutMs - (Date.now() - started)), signal,
|
|
52
|
-
});
|
|
53
|
-
if (result.state === 'accepted')
|
|
54
|
-
return { outcome: 'accepted' };
|
|
55
|
-
if (result.state === 'unavailable')
|
|
56
|
-
return { outcome: 'failed', signature: result.code };
|
|
57
|
-
return { outcome: 'unknown', signature: result.code };
|
|
58
|
-
},
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
function textMessage(message) {
|
|
62
|
-
if (message.role !== 'user' || message.id === undefined || !message.content.every((part) => part.type === 'text' && typeof part.text === 'string'))
|
|
63
|
-
return undefined;
|
|
64
|
-
return message.content.map((part) => part.text).join('');
|
|
65
|
-
}
|
|
66
|
-
/** The exact prepared native message can authorize artifact-backed presentation once. */
|
|
67
|
-
export async function observeOpenCodeContext(sessionId, messages, env, signal) {
|
|
68
|
-
const observed = new Map(messages.flatMap((message) => {
|
|
69
|
-
const text = textMessage(message);
|
|
70
|
-
return text === undefined ? [] : [[message.id, payloadHash(text)]];
|
|
71
|
-
}));
|
|
72
|
-
if (observed.size === 0 || signal.aborted)
|
|
73
|
-
return;
|
|
74
|
-
for (const { location, seat } of await discoverOpenCodeSeats(sessionId, env, signal)) {
|
|
75
|
-
if (signal.aborted)
|
|
76
|
-
return;
|
|
77
|
-
const artifact = createArtifactPort(location, undefined, signal);
|
|
78
|
-
try {
|
|
79
|
-
const { value: matches } = await artifact.read((facts) => {
|
|
80
|
-
if (!sameSeat(facts.seat(seat.participant), seat))
|
|
81
|
-
return [];
|
|
82
|
-
return facts.deliveryEvidence(seat.participant, seat.generation).flatMap((evidence) => {
|
|
83
|
-
const token = evidence.correlation?.harness === 'opencode' ? evidence.correlation.token : undefined;
|
|
84
|
-
const hash = token === undefined ? undefined : observed.get(token);
|
|
85
|
-
const index = parseMessageId(evidence.message), now = Date.now();
|
|
86
|
-
if (token === undefined || hash === undefined || evidence.claimToken === undefined || index === undefined
|
|
87
|
-
|| evidence.route === undefined || !['dispatching', 'unknown', 'accepted'].includes(evidence.outcome)
|
|
88
|
-
|| evidence.correlation?.payloadHash !== hash
|
|
89
|
-
|| evidence.route.kind !== routeKind
|
|
90
|
-
|| presentationBlocksWake(facts.deliveryEvidence(seat.participant, seat.generation, evidence.message), now))
|
|
91
|
-
return [];
|
|
92
|
-
const source = nativePendingSource(facts, { location, participant: seat.participant, generation: seat.generation,
|
|
93
|
-
owner: seat.owner, index, route: evidence.route, now });
|
|
94
|
-
if (source === undefined)
|
|
95
|
-
return [];
|
|
96
|
-
return [{ evidence, index, hash, clipped: evidence.coverage === 'clipped' }];
|
|
97
|
-
});
|
|
98
|
-
}, signal);
|
|
99
|
-
for (const { evidence, index, hash, clipped } of matches) {
|
|
100
|
-
if (signal.aborted || evidence.claimToken === undefined)
|
|
101
|
-
return;
|
|
102
|
-
await presentPending({ artifact, location, participant: seat.participant,
|
|
103
|
-
message: formatMessageId(index), seat: asSeatRef(seat), observationOf: evidence.claimToken, sink: { present() { } }, markSeen: !clipped, signal,
|
|
104
|
-
current: (facts) => {
|
|
105
|
-
const correlation = facts.deliveryByToken(evidence.claimToken);
|
|
106
|
-
return nativePendingHead(facts, { location, participant: seat.participant, generation: seat.generation,
|
|
107
|
-
owner: seat.owner, index, route: evidence.route, now: Date.now() }) !== undefined
|
|
108
|
-
&& ['dispatching', 'unknown', 'accepted'].includes(correlation?.outcome ?? '')
|
|
109
|
-
&& correlation?.correlation?.harness === 'opencode'
|
|
110
|
-
&& correlation.correlation.token !== undefined
|
|
111
|
-
&& correlation.correlation.payloadHash === hash;
|
|
112
|
-
},
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
finally {
|
|
117
|
-
await artifact.close();
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
/** The native receiver owns no claims outside the artifact; HOME is used only for discovery. */
|
|
122
|
-
export async function receiveOpenCodePending(target, env, signal) {
|
|
123
|
-
const observer = await observeSessionPending(ownerFor(target.sessionId), env);
|
|
124
|
-
const transport = createOpenCodeWakeTransport(target, signal);
|
|
125
|
-
let waiting;
|
|
126
|
-
try {
|
|
127
|
-
while (!signal.aborted) {
|
|
128
|
-
const armed = new Promise((resolve) => {
|
|
129
|
-
waiting = observer.wait(Infinity, { signal, skipImmediate: true, onChangeArmed: resolve });
|
|
130
|
-
void waiting.catch(() => resolve(false));
|
|
131
|
-
});
|
|
132
|
-
if (!await armed) {
|
|
133
|
-
await waiting;
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
|
-
for (const membership of await observer.read({ signal })) {
|
|
137
|
-
const { location, seat } = membership;
|
|
138
|
-
if (signal.aborted)
|
|
139
|
-
return;
|
|
140
|
-
const artifact = createArtifactPort(location, undefined, signal);
|
|
141
|
-
try {
|
|
142
|
-
const { value: notifications } = await artifact.read((facts) => {
|
|
143
|
-
if (!sameSeat(facts.seat(seat.participant), seat))
|
|
144
|
-
return [];
|
|
145
|
-
return membership.notifications.filter((note) => automaticWakeEligible(facts.deliveryEvidence(seat.participant, seat.generation, formatMessageId(note.messageIndex)), Date.now()));
|
|
146
|
-
}, signal);
|
|
147
|
-
for (const note of notifications) {
|
|
148
|
-
if (signal.aborted)
|
|
149
|
-
return;
|
|
150
|
-
await deliverPending({ artifact: channelArtifact(artifact), preparePresentation: squareWakeProjection(artifact), transport, location, message: formatMessageId(note.messageIndex), now: Date.now() });
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
catch (error) {
|
|
154
|
-
if (signal.aborted)
|
|
155
|
-
throw signal.reason ?? error;
|
|
156
|
-
// A candidate that became unreadable cannot authorize sending; the shared observer
|
|
157
|
-
// keeps its subscription and will reconsider the artifact after repair.
|
|
158
|
-
}
|
|
159
|
-
finally {
|
|
160
|
-
await artifact.close();
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
await waiting;
|
|
164
|
-
waiting = undefined;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
catch (error) {
|
|
168
|
-
if (!signal.aborted)
|
|
169
|
-
throw error;
|
|
170
|
-
}
|
|
171
|
-
finally {
|
|
172
|
-
observer.close();
|
|
173
|
-
await waiting?.catch(() => { });
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
/** Publish the native address into current artifact seats; the legacy route projection is not read. */
|
|
177
|
-
export async function publishOpenCodeAddress(sessionId, env, signal) {
|
|
178
|
-
await restoreSessionSeats(ownerFor(sessionId), env, { kind: routeKind, address: { sessionId } }, signal);
|
|
179
|
-
const seats = (await discoverOpenCodeSeats(sessionId, env, signal)).filter(({ seat }) => seat.attachment === 'attached');
|
|
180
|
-
for (const { location, seat } of seats) {
|
|
181
|
-
if (signal.aborted)
|
|
182
|
-
return false;
|
|
183
|
-
const artifact = createArtifactPort(location, undefined, signal);
|
|
184
|
-
try {
|
|
185
|
-
await artifact.transactAs({ kind: 'participant', seat: asSeatRef(seat) }, (facts) => facts.updateWakeAddress(asSeatRef(seat), { kind: routeKind, address: { sessionId } }));
|
|
186
|
-
}
|
|
187
|
-
finally {
|
|
188
|
-
await artifact.close();
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
return seats.length > 0;
|
|
192
|
-
}
|
|
193
|
-
export async function cancelOpenCodeDeliveries(sessionId, env, signal) {
|
|
194
|
-
for (const { location, seat } of await discoverOpenCodeSeats(sessionId, env, signal)) {
|
|
195
|
-
if (signal.aborted)
|
|
196
|
-
return;
|
|
197
|
-
if (seat.attachment !== 'attached')
|
|
198
|
-
continue;
|
|
199
|
-
const artifact = createArtifactPort(location, undefined, signal);
|
|
200
|
-
try {
|
|
201
|
-
const { value: through } = await artifact.read((facts) => facts.channel.header().lastMessageIndex, signal);
|
|
202
|
-
if (through >= 0)
|
|
203
|
-
await artifact.cancelDeliveries(asSeatRef(seat), through, signal);
|
|
204
|
-
}
|
|
205
|
-
finally {
|
|
206
|
-
await artifact.close();
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
export async function endOpenCodeSession(sessionId, env, signal) {
|
|
211
|
-
for (const { location, seat } of await discoverOpenCodeSeats(sessionId, env, signal)) {
|
|
212
|
-
if (signal.aborted)
|
|
213
|
-
return;
|
|
214
|
-
const { Square } = await import('../../square-api.js');
|
|
215
|
-
const square = await Square.at({ path: location, env });
|
|
216
|
-
try {
|
|
217
|
-
await square.endHeld(seat.participant, { owner: seat.owner, generation: seat.generation });
|
|
218
|
-
}
|
|
219
|
-
finally {
|
|
220
|
-
await square.close();
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { PresentationResult, PresentPendingInput } from './ports.js';
|
|
2
|
-
/** Prepare custody before any external effect. A batch prepares every included item first. */
|
|
3
|
-
export declare function preparePendingPresentation(input: Omit<PresentPendingInput, 'sink'>): Promise<import("./delivery-effects.js").PreparedPresentation<import("./channel-ports.js").MessageFact> | undefined>;
|
|
4
|
-
/** A sink error cannot prove that content was never injected. */
|
|
5
|
-
export declare function presentPending(input: PresentPendingInput): Promise<PresentationResult>;
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
|
-
import { formatMessageId, parseMessageId } from './message.js';
|
|
3
|
-
import { isSquareError } from './model.js';
|
|
4
|
-
import { pendingHead, sameJoin } from './delivery.js';
|
|
5
|
-
import { deliveryDeferralExpiry } from './delivery-deferral.js';
|
|
6
|
-
import { asSeatRef } from './seats.js';
|
|
7
|
-
import { preparePresentation, presentPrepared } from './delivery-effects.js';
|
|
8
|
-
/** Prepare custody before any external effect. A batch prepares every included item first. */
|
|
9
|
-
export async function preparePendingPresentation(input) {
|
|
10
|
-
const named = input.message;
|
|
11
|
-
const index = typeof named === 'number' ? named : parseMessageId(named);
|
|
12
|
-
if (index === undefined)
|
|
13
|
-
return undefined;
|
|
14
|
-
const initial = await input.artifact.read((facts) => {
|
|
15
|
-
const seat = facts.channel.endpoint(input.participant);
|
|
16
|
-
if (seat === undefined || seat.attachment !== 'attached' || input.generation !== undefined && input.generation !== seat.generation
|
|
17
|
-
|| input.seat !== undefined && (input.seat.generation !== seat.generation || input.seat.owner !== seat.owner)
|
|
18
|
-
|| input.current?.(facts) === false || !pendingHead(facts, input.participant, index)
|
|
19
|
-
|| deliveryDeferralExpiry(facts.channel, seat, index, input.now ?? Date.now()) !== undefined)
|
|
20
|
-
return undefined;
|
|
21
|
-
const item = facts.channel.message(index);
|
|
22
|
-
return item === undefined ? undefined : { participant: { name: input.participant, generation: seat.generation }, seat, item };
|
|
23
|
-
}, input.signal).catch(() => undefined);
|
|
24
|
-
if (initial?.value === undefined)
|
|
25
|
-
return undefined;
|
|
26
|
-
const { participant, seat, item } = initial.value;
|
|
27
|
-
const ref = asSeatRef(seat);
|
|
28
|
-
const owns = (facts) => {
|
|
29
|
-
const current = facts.seat(seat.participant);
|
|
30
|
-
return current !== undefined && current.attachment === 'attached' && current.owner === seat.owner && current.generation === seat.generation
|
|
31
|
-
&& sameJoin(facts, participant);
|
|
32
|
-
};
|
|
33
|
-
const claimToken = randomUUID();
|
|
34
|
-
return preparePresentation({
|
|
35
|
-
payload: item, coverage: input.markSeen === false ? 'clipped' : 'full',
|
|
36
|
-
port: {
|
|
37
|
-
async claim(signal) {
|
|
38
|
-
const claimed = await input.artifact.claimDelivery({ seat: ref, message: formatMessageId(index), kind: 'presentation',
|
|
39
|
-
observationOf: input.observationOf, at: input.now ?? Date.now(), leaseMs: input.timeoutMs ?? 5000, claimToken }, signal).catch(() => ({ status: 'stale' }));
|
|
40
|
-
return claimed.status === 'acquired' ? { token: claimToken, ordinal: claimed.attemptN } : undefined;
|
|
41
|
-
},
|
|
42
|
-
async current(signal) {
|
|
43
|
-
return (await input.artifact.read(facts => owns(facts) && input.current?.(facts) !== false
|
|
44
|
-
&& pendingHead(facts, participant.name, index) !== undefined
|
|
45
|
-
&& deliveryDeferralExpiry(facts.channel, seat, index, input.now ?? Date.now()) === undefined, signal ?? input.signal)).value;
|
|
46
|
-
},
|
|
47
|
-
async settle(claim, completion) {
|
|
48
|
-
const success = completion.outcome === 'presented' || completion.outcome === 'clipped';
|
|
49
|
-
const { value } = await input.artifact.settleAs({ kind: 'participant', seat: ref }, facts => {
|
|
50
|
-
// Captured ownership fences truth; post-effect policy/cancellation cannot veto it.
|
|
51
|
-
if (!owns(facts))
|
|
52
|
-
return false;
|
|
53
|
-
if (!facts.channel.completeDelivery({ seat: ref, message: formatMessageId(index), claimToken: claim.token,
|
|
54
|
-
outcome: completion.outcome, at: success ? input.now ?? Date.now() : Date.now(),
|
|
55
|
-
...(completion.reason === 'threw' ? { messageText: completion.error instanceof Error ? completion.error.message : String(completion.error) } : {}),
|
|
56
|
-
}))
|
|
57
|
-
return false;
|
|
58
|
-
if (completion.outcome === 'presented')
|
|
59
|
-
facts.channel.markSeen(participant.name, [index], input.now ?? Date.now());
|
|
60
|
-
return true;
|
|
61
|
-
}).catch(error => {
|
|
62
|
-
if (!success || isSquareError(error) && error.code === 'already_joined')
|
|
63
|
-
return { value: false };
|
|
64
|
-
throw error;
|
|
65
|
-
});
|
|
66
|
-
return value;
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
}, { signal: input.signal });
|
|
70
|
-
}
|
|
71
|
-
/** A sink error cannot prove that content was never injected. */
|
|
72
|
-
export async function presentPending(input) {
|
|
73
|
-
const prepared = await preparePendingPresentation(input);
|
|
74
|
-
if (prepared === undefined)
|
|
75
|
-
return { presented: false };
|
|
76
|
-
const report = await presentPrepared(prepared, message => input.sink.present(message), {
|
|
77
|
-
signal: input.signal,
|
|
78
|
-
});
|
|
79
|
-
if (report.observation.kind === 'threw')
|
|
80
|
-
throw report.observation.error;
|
|
81
|
-
const result = report.settlements[0].result;
|
|
82
|
-
if (result.persistence.status === 'error')
|
|
83
|
-
throw result.persistence.error;
|
|
84
|
-
return report.observation.kind === 'returned' && result.persistence.status === 'recorded'
|
|
85
|
-
? { presented: true, message: prepared.payload } : { presented: false };
|
|
86
|
-
}
|
package/dist/sqlite-channel.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { DatabaseSync } from 'node:sqlite';
|
|
2
|
-
import type { ChannelWrite, MessageAddressFact, EndpointFact, ReceiveClaimFact, ChannelDeliveryEvidence } from './channel-ports.js';
|
|
3
|
-
import { type DeliveryDeferralFact } from './delivery-deferral.js';
|
|
4
|
-
import { type EndpointGeneration } from './message.js';
|
|
5
|
-
import { type Row } from './sqlite-values.js';
|
|
6
|
-
export declare function messageAddress(row: Row): MessageAddressFact;
|
|
7
|
-
export declare function endpointGeneration(value: unknown): EndpointGeneration;
|
|
8
|
-
export declare function endpoint(row: Row): EndpointFact;
|
|
9
|
-
export declare function deliveryEvidence(row: Row, payload?: Record<string, unknown>): ChannelDeliveryEvidence;
|
|
10
|
-
export declare function deliveryDeferral(row: Row): DeliveryDeferralFact;
|
|
11
|
-
export declare function receiveClaim(row: Row): ReceiveClaimFact;
|
|
12
|
-
export declare function createChannelSqlite(db: DatabaseSync): ChannelWrite & {
|
|
13
|
-
readonly changed: boolean;
|
|
14
|
-
readonly deliveryChanged: boolean;
|
|
15
|
-
};
|