@astrosheep/square 0.3.30 → 0.3.32
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/claude-plugin/.claude-plugin/plugin.json +1 -1
- package/claude-plugin/hooks/hooks.json +0 -3
- package/claude-plugin/skills/square/SKILL.md +5 -4
- package/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/dist/activity.js +8 -3
- package/dist/artifact.js +10 -51
- package/dist/automatic-session.js +49 -14
- package/dist/boundary-presentation.d.ts +1 -1
- package/dist/boundary-presentation.js +58 -14
- package/dist/catch-decisions.d.ts +17 -0
- package/dist/catch-decisions.js +53 -0
- package/dist/claude-hook.d.ts +2 -1
- package/dist/claude-hook.js +5 -2
- package/dist/cli/context.js +10 -2
- package/dist/cli/observation-commands.d.ts +5 -1
- package/dist/cli/observation-commands.js +68 -33
- package/dist/cli/square-commands.js +18 -12
- package/dist/codex-hook.d.ts +2 -1
- package/dist/codex-hook.js +5 -2
- package/dist/codex-queue.js +1 -1
- package/dist/decisions.js +2 -2
- package/dist/delivery-health.d.ts +1 -1
- package/dist/delivery-operations.d.ts +25 -0
- package/dist/delivery-operations.js +207 -0
- package/dist/delivery.d.ts +1 -0
- package/dist/help.js +1 -1
- package/dist/host-ledger-file-adapter.d.ts +34 -0
- package/dist/host-ledger-file-adapter.js +156 -0
- package/dist/host-ledger.d.ts +160 -0
- package/dist/host-ledger.js +1 -0
- package/dist/inbox.d.ts +2 -2
- package/dist/inbox.js +22 -14
- package/dist/index.d.ts +4 -1
- package/dist/index.js +1 -0
- package/dist/landing.d.ts +18 -14
- package/dist/landing.js +29 -113
- package/dist/model.d.ts +9 -17
- package/dist/model.js +2 -2
- package/dist/notifications.d.ts +12 -10
- package/dist/notifications.js +122 -190
- package/dist/open-square.d.ts +5 -4
- package/dist/open-square.js +1 -1
- package/dist/paseo-delivery.js +2 -0
- package/dist/ports.d.ts +138 -0
- package/dist/ports.js +1 -0
- package/dist/presence.d.ts +1 -2
- package/dist/presence.js +13 -46
- package/dist/presentation-operations.d.ts +3 -0
- package/dist/presentation-operations.js +51 -0
- package/dist/presentation.d.ts +2 -2
- package/dist/presentation.js +11 -7
- package/dist/presented.d.ts +1 -12
- package/dist/presented.js +6 -75
- package/dist/registry.d.ts +10 -10
- package/dist/registry.js +47 -113
- package/dist/routes.d.ts +27 -30
- package/dist/routes.js +88 -151
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +3 -4
- package/dist/square-actions.d.ts +33 -0
- package/dist/square-actions.js +204 -0
- package/dist/square-facade.d.ts +10 -7
- package/dist/square-file-adapter.d.ts +5 -3
- package/dist/square-file-adapter.js +23 -5
- package/dist/square-projections.d.ts +68 -0
- package/dist/square-projections.js +92 -0
- package/dist/square-storage.d.ts +2 -2
- package/dist/square-storage.js +2 -2
- package/dist/square-wiring.d.ts +3 -3
- package/dist/square-wiring.js +44 -29
- package/dist/views.d.ts +8 -2
- package/dist/views.js +28 -24
- package/dist/wake-attempts.d.ts +29 -22
- package/dist/wake-attempts.js +36 -119
- package/dist/wake-evidence.d.ts +6 -18
- package/dist/wake-evidence.js +17 -80
- package/dist/wake-port.js +1 -1
- package/dist/wakes.d.ts +2 -16
- package/dist/wakes.js +7 -27
- package/dist/watch.js +2 -3
- package/extensions/square-pi.js +7 -2
- package/package.json +1 -1
- package/skills/brainstorm/SKILL.md +2 -2
- package/skills/square/SKILL.md +5 -4
|
@@ -4,7 +4,7 @@ import { sessionInbox } from '../inbox.js';
|
|
|
4
4
|
import { sweepPendingNotifications } from '../notifications.js';
|
|
5
5
|
import { cmdListSquares } from '../list.js';
|
|
6
6
|
import { parseActivityId, sameName } from '../model.js';
|
|
7
|
-
import { commandPrefix, participantCommandPrefix, participantIdentity, renderGrepActivitiesView, renderEventCli, renderAmbientEvent, renderPresenceAnchor, withPathOutput, } from '../presentation.js';
|
|
7
|
+
import { commandPrefix, participantCommandPrefix, participantIdentity, renderGrepActivitiesView, renderEventCli, renderAmbientEvent, renderPresenceAnchor, withPathOutput, quoteShell, } from '../presentation.js';
|
|
8
8
|
import { actId, nowMs } from '../runtime.js';
|
|
9
9
|
import { cmdStream, cmdStreamNdjson } from '../stream.js';
|
|
10
10
|
import { formatRelativeTime, formatTimestamp, parseTimeOrRelative } from '../time.js';
|
|
@@ -114,20 +114,39 @@ function parseTimestamp(value, flag) {
|
|
|
114
114
|
fail(`Invalid ${flag} timestamp: ${value}`);
|
|
115
115
|
return timestamp;
|
|
116
116
|
}
|
|
117
|
+
function historyContinuationArgs(argv) {
|
|
118
|
+
const result = [];
|
|
119
|
+
for (let index = 0; index < argv.length; index++) {
|
|
120
|
+
const flag = argv[index];
|
|
121
|
+
if (flag === '--limit' || flag === '--before' || flag === '--after') {
|
|
122
|
+
index += 1;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
result.push(flag);
|
|
126
|
+
if (flag === '--from' || flag === '--since' || flag === '--at' || flag === '-B' || flag === '-A' || flag === '-C' || flag === '--mention' || flag === '--grep' || flag === '--fixed' || flag === '--order' || flag === '--format') {
|
|
127
|
+
const value = argv[index + 1];
|
|
128
|
+
if (value !== undefined) {
|
|
129
|
+
result.push(value);
|
|
130
|
+
index += 1;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
117
136
|
function parseHistory(argv, context) {
|
|
118
137
|
const squarePath = requireSquarePath(context);
|
|
119
138
|
const viewer = context.name;
|
|
120
139
|
let lastN = 10;
|
|
121
140
|
let lastNExplicit = false;
|
|
122
|
-
let before;
|
|
123
141
|
let after;
|
|
124
142
|
let afterIndex;
|
|
143
|
+
let beforeIndex;
|
|
125
144
|
const atIndexes = [];
|
|
126
145
|
let beforeContext;
|
|
127
146
|
let afterContext;
|
|
128
147
|
let mention;
|
|
129
148
|
let pending = false;
|
|
130
|
-
let
|
|
149
|
+
let noTruncate = false;
|
|
131
150
|
let grep;
|
|
132
151
|
let fixed;
|
|
133
152
|
let order;
|
|
@@ -148,18 +167,10 @@ function parseHistory(argv, context) {
|
|
|
148
167
|
lastNExplicit = true;
|
|
149
168
|
index += 1;
|
|
150
169
|
}
|
|
151
|
-
else if (flag === '--all') {
|
|
152
|
-
lastN = null;
|
|
153
|
-
lastNExplicit = true;
|
|
154
|
-
}
|
|
155
170
|
else if (flag === '--from') {
|
|
156
171
|
participants.push(...parseNameList(requireValue(argv, index, flag), flag));
|
|
157
172
|
index += 1;
|
|
158
173
|
}
|
|
159
|
-
else if (flag === '--until') {
|
|
160
|
-
before = parseTimestamp(requireValue(argv, index, flag), flag);
|
|
161
|
-
index += 1;
|
|
162
|
-
}
|
|
163
174
|
else if (flag === '--since') {
|
|
164
175
|
after = parseTimestamp(requireValue(argv, index, flag), flag);
|
|
165
176
|
index += 1;
|
|
@@ -168,6 +179,10 @@ function parseHistory(argv, context) {
|
|
|
168
179
|
afterIndex = parseActRef(requireValue(argv, index, flag), flag);
|
|
169
180
|
index += 1;
|
|
170
181
|
}
|
|
182
|
+
else if (flag === '--before') {
|
|
183
|
+
beforeIndex = parseActRef(requireValue(argv, index, flag), flag);
|
|
184
|
+
index += 1;
|
|
185
|
+
}
|
|
171
186
|
else if (flag === '--at') {
|
|
172
187
|
const values = requireValue(argv, index, flag).split(',');
|
|
173
188
|
if (values.some((value) => value === ''))
|
|
@@ -189,8 +204,8 @@ function parseHistory(argv, context) {
|
|
|
189
204
|
afterContext = context;
|
|
190
205
|
index += 1;
|
|
191
206
|
}
|
|
192
|
-
else if (flag === '--
|
|
193
|
-
|
|
207
|
+
else if (flag === '--no-truncate')
|
|
208
|
+
noTruncate = true;
|
|
194
209
|
else if (flag === '--mention') {
|
|
195
210
|
mention = requireValue(argv, index, flag);
|
|
196
211
|
index += 1;
|
|
@@ -227,29 +242,37 @@ function parseHistory(argv, context) {
|
|
|
227
242
|
fail('--grep and --fixed cannot be combined.');
|
|
228
243
|
if (grep === '' || fixed === '')
|
|
229
244
|
fail('--grep and --fixed require non-empty text.');
|
|
245
|
+
if (beforeIndex !== undefined && afterIndex !== undefined)
|
|
246
|
+
fail('--before and --after cannot be combined.');
|
|
230
247
|
if (!lastNExplicit && (atIndexes.length > 0 || pending))
|
|
231
248
|
lastN = null;
|
|
232
249
|
return {
|
|
233
250
|
lastN,
|
|
234
251
|
participants,
|
|
235
|
-
before,
|
|
236
252
|
after,
|
|
237
253
|
afterIndex,
|
|
254
|
+
beforeIndex,
|
|
238
255
|
atIndexes: atIndexes.length === 0 ? undefined : atIndexes,
|
|
239
256
|
beforeContext,
|
|
240
257
|
afterContext,
|
|
241
258
|
mention,
|
|
242
259
|
pending,
|
|
243
260
|
viewer,
|
|
244
|
-
|
|
261
|
+
noTruncate,
|
|
245
262
|
grep,
|
|
246
263
|
fixed,
|
|
247
264
|
order,
|
|
248
265
|
format,
|
|
249
266
|
json,
|
|
267
|
+
continuationArgs: historyContinuationArgs(argv),
|
|
250
268
|
};
|
|
251
269
|
}
|
|
252
|
-
function
|
|
270
|
+
function historyContinuationCommand(options, squarePath, direction, index) {
|
|
271
|
+
const prefix = options.viewer === undefined ? commandPrefix(squarePath) : participantCommandPrefix(squarePath, options.viewer);
|
|
272
|
+
const args = [...(options.continuationArgs ?? []), direction, actId(index), '--limit', String(options.lastN ?? 10)];
|
|
273
|
+
return `${prefix} history ${args.map((arg) => arg.startsWith('-') || /^act\/\d+$/.test(arg) || /^\d+$/.test(arg) ? arg : quoteShell(arg)).join(' ')}`;
|
|
274
|
+
}
|
|
275
|
+
function renderFields(sayNumbers, item, fields, perception) {
|
|
253
276
|
return fields.map((field) => {
|
|
254
277
|
switch (field) {
|
|
255
278
|
case 'id': return actId(item.index);
|
|
@@ -258,14 +281,14 @@ function renderFields(sayNumbers, item, fields) {
|
|
|
258
281
|
case 'ts':
|
|
259
282
|
case 'at': return formatTimestamp(item.at);
|
|
260
283
|
case 'kind': return item.kind;
|
|
261
|
-
case 'body': return 'body' in item && typeof item.body === 'string' ? item.body.replace(/\s+/g, ' ').trim() : '';
|
|
284
|
+
case 'body': return perception === 'presence' ? '' : 'body' in item && typeof item.body === 'string' ? item.body.replace(/\s+/g, ' ').trim() : '';
|
|
262
285
|
case 'number': return item.kind === 'say' ? String(sayNumbers[item.index]) : '';
|
|
263
286
|
case 'reply': return item.kind === 'say' && item.reply !== undefined ? actId(item.reply) : '';
|
|
264
287
|
default: return '';
|
|
265
288
|
}
|
|
266
289
|
}).join('\t');
|
|
267
290
|
}
|
|
268
|
-
function jsonLine(sayNumbers, item) {
|
|
291
|
+
function jsonLine(sayNumbers, item, perception) {
|
|
269
292
|
const act = item;
|
|
270
293
|
return JSON.stringify({
|
|
271
294
|
id: actId(item.index),
|
|
@@ -274,15 +297,15 @@ function jsonLine(sayNumbers, item) {
|
|
|
274
297
|
author: act.actor ?? null,
|
|
275
298
|
at: act.at,
|
|
276
299
|
ts: formatTimestamp(act.at),
|
|
277
|
-
body: 'body' in act && typeof act.body === 'string' ? act.body : '',
|
|
300
|
+
body: perception === 'presence' ? '' : 'body' in act && typeof act.body === 'string' ? act.body : '',
|
|
278
301
|
number: act.kind === 'say' ? sayNumbers[act.index] : null,
|
|
279
302
|
reach: act.kind === 'say' ? act.reach ?? null : null,
|
|
280
303
|
reply: act.kind === 'say' && act.reply !== undefined ? actId(act.reply) : null,
|
|
281
304
|
});
|
|
282
305
|
}
|
|
283
|
-
function renderHistoryProjection(projection, visible,
|
|
306
|
+
function renderHistoryProjection(projection, visible, noTruncate, squarePath, viewer, mode) {
|
|
284
307
|
const shown = visible.filter((activity) => activity.kind === 'say' || activity.kind === 'done');
|
|
285
|
-
const preview =
|
|
308
|
+
const preview = noTruncate ? undefined : 200;
|
|
286
309
|
const chunks = [];
|
|
287
310
|
for (const activity of shown) {
|
|
288
311
|
const options = {
|
|
@@ -302,7 +325,7 @@ function renderHistoryProjection(projection, visible, full, squarePath, viewer,
|
|
|
302
325
|
if (chunks.length === 0)
|
|
303
326
|
return 'latest\n ○ no public activity in this view';
|
|
304
327
|
if (preview !== undefined && shown.some((activity) => activity.kind === 'say' && activity.body.length > preview && (mode === 'archive' || activity.perception === 'full'))) {
|
|
305
|
-
chunks.push(`» ${commandPrefix(squarePath)} history --
|
|
328
|
+
chunks.push(`» ${commandPrefix(squarePath)} history --no-truncate`);
|
|
306
329
|
}
|
|
307
330
|
return chunks.join('\n\n');
|
|
308
331
|
}
|
|
@@ -312,29 +335,41 @@ export const historyCommand = {
|
|
|
312
335
|
const squarePath = requireSquarePath(context);
|
|
313
336
|
const square = await openSquare(squarePath, { clock: nowMs });
|
|
314
337
|
try {
|
|
315
|
-
|
|
338
|
+
// Keep the projection chronological; pagination chooses a stable edge,
|
|
339
|
+
// then --order only changes how the selected page is displayed.
|
|
340
|
+
const projection = await historyPresentation(square, { ...options, order: 'asc' });
|
|
316
341
|
let events = [...projection.activities];
|
|
317
342
|
const searching = options.grep !== undefined || options.fixed !== undefined;
|
|
318
343
|
const totalMatches = searching ? events.length : 0;
|
|
319
344
|
if (options.lastN != null) {
|
|
320
|
-
events = options.
|
|
345
|
+
events = options.afterIndex !== undefined
|
|
321
346
|
? events.slice(0, options.lastN)
|
|
322
347
|
: events.slice(-options.lastN);
|
|
323
348
|
}
|
|
349
|
+
if (options.order === 'desc')
|
|
350
|
+
events.reverse();
|
|
324
351
|
if (options.json)
|
|
325
|
-
return events.map((item) => jsonLine(projection.sayNumbers, item)).join('\n') + (events.length > 0 ? '\n' : '');
|
|
352
|
+
return events.map((item) => jsonLine(projection.sayNumbers, item, item.perception)).join('\n') + (events.length > 0 ? '\n' : '');
|
|
326
353
|
if (options.format !== undefined && options.format.length > 0) {
|
|
327
|
-
return events.map((item) => renderFields(projection.sayNumbers, item, options.format)).join('\n') + (events.length > 0 ? '\n' : '');
|
|
354
|
+
return events.map((item) => renderFields(projection.sayNumbers, item, options.format, item.perception)).join('\n') + (events.length > 0 ? '\n' : '');
|
|
328
355
|
}
|
|
329
356
|
const pattern = options.grep ?? options.fixed;
|
|
330
357
|
const anonymous = options.viewer === undefined;
|
|
331
|
-
const archive =
|
|
332
|
-
|| (options.lastN == null && options.full === true)
|
|
333
|
-
|| anonymous;
|
|
358
|
+
const archive = anonymous;
|
|
334
359
|
const output = pattern === undefined || pattern === ''
|
|
335
|
-
? renderHistoryProjection(projection, events, options.
|
|
336
|
-
: renderGrepActivitiesView(events, totalMatches, options.
|
|
337
|
-
|
|
360
|
+
? renderHistoryProjection(projection, events, options.noTruncate === true, squarePath, options.viewer ?? '', archive ? 'archive' : 'ambient')
|
|
361
|
+
: renderGrepActivitiesView(events, totalMatches, options.noTruncate, squarePath, pattern, options.fixed !== undefined, (item) => projection.activities.find((candidate) => candidate.index === item.index)?.perception ?? 'full');
|
|
362
|
+
const publicEvents = events.filter((item) => item.kind === 'say' || item.kind === 'done');
|
|
363
|
+
const allPublic = projection.activities.filter((item) => item.kind === 'say' || item.kind === 'done');
|
|
364
|
+
const pageMin = publicEvents.length === 0 ? undefined : Math.min(...publicEvents.map((item) => item.index));
|
|
365
|
+
const pageMax = publicEvents.length === 0 ? undefined : Math.max(...publicEvents.map((item) => item.index));
|
|
366
|
+
const hasMore = options.lastN != null && publicEvents.length > 0 && (options.afterIndex !== undefined
|
|
367
|
+
? allPublic.some((item) => item.index > (pageMax ?? options.afterIndex))
|
|
368
|
+
: allPublic.some((item) => item.index < (pageMin ?? Infinity)));
|
|
369
|
+
const cursorDirection = options.afterIndex !== undefined ? '--after' : '--before';
|
|
370
|
+
const cursorIndex = cursorDirection === '--after' ? Math.max(...publicEvents.map((item) => item.index)) : Math.min(...publicEvents.map((item) => item.index));
|
|
371
|
+
const continuation = hasMore ? `\n\n» ${historyContinuationCommand(options, squarePath, cursorDirection, cursorIndex)}` : '';
|
|
372
|
+
return withPathOutput(squarePath, output + continuation, { participantCount: projection.participantCount });
|
|
338
373
|
}
|
|
339
374
|
finally {
|
|
340
375
|
await closeOpenSquare(square);
|
|
@@ -428,7 +463,7 @@ export const statusCommand = {
|
|
|
428
463
|
: [` ${visible.replace(/\n/g, '\n ')}`];
|
|
429
464
|
if (visible.includes('more chars') && result.latestAct !== undefined) {
|
|
430
465
|
const prefix = context.name === undefined ? commandPrefix(squarePath) : participantCommandPrefix(squarePath, context.name);
|
|
431
|
-
latest.push(`» ${prefix} history --at ${actId(result.latestAct)} -C 2 --
|
|
466
|
+
latest.push(`» ${prefix} history --at ${actId(result.latestAct)} -C 2 --no-truncate`);
|
|
432
467
|
}
|
|
433
468
|
const output = [
|
|
434
469
|
`${result.activeCount} active · ${result.doneCount} done · cap ${cap} · throttle ${result.throttlePerMinute === undefined ? 'none' : `${result.throttlePerMinute}/min`}`,
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { cmdActivity } from '../activity.js';
|
|
2
2
|
import { formatActivityId, formatHardCap, parseActivityId, } from '../model.js';
|
|
3
3
|
import { participantCommandPrefix, participantIdentity, renderEventCli, renderAmbientEvent, withPathOutput, } from '../presentation.js';
|
|
4
|
-
import { hasAutomaticDeliveryIdentity,
|
|
5
|
-
import {
|
|
4
|
+
import { hasAutomaticDeliveryIdentity, } from '../registry.js';
|
|
5
|
+
import { createHostLedgerPort } from '../host-ledger-file-adapter.js';
|
|
6
|
+
import { projectLocalParticipantBinding, sessionIdsFromEnvironment } from '../square-projections.js';
|
|
6
7
|
import { nowMs } from '../runtime.js';
|
|
7
8
|
import { createSquare, openSquare } from '../square-file-adapter.js';
|
|
8
9
|
import { closeOpenSquare } from '../open-square.js';
|
|
9
10
|
import { openParticipant, Square } from '../square-wiring.js';
|
|
10
11
|
import { entryPresentation, eventPresentation } from '../views.js';
|
|
12
|
+
import { createDefaultWakeTransport } from '../notifications.js';
|
|
11
13
|
import { fail, parseHardCap, parsePositiveInteger, readStdin, requireParticipant, requireSquarePath, requireValue, resolveBody, usage, } from './context.js';
|
|
12
14
|
function parseBuild(argv) {
|
|
13
15
|
const options = { force: false, hardCap: null };
|
|
@@ -84,13 +86,19 @@ export const joinCommand = {
|
|
|
84
86
|
const beforeSquare = await openSquare(squarePath, { clock: nowMs });
|
|
85
87
|
const before = await entryPresentation(beforeSquare, intent.name, intent.lastN);
|
|
86
88
|
await closeOpenSquare(beforeSquare);
|
|
87
|
-
const
|
|
89
|
+
const hostLedger = createHostLedgerPort();
|
|
90
|
+
const square = await Square.at({ path: squarePath, clock: nowMs, hostLedger, wakeTransport: await createDefaultWakeTransport(hostLedger, nowMs) });
|
|
88
91
|
try {
|
|
92
|
+
const reconnect = before.joined
|
|
93
|
+
&& await projectLocalParticipantBinding({
|
|
94
|
+
hostLedger: createHostLedgerPort(),
|
|
95
|
+
location: squarePath,
|
|
96
|
+
participant: intent.name,
|
|
97
|
+
sessionIds: sessionIdsFromEnvironment(),
|
|
98
|
+
}) !== undefined;
|
|
89
99
|
const participant = await square.join(intent.name);
|
|
90
100
|
const joinedName = participant.name;
|
|
91
101
|
const isRejoin = before.joined;
|
|
92
|
-
const reconnect = isRejoin
|
|
93
|
-
&& await localParticipantOwner(squarePath, joinedName) !== undefined;
|
|
94
102
|
if (isRejoin && !intent.kick && !reconnect) {
|
|
95
103
|
fail([
|
|
96
104
|
`✕ ${participantIdentity(joinedName)} shoos you out of the square`,
|
|
@@ -102,8 +110,7 @@ export const joinCommand = {
|
|
|
102
110
|
const afterSquare = await openSquare(squarePath, { clock: nowMs });
|
|
103
111
|
const after = await entryPresentation(afterSquare, joinedName, intent.lastN);
|
|
104
112
|
await closeOpenSquare(afterSquare);
|
|
105
|
-
await
|
|
106
|
-
await sweepPendingNotifications(squarePath);
|
|
113
|
+
await square.reconcileBinding().catch(() => undefined);
|
|
107
114
|
const activities = after.recentActivities.map((event) => renderAmbientEvent(event, joinedName, {
|
|
108
115
|
now: nowMs(),
|
|
109
116
|
preview: intent.lastN === null ? undefined : 200,
|
|
@@ -175,7 +182,6 @@ export const expressCommand = {
|
|
|
175
182
|
parse: parseActivity,
|
|
176
183
|
async execute(intent, context) {
|
|
177
184
|
const squarePath = requireSquarePath(context);
|
|
178
|
-
await sweepPendingNotifications(squarePath);
|
|
179
185
|
const body = await resolveBody(intent.activity);
|
|
180
186
|
const reachArg = intent.reach === 'bell' ? ' --bell' : '';
|
|
181
187
|
await cmdActivity(squarePath, intent.name, body, (value) => value, {
|
|
@@ -275,12 +281,12 @@ export const doneCommand = {
|
|
|
275
281
|
async execute(intent, context) {
|
|
276
282
|
const squarePath = requireSquarePath(context);
|
|
277
283
|
const body = (await resolveBody(intent.body ?? (process.stdin.isTTY ? '' : '-'))).replace(/\r\n/g, '\n').trim();
|
|
278
|
-
const square = await Square.at({ path: squarePath, clock: nowMs
|
|
284
|
+
const square = await Square.at({ path: squarePath, clock: nowMs });
|
|
279
285
|
const participant = await square.join(intent.name);
|
|
280
286
|
const result = await participant.done(body);
|
|
287
|
+
await square.reconcileBinding();
|
|
281
288
|
await square.close();
|
|
282
289
|
const name = result.activity.actor;
|
|
283
|
-
await recordLocalDone(name, squarePath);
|
|
284
290
|
const presentation = await openSquare(squarePath, { clock: nowMs });
|
|
285
291
|
const participantCount = (await entryPresentation(presentation, name).finally(() => closeOpenSquare(presentation))).participantCount;
|
|
286
292
|
return withPathOutput(squarePath, `○ ${participantIdentity(name)} steps out of the square — done · just now`, { participantCount });
|
|
@@ -296,7 +302,7 @@ export const holdCommand = {
|
|
|
296
302
|
parse: parseHold,
|
|
297
303
|
async execute(intent, context) {
|
|
298
304
|
const squarePath = requireSquarePath(context);
|
|
299
|
-
const square = await Square.at({ path: squarePath, clock: nowMs
|
|
305
|
+
const square = await Square.at({ path: squarePath, clock: nowMs });
|
|
300
306
|
try {
|
|
301
307
|
const participant = await square.join(intent.name);
|
|
302
308
|
const result = await participant.hold((await resolveBody(intent.body ?? '')).replace(/\r\n/g, '\n').trim());
|
|
@@ -320,7 +326,7 @@ export const resumeCommand = {
|
|
|
320
326
|
},
|
|
321
327
|
async execute(intent, context) {
|
|
322
328
|
const squarePath = requireSquarePath(context);
|
|
323
|
-
const square = await Square.at({ path: squarePath, clock: nowMs
|
|
329
|
+
const square = await Square.at({ path: squarePath, clock: nowMs });
|
|
324
330
|
try {
|
|
325
331
|
const participant = await square.join(intent.name);
|
|
326
332
|
const result = await participant.resume();
|
package/dist/codex-hook.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { InboxMembership } from './model.js';
|
|
2
|
+
import type { WakeAdapter } from './delivery.js';
|
|
2
3
|
export interface CodexHookInput {
|
|
3
4
|
session_id?: unknown;
|
|
4
5
|
hook_event_name?: unknown;
|
|
5
6
|
cwd?: unknown;
|
|
6
7
|
source?: unknown;
|
|
7
8
|
}
|
|
8
|
-
export declare function codexHookResponse(input: CodexHookInput, lookup?: (sessionId: string) => Promise<InboxMembership[]> | InboxMembership[], env?: NodeJS.ProcessEnv): Promise<object | undefined>;
|
|
9
|
+
export declare function codexHookResponse(input: CodexHookInput, lookup?: (sessionId: string, env?: NodeJS.ProcessEnv) => Promise<InboxMembership[]> | InboxMembership[], env?: NodeJS.ProcessEnv, deliveryAdapters?: WakeAdapter[]): Promise<object | undefined>;
|
|
9
10
|
export declare function runCodexHook(inputText: string, env?: NodeJS.ProcessEnv): Promise<string>;
|
|
10
11
|
export declare function runCodexHookAsync(inputText: string, env?: NodeJS.ProcessEnv): Promise<string>;
|
package/dist/codex-hook.js
CHANGED
|
@@ -2,11 +2,12 @@ import { presentPendingAtBoundary } from './boundary-presentation.js';
|
|
|
2
2
|
import { sessionInbox } from './inbox.js';
|
|
3
3
|
import { automaticSessionEnd, automaticSessionStart } from './automatic-session.js';
|
|
4
4
|
import { clearCodexBoundary, recordCodexBoundary } from './codex-boundary-state.js';
|
|
5
|
+
import { sweepPrivilegedPending } from './notifications.js';
|
|
5
6
|
const CODEX_HOOK_EVENTS = {
|
|
6
7
|
PostToolUse: 'PostToolUse',
|
|
7
8
|
Stop: 'Stop',
|
|
8
9
|
};
|
|
9
|
-
export async function codexHookResponse(input, lookup = sessionInbox, env = process.env) {
|
|
10
|
+
export async function codexHookResponse(input, lookup = sessionInbox, env = process.env, deliveryAdapters) {
|
|
10
11
|
if (typeof input.session_id !== 'string' || input.session_id === '')
|
|
11
12
|
return undefined;
|
|
12
13
|
if (typeof input.hook_event_name !== 'string')
|
|
@@ -15,9 +16,11 @@ export async function codexHookResponse(input, lookup = sessionInbox, env = proc
|
|
|
15
16
|
if (hookEventName === undefined)
|
|
16
17
|
return undefined;
|
|
17
18
|
await recordCodexBoundary(input.session_id, hookEventName === 'Stop' ? 'Stop' : 'non-stop', env);
|
|
18
|
-
|
|
19
|
+
const response = await presentPendingAtBoundary(input.session_id, (context) => hookEventName === 'Stop'
|
|
19
20
|
? { systemMessage: context }
|
|
20
21
|
: { hookSpecificOutput: { hookEventName: 'PostToolUse', additionalContext: context } }, lookup, env);
|
|
22
|
+
await sweepPrivilegedPending(typeof input.cwd === 'string' ? input.cwd : process.cwd(), env, deliveryAdapters).catch(() => undefined);
|
|
23
|
+
return response;
|
|
21
24
|
}
|
|
22
25
|
export async function runCodexHook(inputText, env = process.env) {
|
|
23
26
|
let input;
|
package/dist/codex-queue.js
CHANGED
|
@@ -36,7 +36,7 @@ export class CodexQueueAdapter {
|
|
|
36
36
|
async dispatch(address, payload, beforeSend) {
|
|
37
37
|
const threadId = address.threadId?.trim();
|
|
38
38
|
if (!threadId) {
|
|
39
|
-
return { outcome: 'unavailable', signature: 'invalid_address', message: 'Codex route has no thread id.' };
|
|
39
|
+
return { outcome: 'unavailable', signature: 'invalid_address', message: 'Codex route has no thread id.', routeStale: true };
|
|
40
40
|
}
|
|
41
41
|
const env = this.opts.env ?? process.env;
|
|
42
42
|
if (!await codexQueueEligible(threadId, env)) {
|
package/dist/decisions.js
CHANGED
|
@@ -274,11 +274,11 @@ export function coreActivities(squareState, opts, suppliedDelivery) {
|
|
|
274
274
|
}
|
|
275
275
|
if (opts.afterIndex != null)
|
|
276
276
|
acts = acts.filter((act) => act.index > opts.afterIndex);
|
|
277
|
+
if (opts.beforeIndex != null)
|
|
278
|
+
acts = acts.filter((act) => act.index < opts.beforeIndex);
|
|
277
279
|
if (canonicalParticipants.length > 0) {
|
|
278
280
|
acts = acts.filter((act) => act.actor !== undefined && canonicalParticipants.some((participant) => sameName(participant, act.actor)));
|
|
279
281
|
}
|
|
280
|
-
if (opts.before != null)
|
|
281
|
-
acts = acts.filter((act) => act.at < opts.before);
|
|
282
282
|
if (opts.after != null)
|
|
283
283
|
acts = acts.filter((act) => act.at > opts.after);
|
|
284
284
|
if (opts.mention != null) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type DirectedNotificationRoute } from './delivery.js';
|
|
2
|
-
import type { WakeAttempt } from './
|
|
2
|
+
import type { WakeAttempt } from './square-projections.js';
|
|
3
3
|
export type DeliveryHealthKind = 'awaiting' | 'wake-accepted' | 'wake-unknown' | 'presented-not-delivered' | 'unreachable';
|
|
4
4
|
export interface DeliveryHealthItem {
|
|
5
5
|
squarePath: string;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type SquareState } from './model.js';
|
|
2
|
+
import type { HostLedgerPort, PresenceRecord, SquareArtifactPort, DeliverPendingInput, DeliveryResult, ObserveSquareInput, ReconcileBindingInput, SquareObservation } from './ports.js';
|
|
3
|
+
import { deriveDeliveryModel } from './delivery.js';
|
|
4
|
+
import { type WakeAttempt } from './square-projections.js';
|
|
5
|
+
export declare function observeSquare(input: ObserveSquareInput): Promise<SquareObservation>;
|
|
6
|
+
export declare function reconcileBinding(input: ReconcileBindingInput): Promise<import("./host-ledger.js").ReconcileBindingResult>;
|
|
7
|
+
export declare function deliverPending(input: DeliverPendingInput): Promise<DeliveryResult>;
|
|
8
|
+
export declare function selectPendingWakeActivities(state: SquareState, routes: readonly PresenceRecord[], attempts: readonly WakeAttempt[], now: number, graceMs: number, limit: number, delivery?: import("./delivery.js").DeliveryModel): number[];
|
|
9
|
+
export declare function sweepPending(input: {
|
|
10
|
+
readonly artifact: SquareArtifactPort;
|
|
11
|
+
readonly hostLedger: HostLedgerPort;
|
|
12
|
+
readonly location: string;
|
|
13
|
+
readonly now: number;
|
|
14
|
+
readonly graceMs: number;
|
|
15
|
+
readonly limit: number;
|
|
16
|
+
}): Promise<number[]>;
|
|
17
|
+
export declare function sweepPendingFromState(input: {
|
|
18
|
+
readonly state: SquareState;
|
|
19
|
+
readonly hostLedger: HostLedgerPort;
|
|
20
|
+
readonly location: string;
|
|
21
|
+
readonly now: number;
|
|
22
|
+
readonly graceMs: number;
|
|
23
|
+
readonly limit: number;
|
|
24
|
+
readonly deriveDelivery?: (snapshot: SquareState) => ReturnType<typeof deriveDeliveryModel>;
|
|
25
|
+
}): Promise<number[]>;
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { formatActivityId, parseActivityId } from './square-core.js';
|
|
3
|
+
import { nameKey } from './model.js';
|
|
4
|
+
import { deriveDeliveryModel } from './delivery.js';
|
|
5
|
+
import { isWakeRouteAttemptable } from './square-projections.js';
|
|
6
|
+
import { retireWakeRouteFromArtifact } from './routes.js';
|
|
7
|
+
export async function observeSquare(input) {
|
|
8
|
+
const snapshot = await input.artifact.read();
|
|
9
|
+
const delivery = deriveDeliveryModel(snapshot.state);
|
|
10
|
+
let rows = [];
|
|
11
|
+
if (input.hostLedger !== undefined) {
|
|
12
|
+
try {
|
|
13
|
+
rows = await input.hostLedger.listPresence({ location: input.location, scopes: ['user', 'local'], now: input.now });
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
rows = [];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const bindings = rows.map((record) => ({
|
|
20
|
+
location: record.location,
|
|
21
|
+
participant: record.participant,
|
|
22
|
+
sessionId: record.session,
|
|
23
|
+
channel: record.channel,
|
|
24
|
+
...(record.route === undefined ? {} : { route: { location: record.location, participant: record.participant, sessionId: record.session, channel: record.channel, kind: record.route.kind, address: { ...record.route.address }, updatedAt: record.updatedAt ?? 0 } }),
|
|
25
|
+
updatedAt: record.updatedAt ?? 0,
|
|
26
|
+
}));
|
|
27
|
+
return { ...(input.location === undefined ? {} : { location: input.location }), version: snapshot.version, state: snapshot.state, pending: delivery.joinedRecipients().map((recipient) => ({ recipient, notifications: delivery.pendingFor(recipient) })), bindings };
|
|
28
|
+
}
|
|
29
|
+
export async function reconcileBinding(input) {
|
|
30
|
+
return input.hostLedger.reconcileBinding({ artifact: input.artifact, scopes: input.scopes, now: input.now });
|
|
31
|
+
}
|
|
32
|
+
export async function deliverPending(input) {
|
|
33
|
+
const observation = await observeSquare({ artifact: input.artifact, hostLedger: input.hostLedger, location: input.location, now: input.now });
|
|
34
|
+
const routes = (observation.state.routes ?? []).map((route) => ({
|
|
35
|
+
location: route.location,
|
|
36
|
+
participant: route.participant,
|
|
37
|
+
session: route.sessionId,
|
|
38
|
+
channel: route.channel,
|
|
39
|
+
route: { kind: route.kind, address: route.address },
|
|
40
|
+
updatedAt: route.updatedAt,
|
|
41
|
+
}));
|
|
42
|
+
let attempted = 0;
|
|
43
|
+
let accepted = 0;
|
|
44
|
+
let failed = 0;
|
|
45
|
+
let unknown = 0;
|
|
46
|
+
let notCapable = 0;
|
|
47
|
+
for (const membership of observation.pending) {
|
|
48
|
+
for (const notification of membership.notifications) {
|
|
49
|
+
const candidates = routes.filter((route) => nameKey(route.participant) === nameKey(membership.recipient));
|
|
50
|
+
if (candidates.length === 0) {
|
|
51
|
+
notCapable += 1;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (input.activity !== undefined) {
|
|
55
|
+
const requested = typeof input.activity === 'number' ? input.activity : parseActivityId(input.activity);
|
|
56
|
+
if (requested === undefined || requested !== notification.item.index)
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
let acceptedForAttention = false;
|
|
60
|
+
try {
|
|
61
|
+
const prior = await input.hostLedger.listWakeAttempts({ attention: { squarePath: input.location, actIndex: notification.item.index, recipient: membership.recipient }, now: input.now });
|
|
62
|
+
acceptedForAttention = prior.some((attempt) => attempt.outcome === 'accepted');
|
|
63
|
+
}
|
|
64
|
+
catch { /* capability is handled by the route-level probe */ }
|
|
65
|
+
if (acceptedForAttention)
|
|
66
|
+
continue;
|
|
67
|
+
for (const route of candidates) {
|
|
68
|
+
const activity = formatActivityId(notification.item.index);
|
|
69
|
+
const attention = { squarePath: input.location, actIndex: notification.item.index, recipient: membership.recipient };
|
|
70
|
+
const leaseMs = input.timeoutMs ?? 5000;
|
|
71
|
+
const requestRoute = { location: route.location, participant: route.participant, sessionId: route.session, channel: route.channel, kind: route.route.kind, address: { ...route.route.address }, updatedAt: route.updatedAt ?? 0 };
|
|
72
|
+
if (input.transport.probe !== undefined) {
|
|
73
|
+
try {
|
|
74
|
+
const probe = await input.transport.probe(requestRoute);
|
|
75
|
+
if (probe === false || (typeof probe === 'object' && probe.outcome === 'not-capable')) {
|
|
76
|
+
notCapable += 1;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
notCapable += 1;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
let leaseId = randomUUID();
|
|
86
|
+
let lease;
|
|
87
|
+
try {
|
|
88
|
+
lease = await input.hostLedger.claimWakeDispatch({ attention, leaseId, leaseMs, session: route.session, at: input.now });
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
notCapable += 1;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (lease.type === 'ambiguous') {
|
|
95
|
+
await input.hostLedger.appendEvidence({ location: input.location, participant: membership.recipient, session: route.session, activity, kind: 'wake', outcome: 'unknown', routeKind: lease.lease.routeKind ?? route.route.kind, attemptN: lease.lease.attemptN ?? 1, signature: 'worker_interrupted_during_dispatch', message: 'The notification worker ended after dispatch began; transport acceptance is unknown.', at: input.now });
|
|
96
|
+
await input.hostLedger.releaseWakeDispatch({ attention, leaseId: lease.lease.leaseId, session: route.session, at: input.now });
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (lease.type !== 'acquired')
|
|
100
|
+
continue;
|
|
101
|
+
let attempts;
|
|
102
|
+
try {
|
|
103
|
+
attempts = await input.hostLedger.listWakeAttempts({ attention, session: route.session, now: input.now });
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
notCapable += 1;
|
|
107
|
+
await input.hostLedger.releaseWakeDispatch({ attention, leaseId, session: route.session, at: input.now }).catch(() => undefined);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (attempts.some((attempt) => attempt.routeKind === route.route.kind && attempt.outcome === 'unknown')) {
|
|
111
|
+
await input.hostLedger.releaseWakeDispatch({ attention, leaseId, session: route.session, at: input.now });
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const attemptN = attempts.reduce((highest, record) => Math.max(highest, record.attemptN ?? 0), 0) + 1;
|
|
115
|
+
const request = { location: input.location, participant: membership.recipient, activity, route: requestRoute };
|
|
116
|
+
let outcome;
|
|
117
|
+
const claim = await input.hostLedger.claimEvidence({ location: input.location, participant: membership.recipient, session: route.session, activity, kind: 'wake', leaseMs, now: input.now });
|
|
118
|
+
if (claim.status !== 'acquired') {
|
|
119
|
+
await input.hostLedger.releaseWakeDispatch({ attention, leaseId, session: route.session, at: input.now });
|
|
120
|
+
if (claim.status === 'degraded')
|
|
121
|
+
notCapable += 1;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
attempted += 1;
|
|
125
|
+
const dispatching = await input.hostLedger.transitionWakeDispatch({ attention, leaseId, phase: 'dispatching', leaseMs, routeKind: route.route.kind, attemptN, session: route.session, at: input.now });
|
|
126
|
+
if (!dispatching) {
|
|
127
|
+
await input.hostLedger.releaseWakeDispatch({ attention, leaseId, session: route.session, at: input.now });
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
outcome = await Promise.race([input.transport.attempt(request, leaseMs), new Promise((resolve) => setTimeout(() => resolve({ outcome: 'unknown', diagnostic: 'transport timeout' }), leaseMs))]);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
outcome = { outcome: 'unknown', diagnostic: error instanceof Error ? error.message : String(error) };
|
|
135
|
+
}
|
|
136
|
+
if (outcome.outcome === 'not-capable') {
|
|
137
|
+
await input.hostLedger.releaseEvidence({ location: input.location, participant: membership.recipient, session: route.session, activity, kind: 'wake', now: input.now }).catch(() => undefined);
|
|
138
|
+
await input.hostLedger.releaseWakeDispatch({ attention, leaseId, session: route.session, at: input.now }).catch(() => undefined);
|
|
139
|
+
notCapable += 1;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (outcome.outcome === 'failed' && outcome.unavailable) {
|
|
143
|
+
if (outcome.routeStale === true)
|
|
144
|
+
await retireWakeRouteFromArtifact(input.artifact, { location: route.location, participant: route.participant, sessionId: route.session });
|
|
145
|
+
await input.hostLedger.releaseEvidence({ location: input.location, participant: membership.recipient, session: route.session, activity, kind: 'wake', now: input.now });
|
|
146
|
+
await input.hostLedger.releaseWakeDispatch({ attention, leaseId, session: route.session, at: input.now });
|
|
147
|
+
failed += 1;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
await input.hostLedger.appendEvidence({ location: input.location, participant: membership.recipient, session: route.session, activity, kind: 'wake', outcome: outcome.outcome, routeKind: route.route.kind, attemptN: outcome.attemptN ?? attemptN, ...(outcome.outcome === 'accepted' && outcome.signature === undefined ? {} : outcome.outcome === 'accepted' ? { signature: outcome.signature } : outcome.outcome === 'failed' ? { message: outcome.message } : { diagnostic: outcome.diagnostic }), at: input.now });
|
|
151
|
+
await input.hostLedger.releaseWakeDispatch({ attention, leaseId, session: route.session, at: input.now });
|
|
152
|
+
if (outcome.outcome === 'accepted') {
|
|
153
|
+
accepted += 1;
|
|
154
|
+
acceptedForAttention = true;
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
if (outcome.outcome === 'failed')
|
|
158
|
+
failed += 1;
|
|
159
|
+
else
|
|
160
|
+
unknown += 1;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return { attempted, accepted, failed, unknown, notCapable };
|
|
165
|
+
}
|
|
166
|
+
export function selectPendingWakeActivities(state, routes, attempts, now, graceMs, limit, delivery = deriveDeliveryModel(state)) {
|
|
167
|
+
const selected = new Set();
|
|
168
|
+
for (const membership of delivery.joinedRecipients()) {
|
|
169
|
+
for (const notification of delivery.pendingFor(membership)) {
|
|
170
|
+
if (now - notification.item.at <= graceMs)
|
|
171
|
+
continue;
|
|
172
|
+
if (attempts.some((attempt) => attempt.attention.actIndex === notification.item.index && nameKey(attempt.attention.recipient) === nameKey(membership) && attempt.outcome === 'accepted'))
|
|
173
|
+
continue;
|
|
174
|
+
const eligible = routes.some((binding) => {
|
|
175
|
+
if (binding.route === undefined || nameKey(binding.participant) !== nameKey(membership))
|
|
176
|
+
return false;
|
|
177
|
+
const matching = attempts.filter((attempt) => attempt.session === binding.session && nameKey(attempt.attention.recipient) === nameKey(membership) && attempt.attention.actIndex === notification.item.index);
|
|
178
|
+
return isWakeRouteAttemptable({ kind: binding.route.kind, updatedAt: binding.updatedAt ?? 0 }, matching);
|
|
179
|
+
});
|
|
180
|
+
if (eligible)
|
|
181
|
+
selected.add(notification.item.index);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return [...selected].sort((left, right) => left - right).slice(0, Math.max(0, limit));
|
|
185
|
+
}
|
|
186
|
+
export async function sweepPending(input) {
|
|
187
|
+
const { state } = await input.artifact.read();
|
|
188
|
+
return sweepPendingFromState({ ...input, state });
|
|
189
|
+
}
|
|
190
|
+
export async function sweepPendingFromState(input) {
|
|
191
|
+
const bindings = (input.state.routes ?? []).map((route) => ({ location: input.location, participant: route.participant, session: route.sessionId, channel: route.channel, route: { kind: route.kind, address: route.address }, updatedAt: route.updatedAt }));
|
|
192
|
+
let records = [];
|
|
193
|
+
try {
|
|
194
|
+
records = await input.hostLedger.listWakeAttempts({ now: input.now });
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
records = [];
|
|
198
|
+
}
|
|
199
|
+
const attempts = records.flatMap((record) => {
|
|
200
|
+
const index = parseActivityId(record.activity);
|
|
201
|
+
if (index === undefined || record.routeKind === undefined || typeof record.attemptN !== 'number')
|
|
202
|
+
return [];
|
|
203
|
+
return [{ at: record.at ?? input.now, attention: { squarePath: record.location, actIndex: index, recipient: record.participant }, routeKind: record.routeKind, outcome: record.outcome, attemptN: record.attemptN, ...(record.session === undefined ? {} : { session: record.session }) }];
|
|
204
|
+
});
|
|
205
|
+
const delivery = input.deriveDelivery?.(input.state) ?? deriveDeliveryModel(input.state);
|
|
206
|
+
return selectPendingWakeActivities(input.state, bindings, attempts, input.now, input.graceMs, input.limit, delivery);
|
|
207
|
+
}
|