@astrosheep/square 0.4.0 → 0.4.2
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-boundary-state.d.ts +20 -0
- package/dist/claude-boundary-state.js +84 -0
- package/dist/claude-hook.d.ts +2 -0
- package/dist/claude-hook.js +231 -0
- package/dist/claude-mcp-context.d.ts +1 -1
- package/dist/claude-mcp-context.js +12 -3
- package/dist/cli/context.js +7 -9
- package/dist/cli/observation-commands.d.ts +4 -1
- package/dist/cli/observation-commands.js +37 -4
- package/dist/cli/registry.js +5 -2
- 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-claude.js +3 -4
- package/dist/harness-links.js +63 -7
- package/dist/harness.js +12 -17
- package/dist/help.js +4 -1
- 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 +2 -3
- 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 +7 -39
- package/examples/symmetric-domains.mjs +29 -249
- package/guides/change-waits.md +13 -53
- package/guides/claude-native.md +7 -171
- package/guides/codex-delivery.md +55 -80
- package/guides/delivery-primitives.md +48 -207
- package/guides/mcp.md +1 -1
- 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 +2 -3
- package/plugins/claude/hooks/hooks.json +11 -1
- 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/claude-delivery.d.ts +0 -12
- package/dist/claude-delivery.js +0 -108
- package/dist/claude-mod.d.ts +0 -8
- package/dist/claude-mod.js +0 -172
- package/dist/claude-transport.d.ts +0 -5
- package/dist/claude-transport.js +0 -58
- 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/claude/hooks/register.js +0 -132
- package/plugins/claude/types.d.ts +0 -7
- 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/codex-queue.js
CHANGED
|
@@ -1,46 +1,46 @@
|
|
|
1
1
|
import { connect, DeliveryError } from './packages/agent-delivery/src/index.js';
|
|
2
|
-
|
|
2
|
+
import { codexIdleAtStop } from './packages/agent-delivery/src/codex.js';
|
|
3
|
+
import { codexQueueEndpoint } from './packages/agent-delivery/src/codex-endpoint.js';
|
|
3
4
|
import { codexBoundaryStore } from './codex-boundary-state.js';
|
|
5
|
+
import { renderDeliveryReminder } from './delivery-frame.js';
|
|
6
|
+
/** Instantiated only by the receiver, for one captured Stop epoch. */
|
|
4
7
|
export class CodexQueueTransport {
|
|
5
8
|
opts;
|
|
6
|
-
|
|
9
|
+
kind = 'codex-queue';
|
|
10
|
+
constructor(opts) {
|
|
7
11
|
this.opts = opts;
|
|
8
12
|
}
|
|
9
|
-
async
|
|
10
|
-
const deadline = Date.now() + timeoutMs;
|
|
11
|
-
const { address } = request.route;
|
|
13
|
+
async send(card, address, context) {
|
|
12
14
|
const threadId = address.threadId?.trim();
|
|
13
|
-
if (!threadId)
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
if (!threadId || !address.codexHome || Object.keys(address).length !== Object.keys(this.opts.address).length ||
|
|
16
|
+
!Object.entries(this.opts.address).every(([key, value]) => address[key] === value))
|
|
17
|
+
return 'not-capable';
|
|
16
18
|
const env = this.opts.env ?? process.env;
|
|
17
|
-
const
|
|
18
|
-
if (payload === undefined)
|
|
19
|
-
return { outcome: 'not-capable', diagnostic: 'No pending Square activity to preview.' };
|
|
20
|
-
if (!(await beforeSend()))
|
|
21
|
-
return { outcome: 'not-capable' };
|
|
19
|
+
const boundaries = codexBoundaryStore(env), epoch = this.opts.epoch;
|
|
22
20
|
try {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
21
|
+
// Receiver delivery addresses the already-hosting endpoint explicitly;
|
|
22
|
+
// disappearing native custody must not trigger CLI service startup.
|
|
23
|
+
const endpoint = address.endpoint ?? codexQueueEndpoint({ CODEX_HOME: address.codexHome });
|
|
24
|
+
const agent = await connect({ harness: 'codex', transport: 'cli', sessionId: threadId,
|
|
25
|
+
boundaries: { queueEligible: async (id) => {
|
|
26
|
+
if (!await boundaries.queueEligible(id, epoch))
|
|
27
|
+
return false;
|
|
28
|
+
// Repeat the native sample after claim, immediately before the final
|
|
29
|
+
// epoch and ledger fences. These reads are not an atomic native enqueue.
|
|
30
|
+
if (!await codexIdleAtStop({ harness: 'codex', sessionId: id, codexHome: address.codexHome,
|
|
31
|
+
endpoint, timeoutMs: Math.max(1, context.deadline - Date.now()), signal: context.signal }, epoch.turnId))
|
|
32
|
+
return false;
|
|
33
|
+
return await boundaries.queueEligible(id, epoch) && await context.beforeSend()
|
|
34
|
+
&& !context.signal.aborted && Date.now() < context.deadline;
|
|
35
|
+
} },
|
|
36
|
+
env, bin: env.SQUARE_CODEX_BIN, codexHome: address.codexHome, endpoint });
|
|
37
|
+
if (context.signal.aborted || Date.now() >= context.deadline)
|
|
38
|
+
return 'not-capable';
|
|
39
|
+
await agent.queue(renderDeliveryReminder([card.text], { tagged: true }), { timeoutMs: context.deadline - Date.now(), signal: context.signal });
|
|
40
|
+
return 'accepted';
|
|
33
41
|
}
|
|
34
42
|
catch (error) {
|
|
35
|
-
|
|
36
|
-
return { outcome: 'failed', signature: 'boundary_not_stopped',
|
|
37
|
-
message: 'The Codex thread has no current Stop boundary for queueing.' };
|
|
38
|
-
}
|
|
39
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
40
|
-
if (!(error instanceof DeliveryError) || error.maybeDelivered)
|
|
41
|
-
return { outcome: 'unknown', signature: 'queue_unknown', message };
|
|
42
|
-
return { outcome: 'failed', signature: ['unavailable', 'timeout', 'aborted'].includes(error.code)
|
|
43
|
-
? 'queue_pre_accept_transient' : 'queue_pre_accept_rejected', message };
|
|
43
|
+
return error instanceof DeliveryError && !error.maybeDelivered ? 'not-capable' : 'unknown';
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { promises as fs } from 'node:fs';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { DeliveryLedger, deliveryLedgerLocation } from './delivery-ledger.js';
|
|
5
|
+
import { retainDeliveryLedger } from './delivery-lifetime.js';
|
|
6
|
+
import { harnessOwner } from './owner.js';
|
|
7
|
+
import { withFileLock } from './file-lock.js';
|
|
8
|
+
import { canonicalPath } from './canonical-path.js';
|
|
9
|
+
import { codexBoundaryStore } from './codex-boundary-state.js';
|
|
10
|
+
import { codexWakeAddress } from './codex-address.js';
|
|
11
|
+
import { parseCodexHookText } from './codex-hook.js';
|
|
12
|
+
import { CodexQueueTransport } from './codex-queue.js';
|
|
13
|
+
import { codexIdleAtStop } from './packages/agent-delivery/src/codex.js';
|
|
14
|
+
/** One native async hook owns observation; the lock itself conveys no idle authority. */
|
|
15
|
+
export async function runCodexWatch(inputText, env = process.env, signal) {
|
|
16
|
+
const input = parseCodexHookText(inputText);
|
|
17
|
+
if (input?.hook_event_name !== 'Stop' || signal?.aborted)
|
|
18
|
+
return '';
|
|
19
|
+
const controller = new AbortController();
|
|
20
|
+
const stop = () => controller.abort();
|
|
21
|
+
const timer = setTimeout(stop, 86_400_000);
|
|
22
|
+
const combined = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;
|
|
23
|
+
process.on('SIGTERM', stop);
|
|
24
|
+
process.on('SIGINT', stop);
|
|
25
|
+
try {
|
|
26
|
+
const route = codexWakeAddress(input.session_id, env);
|
|
27
|
+
const home = await canonicalPath(route.address.codexHome);
|
|
28
|
+
const coordinate = [input.session_id, route.address.endpoint ?? home];
|
|
29
|
+
const root = path.join(path.dirname(deliveryLedgerLocation(env)), 'codex-watchers');
|
|
30
|
+
await fs.mkdir(root, { recursive: true, mode: 0o700 });
|
|
31
|
+
const lock = path.join(root, createHash('sha256').update(JSON.stringify(coordinate)).digest('hex') + '.lock');
|
|
32
|
+
const file = await fs.open(lock, 'a', 0o600);
|
|
33
|
+
await file.close();
|
|
34
|
+
await withFileLock(lock, { retryMs: 10, skipIfBusy: true, signal: combined }, async () => {
|
|
35
|
+
const ledger = await DeliveryLedger.open({ location: deliveryLedgerLocation(env) });
|
|
36
|
+
const lifetime = retainDeliveryLedger(ledger);
|
|
37
|
+
let observer;
|
|
38
|
+
try {
|
|
39
|
+
const owner = harnessOwner({ channel: 'codex', session: input.session_id });
|
|
40
|
+
const captured = await ledger.session(owner);
|
|
41
|
+
if (captured?.state !== 'attached')
|
|
42
|
+
return;
|
|
43
|
+
const boundaries = codexBoundaryStore(env);
|
|
44
|
+
// Subscribe before inspecting pending work. Both ledger edges and the
|
|
45
|
+
// bounded wait below cause reconciliation; boundary-only changes matter.
|
|
46
|
+
observer = await ledger.observe(owner);
|
|
47
|
+
while (!combined.aborted) {
|
|
48
|
+
let version = await observer.read(combined);
|
|
49
|
+
const session = await ledger.session(owner);
|
|
50
|
+
if (session?.state !== 'attached' || session.generation !== captured.generation)
|
|
51
|
+
return;
|
|
52
|
+
const registered = session.routes.find(candidate => candidate.kind === 'codex-queue');
|
|
53
|
+
if (!registered || registered.address.threadId !== input.session_id ||
|
|
54
|
+
registered.address.endpoint !== route.address.endpoint ||
|
|
55
|
+
!registered.address.codexHome || await canonicalPath(registered.address.codexHome) !== home)
|
|
56
|
+
return;
|
|
57
|
+
const boundary = await boundaries.read(input.session_id);
|
|
58
|
+
if (boundary?.ended)
|
|
59
|
+
return;
|
|
60
|
+
const pending = await ledger.pending(owner, { generation: captured.generation });
|
|
61
|
+
const held = new Set((await ledger.handoffs(owner, { generation: captured.generation }))
|
|
62
|
+
.filter(attempt => !['failed', 'released'].includes(attempt.outcome)).map(attempt => attempt.seq));
|
|
63
|
+
for (const card of pending) {
|
|
64
|
+
if (combined.aborted)
|
|
65
|
+
break;
|
|
66
|
+
if (held.has(card.seq))
|
|
67
|
+
continue;
|
|
68
|
+
// Follow the latest Stop, regardless of which Stop launched this process.
|
|
69
|
+
const epoch = (await boundaries.read(input.session_id))?.stop;
|
|
70
|
+
if (!epoch || !await codexIdleAtStop({ harness: 'codex', sessionId: input.session_id,
|
|
71
|
+
codexHome: registered.address.codexHome, endpoint: registered.address.endpoint,
|
|
72
|
+
timeoutMs: 2000, signal: combined }, epoch.turnId))
|
|
73
|
+
break;
|
|
74
|
+
lifetime.track(await ledger.dispatchHandoff(owner, card.key, new CodexQueueTransport({ epoch, env, address: registered.address }), { generation: captured.generation, timeoutMs: 5000, signal: combined }));
|
|
75
|
+
// Drain our own claim/settlement edge. A known-unsent failure must
|
|
76
|
+
// not turn file observation into an immediate retry loop. Any post
|
|
77
|
+
// racing this read is still found by the bounded rescan below.
|
|
78
|
+
version = await observer.read(combined);
|
|
79
|
+
}
|
|
80
|
+
// No claims are held while waiting, including while active or disarmed.
|
|
81
|
+
await observer.changed(version, 1000, combined);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
observer?.close();
|
|
86
|
+
await lifetime.close();
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
if (!combined.aborted)
|
|
92
|
+
throw new Error('Codex watcher unavailable.');
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
clearTimeout(timer);
|
|
96
|
+
process.off('SIGTERM', stop);
|
|
97
|
+
process.off('SIGINT', stop);
|
|
98
|
+
}
|
|
99
|
+
return '';
|
|
100
|
+
}
|
package/dist/decisions.js
CHANGED
|
@@ -51,7 +51,7 @@ export function unreadActivitySummaries(facts, name, now) {
|
|
|
51
51
|
let afterIndex = readCursor(facts, name);
|
|
52
52
|
const summaries = new Map();
|
|
53
53
|
while (true) {
|
|
54
|
-
const page = facts.directed(name, { afterIndex, kinds: ['say'] }, { edge: 'oldest', limit: FACT_PAGE_SIZE });
|
|
54
|
+
const page = facts.directed(name, { afterIndex, kinds: ['say'], unseenOnly: true }, { edge: 'oldest', limit: FACT_PAGE_SIZE });
|
|
55
55
|
for (const item of page.items) {
|
|
56
56
|
const summary = summaries.get(item.actor) ?? { count: 0, latestAt: -Infinity, lastIndex: item.index };
|
|
57
57
|
summary.count++;
|
|
@@ -194,7 +194,7 @@ export function coreParticipants(facts, now) {
|
|
|
194
194
|
activityCount: current.sayCount, lastActiveAt,
|
|
195
195
|
presence: current.done ? 'done' : !current.joined ? 'never-joined' : facts.seat(current.name)?.attachment === 'detached' ? 'away'
|
|
196
196
|
: freshWatchLease(facts, current.name, now) !== undefined ? 'watching' : 'active',
|
|
197
|
-
unreadActivityCount: current.joined ? facts.directedCount(current.name, { kinds: ['say'], afterIndex: readCursor(facts, current.name) }) : 0,
|
|
197
|
+
unreadActivityCount: current.joined ? facts.directedCount(current.name, { kinds: ['say'], afterIndex: readCursor(facts, current.name), unseenOnly: true }) : 0,
|
|
198
198
|
pendingMentionCount: current.joined ? facts.directedCount(current.name, { kinds: ['say'], afterIndex: current.lastJoinIndex ?? -1, unseenOnly: true }) : 0,
|
|
199
199
|
};
|
|
200
200
|
}).sort(compareParticipantActivity);
|
|
@@ -1,19 +1,10 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
readonly id: string;
|
|
9
|
-
readonly match: MetadataMatch;
|
|
10
|
-
readonly expiresAt: number;
|
|
1
|
+
import type { ActivityMetadata } from './model.js';
|
|
2
|
+
export type MetadataScalar = null | boolean | number | string;
|
|
3
|
+
export interface MetadataMatch {
|
|
4
|
+
readonly fields: readonly Readonly<{
|
|
5
|
+
path: readonly string[];
|
|
6
|
+
equals: MetadataScalar;
|
|
7
|
+
}>[];
|
|
11
8
|
}
|
|
12
|
-
export declare const MAX_DELIVERY_DEFERRAL_MS: number;
|
|
13
9
|
export declare function validateMetadataMatch(value: unknown): MetadataMatch;
|
|
14
|
-
export declare function
|
|
15
|
-
export declare function validateDeferralId(value: unknown): string;
|
|
16
|
-
export declare function matchesMetadata(metadata: MessageMetadata | undefined, match: MetadataMatch): boolean;
|
|
17
|
-
export declare function matchingDeferrals(deferrals: readonly DeliveryDeferralFact[], metadata: MessageMetadata | undefined, now: number): readonly DeliveryDeferralFact[];
|
|
18
|
-
export declare function deliveryDeferralExpiry(facts: ChannelRead, captured: EndpointRef, index: number, now: number): number | undefined;
|
|
19
|
-
export declare function earliestMatchingDeferralExpiry(deferrals: readonly DeliveryDeferralFact[], metadata: MessageMetadata | undefined, now: number): number | undefined;
|
|
10
|
+
export declare function matchesMetadata(metadata: ActivityMetadata | undefined, match: MetadataMatch): boolean;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { SquareError } from './neutral.js';
|
|
2
|
-
export const MAX_DELIVERY_DEFERRAL_MS = 24 * 60 * 60 * 1000;
|
|
3
2
|
export function validateMetadataMatch(value) {
|
|
4
3
|
const fail = () => { throw new SquareError('invalid_args', 'Invalid metadata match.'); };
|
|
5
4
|
const record = (item) => {
|
|
@@ -41,16 +40,6 @@ export function validateMetadataMatch(value) {
|
|
|
41
40
|
});
|
|
42
41
|
return Object.freeze({ fields: Object.freeze(validated) });
|
|
43
42
|
}
|
|
44
|
-
export function validateDeferralDuration(value) {
|
|
45
|
-
if (!Number.isSafeInteger(value) || value <= 0)
|
|
46
|
-
throw new SquareError('invalid_args', 'Delivery deferral duration must be a positive safe integer.');
|
|
47
|
-
return Math.min(value, MAX_DELIVERY_DEFERRAL_MS);
|
|
48
|
-
}
|
|
49
|
-
export function validateDeferralId(value) {
|
|
50
|
-
if (typeof value !== 'string' || !/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value))
|
|
51
|
-
throw new SquareError('invalid_args', 'Invalid delivery deferral id.');
|
|
52
|
-
return value;
|
|
53
|
-
}
|
|
54
43
|
export function matchesMetadata(metadata, match) {
|
|
55
44
|
if (metadata === undefined)
|
|
56
45
|
return false;
|
|
@@ -64,20 +53,3 @@ export function matchesMetadata(metadata, match) {
|
|
|
64
53
|
return value === equals && (value === null || typeof value !== 'object');
|
|
65
54
|
});
|
|
66
55
|
}
|
|
67
|
-
export function matchingDeferrals(deferrals, metadata, now) {
|
|
68
|
-
return deferrals.filter((lease) => lease.expiresAt > now && matchesMetadata(metadata, lease.match));
|
|
69
|
-
}
|
|
70
|
-
export function deliveryDeferralExpiry(facts, captured, index, now) {
|
|
71
|
-
const seat = facts.endpoint(captured.participant);
|
|
72
|
-
if (seat === undefined || seat.generation !== captured.generation || seat.owner !== captured.owner)
|
|
73
|
-
return undefined;
|
|
74
|
-
const leases = facts.deliveryDeferrals(seat.participant, seat.generation);
|
|
75
|
-
if (!leases.some(lease => lease.expiresAt > now))
|
|
76
|
-
return undefined;
|
|
77
|
-
const message = facts.message(index);
|
|
78
|
-
return message === undefined ? undefined : earliestMatchingDeferralExpiry(leases, message.metadata, now);
|
|
79
|
-
}
|
|
80
|
-
export function earliestMatchingDeferralExpiry(deferrals, metadata, now) {
|
|
81
|
-
const matches = matchingDeferrals(deferrals, metadata, now);
|
|
82
|
-
return matches.length ? Math.min(...matches.map((lease) => lease.expiresAt)) : undefined;
|
|
83
|
-
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Receivers frame finished cards; card text and its whitespace stay verbatim. */
|
|
2
|
+
export declare function renderDeliveryReminder(texts: readonly string[], options: {
|
|
3
|
+
tagged: boolean;
|
|
4
|
+
}): string;
|
|
5
|
+
export declare function deliveryFrameOverhead(options: {
|
|
6
|
+
tagged: boolean;
|
|
7
|
+
}): number;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const warning = 'This is an external notification, not a direct message from the user.';
|
|
2
|
+
/** Receivers frame finished cards; card text and its whitespace stay verbatim. */
|
|
3
|
+
export function renderDeliveryReminder(texts, options) {
|
|
4
|
+
if (!texts.length)
|
|
5
|
+
return '';
|
|
6
|
+
const text = `${warning}\n\n${texts.join('\n\n')}`;
|
|
7
|
+
return options.tagged ? `<system-reminder>\n${text}\n</system-reminder>` : text;
|
|
8
|
+
}
|
|
9
|
+
export function deliveryFrameOverhead(options) {
|
|
10
|
+
return renderDeliveryReminder([''], options).length;
|
|
11
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { DeliveryAttempt, DeliveryCard, DeliveryCorrelation, DeliveryHandoffRef, DeliveryHealth, DeliveryOutcome, DeliveryReceipt, DeliveryRoute, DeliverySession, PostResult, SuppressionResult } from './delivery-ledger.js';
|
|
2
|
+
/** All ledger file access and SQL are confined here. Transactions never span external effects. */
|
|
3
|
+
export declare class LedgerStore {
|
|
4
|
+
readonly location: string;
|
|
5
|
+
private readonly db;
|
|
6
|
+
private readonly clock;
|
|
7
|
+
private pending;
|
|
8
|
+
private closed;
|
|
9
|
+
private constructor();
|
|
10
|
+
static open(location: string, clock: () => number): Promise<LedgerStore>;
|
|
11
|
+
close(): Promise<void>;
|
|
12
|
+
private transaction;
|
|
13
|
+
private getSession;
|
|
14
|
+
private attached;
|
|
15
|
+
private listAttempts;
|
|
16
|
+
private awaitingEffect;
|
|
17
|
+
private getAttempt;
|
|
18
|
+
private normalize;
|
|
19
|
+
private deleteCard;
|
|
20
|
+
private gc;
|
|
21
|
+
session(owner: string): Promise<DeliverySession | undefined>;
|
|
22
|
+
sessions(): Promise<readonly DeliverySession[]>;
|
|
23
|
+
pendingCards(owner: string, generation: string): Promise<readonly DeliveryCard[]>;
|
|
24
|
+
handoffs(owner: string, generation: string): Promise<readonly DeliveryAttempt[]>;
|
|
25
|
+
attach(input: {
|
|
26
|
+
owner: string;
|
|
27
|
+
expectedGeneration: string | null;
|
|
28
|
+
routes: readonly DeliveryRoute[];
|
|
29
|
+
}): Promise<DeliverySession | false>;
|
|
30
|
+
updateRoutes(input: {
|
|
31
|
+
owner: string;
|
|
32
|
+
generation: string;
|
|
33
|
+
routes: readonly DeliveryRoute[];
|
|
34
|
+
}): Promise<boolean>;
|
|
35
|
+
lifecycle(input: {
|
|
36
|
+
owner: string;
|
|
37
|
+
generation: string;
|
|
38
|
+
}, state: 'detached' | 'deleted'): Promise<boolean>;
|
|
39
|
+
post(input: {
|
|
40
|
+
owner: string;
|
|
41
|
+
key: string;
|
|
42
|
+
text: string;
|
|
43
|
+
details: string | null;
|
|
44
|
+
}): Promise<PostResult>;
|
|
45
|
+
suppress(input: {
|
|
46
|
+
owner: string;
|
|
47
|
+
key: string;
|
|
48
|
+
scope: 'exact' | 'prefix';
|
|
49
|
+
expiresAt?: number;
|
|
50
|
+
}): Promise<SuppressionResult>;
|
|
51
|
+
renewSuppression(input: {
|
|
52
|
+
owner: string;
|
|
53
|
+
id: string;
|
|
54
|
+
expiresAt: number;
|
|
55
|
+
}): Promise<boolean>;
|
|
56
|
+
releaseSuppression(input: {
|
|
57
|
+
owner: string;
|
|
58
|
+
id: string;
|
|
59
|
+
}): Promise<boolean>;
|
|
60
|
+
cancel(input: {
|
|
61
|
+
owner: string;
|
|
62
|
+
generation: string;
|
|
63
|
+
}): Promise<boolean>;
|
|
64
|
+
private isSuppressed;
|
|
65
|
+
private eligible;
|
|
66
|
+
private insertAttempt;
|
|
67
|
+
claimPresentation(owner: string, generation: string, budgetChars: number, leaseMs: number, signal?: AbortSignal): Promise<{
|
|
68
|
+
cards: DeliveryCard[];
|
|
69
|
+
attempts: DeliveryAttempt[];
|
|
70
|
+
} | undefined>;
|
|
71
|
+
claimHandoff(owner: string, key: string, generation: string, kind: string, leaseMs: number, signal?: AbortSignal): Promise<{
|
|
72
|
+
card: DeliveryCard;
|
|
73
|
+
route: DeliveryRoute;
|
|
74
|
+
attempt: DeliveryAttempt;
|
|
75
|
+
} | undefined>;
|
|
76
|
+
current(tokens: readonly string[], signal?: AbortSignal, route?: DeliveryRoute): Promise<boolean>;
|
|
77
|
+
prepare(token: string, value: DeliveryCorrelation, signal?: AbortSignal): Promise<boolean>;
|
|
78
|
+
private receipt;
|
|
79
|
+
private confirm;
|
|
80
|
+
settle(tokens: readonly string[], outcome: Exclude<DeliveryOutcome, 'dispatching'>): Promise<boolean>;
|
|
81
|
+
private handoffFacts;
|
|
82
|
+
private matchingHandoff;
|
|
83
|
+
private currentHandoff;
|
|
84
|
+
handoff(token: string): Promise<(DeliveryHandoffRef & {
|
|
85
|
+
routeKind: string;
|
|
86
|
+
}) | undefined>;
|
|
87
|
+
guardHandoff(ref: DeliveryHandoffRef): Promise<boolean>;
|
|
88
|
+
acceptHandoff(ref: DeliveryHandoffRef): Promise<boolean>;
|
|
89
|
+
observePresentation(handoff: string | DeliveryHandoffRef, options?: {
|
|
90
|
+
readonly requireCurrent?: boolean;
|
|
91
|
+
}): Promise<boolean>;
|
|
92
|
+
receipts(owner: string, options: {
|
|
93
|
+
keys?: readonly string[];
|
|
94
|
+
since?: number;
|
|
95
|
+
}): Promise<readonly DeliveryReceipt[]>;
|
|
96
|
+
health(owner?: string): Promise<DeliveryHealth>;
|
|
97
|
+
}
|