@astrosheep/square 0.3.94 → 0.3.95
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.js +1 -1
- package/dist/attention.d.ts +19 -0
- package/dist/attention.js +30 -0
- package/dist/boundary-presentation.js +33 -40
- package/dist/catch-decisions.d.ts +1 -2
- package/dist/catch-decisions.js +41 -21
- package/dist/cli/context.js +0 -2
- package/dist/cli/express-presentation.js +3 -3
- package/dist/cli/json.js +1 -1
- package/dist/cli/program.js +1 -1
- package/dist/cli/registry.js +1 -3
- package/dist/cli/square-commands.d.ts +0 -2
- package/dist/cli/square-commands.js +21 -27
- package/dist/decisions.d.ts +0 -5
- package/dist/decisions.js +0 -13
- package/dist/delivery-attempts.d.ts +52 -0
- package/dist/delivery-attempts.js +73 -0
- package/dist/delivery-effects.d.ts +139 -0
- package/dist/delivery-effects.js +163 -0
- package/dist/delivery-operations.js +43 -68
- package/dist/delivery-primitives.d.ts +5 -0
- package/dist/delivery-primitives.js +3 -0
- package/dist/delivery.d.ts +1 -1
- package/dist/delivery.js +6 -5
- package/dist/help.js +4 -6
- package/dist/http-delivery.d.ts +16 -0
- package/dist/http-delivery.js +101 -0
- package/dist/model.d.ts +4 -6
- package/dist/model.js +1 -1
- package/dist/notifications.js +2 -0
- package/dist/operation-presentation.d.ts +1 -2
- package/dist/operation-presentation.js +12 -20
- package/dist/ports.d.ts +5 -14
- package/dist/presentation-operations.d.ts +14 -19
- package/dist/presentation-operations.js +47 -57
- package/dist/presentation.d.ts +2 -9
- package/dist/presentation.js +13 -48
- package/dist/receive.d.ts +66 -0
- package/dist/receive.js +54 -0
- package/dist/runtime.d.ts +0 -1
- package/dist/runtime.js +2 -3
- package/dist/sqlite-square.d.ts +0 -1
- package/dist/sqlite-square.js +29 -55
- package/dist/square-api.d.ts +1 -1
- package/dist/square-api.js +1 -1
- package/dist/square-core.d.ts +0 -9
- package/dist/square-domain.js +9 -31
- package/dist/square-facade.d.ts +5 -21
- package/dist/square-legacy-framing.d.ts +2 -0
- package/dist/square-legacy-framing.js +8 -0
- package/dist/square-migration.d.ts +3 -1
- package/dist/square-migration.js +46 -13
- package/dist/square-receive.d.ts +6 -0
- package/dist/square-receive.js +18 -0
- package/dist/square-sqlite.d.ts +5 -3
- package/dist/square-sqlite.js +89 -90
- package/dist/square-tools.js +3 -11
- package/dist/views.d.ts +2 -3
- package/dist/views.js +13 -19
- package/dist/wakes.d.ts +0 -3
- package/dist/wakes.js +3 -5
- package/examples/symmetric-domains.mjs +250 -0
- package/guides/application-facade-delivery-evidence.md +5 -5
- package/guides/attention-primitive.md +32 -0
- package/guides/change-waits.md +2 -2
- package/guides/codex-delivery.md +1 -1
- package/guides/delivery-primitives.md +180 -0
- package/guides/mcp.md +1 -1
- package/guides/migration.md +12 -6
- package/guides/participant-ownership.md +1 -1
- package/guides/pi-tools.md +2 -2
- package/guides/primitive-catch-consolidation.md +5 -5
- package/guides/primitive-history-consolidation.md +2 -2
- package/guides/primitive-status-consolidation.md +3 -3
- package/guides/primitive-storage-consolidation.md +1 -1
- package/guides/receive-primitive.md +90 -0
- package/guides/row-runtime-delivery-evidence.md +1 -1
- package/guides/row-storage-design.md +17 -23
- package/guides/square-api.md +77 -10
- package/guides/symmetric-domains.md +92 -0
- package/package.json +14 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/skills/square/SKILL.md +2 -6
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/skills/square/SKILL.md +2 -6
package/dist/square-sqlite.js
CHANGED
|
@@ -2,7 +2,9 @@ import { DatabaseSync as MemoryDatabase } from 'node:sqlite';
|
|
|
2
2
|
import { SquareError, nameKey, isWakeRouteKind, validateOwner, validateActivityMetadata, activityMetadataHasContent } from './model.js';
|
|
3
3
|
import { validateMetadataMatch, matchingDeferrals, validateDeferralId } from './delivery-deferral.js';
|
|
4
4
|
import { deriveLanding, landingFacts } from './square-domain.js';
|
|
5
|
-
import {
|
|
5
|
+
import { claimAttempt, prepareAttempt, settleAttempt } from './delivery-attempts.js';
|
|
6
|
+
import { legacyRowFramingIssue, legacyActivitySchema } from './square-legacy-framing.js';
|
|
7
|
+
import { receive, recordConsumed } from './receive.js';
|
|
6
8
|
export function emptyRuntimeState(nextActIndex = 0) {
|
|
7
9
|
return { nextActIndex, observations: {}, leases: {} };
|
|
8
10
|
}
|
|
@@ -13,7 +15,8 @@ export const V3_USER_VERSION = 3;
|
|
|
13
15
|
export const V4_USER_VERSION = 4;
|
|
14
16
|
export const V5_USER_VERSION = 5;
|
|
15
17
|
export const V6_USER_VERSION = 6;
|
|
16
|
-
export const
|
|
18
|
+
export const V7_USER_VERSION = 7;
|
|
19
|
+
export const CURRENT_USER_VERSION = 8;
|
|
17
20
|
export const V1_SNAPSHOT_TABLE = 'square_snapshot';
|
|
18
21
|
const V1_SNAPSHOT_DDL = `CREATE TABLE ${V1_SNAPSHOT_TABLE} (id INTEGER PRIMARY KEY CHECK(id=1), revision INTEGER NOT NULL CHECK(revision>=0), state TEXT NOT NULL)`;
|
|
19
22
|
const V1_SNAPSHOT_COLUMNS = [
|
|
@@ -58,18 +61,16 @@ export function validateLegacyFraming(database, applicationId, location = '') {
|
|
|
58
61
|
function normalizedLegacySql(sql) {
|
|
59
62
|
return sql.trim().replace(/\s+/g, ' ').replace(/\s*(>=|[(),=])\s*/g, '$1');
|
|
60
63
|
}
|
|
61
|
-
const CURRENT_ACTIVITY_KIND_CHECK = "kind IN ('join','done','say','
|
|
64
|
+
const CURRENT_ACTIVITY_KIND_CHECK = "kind IN ('join','done','say','read','listen','ignore','unlisten')";
|
|
62
65
|
export const ACTIVITIES_TABLE_SQL = `CREATE TABLE activities (
|
|
63
66
|
act_index INTEGER PRIMARY KEY CHECK (act_index BETWEEN 0 AND 9007199254740991),
|
|
64
67
|
at REAL NOT NULL,
|
|
65
68
|
kind TEXT NOT NULL CHECK (${CURRENT_ACTIVITY_KIND_CHECK}),
|
|
66
|
-
actor_json TEXT COLLATE BINARY,
|
|
67
|
-
actor_key_json TEXT COLLATE BINARY,
|
|
69
|
+
actor_json TEXT COLLATE BINARY NOT NULL,
|
|
70
|
+
actor_key_json TEXT COLLATE BINARY NOT NULL,
|
|
68
71
|
reach TEXT CHECK (reach = 'bell'),
|
|
69
72
|
reply_index INTEGER CHECK (reply_index >= 0 AND reply_index < act_index),
|
|
70
73
|
payload_json TEXT NOT NULL CHECK (json_valid(payload_json)),
|
|
71
|
-
CHECK ((actor_json IS NULL) = (actor_key_json IS NULL)),
|
|
72
|
-
CHECK (kind IN ('hold','resume') OR actor_json IS NOT NULL),
|
|
73
74
|
CHECK (kind = 'say' OR (reach IS NULL AND reply_index IS NULL))
|
|
74
75
|
);`;
|
|
75
76
|
export const ACTIVITY_INDEXES_SQL = [
|
|
@@ -182,9 +183,11 @@ CREATE TABLE landed_audience (
|
|
|
182
183
|
UNIQUE (act_index, recipient_order)
|
|
183
184
|
) WITHOUT ROWID;
|
|
184
185
|
CREATE INDEX audience_recipient ON landed_audience(recipient_key_json, act_index);`;
|
|
185
|
-
export const
|
|
186
|
+
export const FORMAT7_SCHEMA_SQL = legacyActivitySchema(SCHEMA_SQL);
|
|
187
|
+
export const FORMAT6_SCHEMA_SQL = FORMAT7_SCHEMA_SQL.replace(` owner_json TEXT NOT NULL,\n generation_json TEXT NOT NULL,`, ` channel_json TEXT NOT NULL,\n session_json TEXT NOT NULL,\n agent_json TEXT,\n generation_json TEXT NOT NULL,`).replace('CREATE INDEX seat_owner ON seats(owner_json);', 'CREATE INDEX seat_body ON seats(channel_json, session_json, agent_json);');
|
|
186
188
|
export const FORMAT5_SCHEMA_SQL = FORMAT6_SCHEMA_SQL.replace(`,\n ${ATTACHMENT_COLUMN_SQL}`, '');
|
|
187
189
|
export const FORMAT4_SCHEMA_SQL = FORMAT5_SCHEMA_SQL.replace(DELIVERY_DEFERRALS_SQL, '');
|
|
190
|
+
const FORMAT7_SCHEMA = FORMAT7_SCHEMA_SQL.split(';').map(s => s.trim()).filter(Boolean).map(normalizedSql).sort();
|
|
188
191
|
const FORMAT6_SCHEMA = FORMAT6_SCHEMA_SQL.split(';').map(s => s.trim()).filter(Boolean).map(normalizedSql).sort();
|
|
189
192
|
const FORMAT5_SCHEMA = FORMAT5_SCHEMA_SQL.split(';').map(s => s.trim()).filter(Boolean).map(normalizedSql).sort();
|
|
190
193
|
const EXPECTED_SCHEMA_SQL = SCHEMA_SQL.split(';').map(s => s.trim()).filter(Boolean).map(normalizedSql).sort();
|
|
@@ -244,17 +247,17 @@ function immutable(value) {
|
|
|
244
247
|
}
|
|
245
248
|
return value;
|
|
246
249
|
}
|
|
247
|
-
const kinds = ['join', 'done', 'say', '
|
|
250
|
+
const kinds = ['join', 'done', 'say', 'read', 'listen', 'ignore', 'unlisten'];
|
|
248
251
|
function head(row) {
|
|
249
252
|
const index = integer(row.act_index), at = finite(row.at);
|
|
250
253
|
requireFact(kinds.includes(string(row.kind)));
|
|
251
|
-
const actor =
|
|
252
|
-
requireFact(
|
|
254
|
+
const actor = decoded(row.actor_json, true);
|
|
255
|
+
requireFact(row.actor_key_json === key(actor));
|
|
253
256
|
requireFact(row.reach === null || row.kind === 'say' && row.reach === 'bell');
|
|
254
257
|
if (row.reply_index !== null)
|
|
255
258
|
requireFact(row.kind === 'say' && integer(row.reply_index) < index);
|
|
256
259
|
return immutable({
|
|
257
|
-
index, at, kind: row.kind,
|
|
260
|
+
index, at, kind: row.kind, actor, ...(row.reach === null ? {} : { reach: 'bell' }), ...(row.reply_index === null ? {} : { reply: row.reply_index })
|
|
258
261
|
});
|
|
259
262
|
}
|
|
260
263
|
export function validateStoredActivity(value) {
|
|
@@ -262,13 +265,8 @@ export function validateStoredActivity(value) {
|
|
|
262
265
|
integer(a.index);
|
|
263
266
|
finite(a.at);
|
|
264
267
|
requireFact(kinds.includes(string(a.kind)));
|
|
265
|
-
const common = ['kind', 'at', 'index'];
|
|
266
|
-
|
|
267
|
-
common.push('actor');
|
|
268
|
-
string(a.actor, true);
|
|
269
|
-
}
|
|
270
|
-
else if (Object.hasOwn(a, 'actor'))
|
|
271
|
-
string(a.actor, true);
|
|
268
|
+
const common = ['kind', 'at', 'index', 'actor'];
|
|
269
|
+
string(a.actor, true);
|
|
272
270
|
switch (a.kind) {
|
|
273
271
|
case 'say':
|
|
274
272
|
shape(a, [...common], ['body', 'mentions', 'reach', 'reply', 'metadata']);
|
|
@@ -292,8 +290,7 @@ export function validateStoredActivity(value) {
|
|
|
292
290
|
requireFact(integer(a.reply) < a.index);
|
|
293
291
|
break;
|
|
294
292
|
case 'done':
|
|
295
|
-
|
|
296
|
-
shape(a, common, ['body', ...(a.kind === 'hold' ? ['actor'] : [])]);
|
|
293
|
+
shape(a, common, ['body']);
|
|
297
294
|
if (Object.hasOwn(a, 'body'))
|
|
298
295
|
string(a.body);
|
|
299
296
|
break;
|
|
@@ -307,14 +304,14 @@ export function validateStoredActivity(value) {
|
|
|
307
304
|
shape(a, [...common, 'target']);
|
|
308
305
|
string(a.target, true);
|
|
309
306
|
break;
|
|
310
|
-
default: shape(a, common
|
|
307
|
+
default: shape(a, common);
|
|
311
308
|
}
|
|
312
309
|
return value;
|
|
313
310
|
}
|
|
314
311
|
function activity(row) {
|
|
315
312
|
const h = head(row), payload = object(json(row.payload_json));
|
|
316
313
|
// Payload cannot shadow indexed structural columns.
|
|
317
|
-
const allowed = h.kind === 'say' ? ['body', 'mentions', 'metadata'] : h.kind === 'done'
|
|
314
|
+
const allowed = h.kind === 'say' ? ['body', 'mentions', 'metadata'] : h.kind === 'done' ? ['body'] : h.kind === 'read' ? ['through'] : h.kind === 'listen' || h.kind === 'ignore' || h.kind === 'unlisten' ? ['target'] : [];
|
|
318
315
|
requireFact(Object.keys(payload).every(k => allowed.includes(k)));
|
|
319
316
|
return immutable(validateStoredActivity({ ...h, ...payload }));
|
|
320
317
|
}
|
|
@@ -403,8 +400,7 @@ function nativeCorrelation(value) {
|
|
|
403
400
|
...(item.messageId === undefined ? {} : { messageId: item.messageId }),
|
|
404
401
|
...(item.payloadHash === undefined ? {} : { payloadHash: item.payloadHash }) });
|
|
405
402
|
}
|
|
406
|
-
function deliveryEvidence(row) {
|
|
407
|
-
const payload = object(json(row.payload_json));
|
|
403
|
+
function deliveryEvidence(row, payload = object(json(row.payload_json))) {
|
|
408
404
|
const participant = decoded(row.participant_json, true);
|
|
409
405
|
requireFact(row.participant_key_json === key(participant));
|
|
410
406
|
const generation = activityId(decoded(row.generation_json, true));
|
|
@@ -461,7 +457,7 @@ export function validateRowFraming(db, version, location = '') {
|
|
|
461
457
|
const identity = db.prepare('SELECT application_id,user_version FROM pragma_application_id,pragma_user_version').get();
|
|
462
458
|
requireFact(identity.application_id === APPLICATION_ID, 'unrelated SQLite database.');
|
|
463
459
|
requireFact(identity.user_version === version, 'unsupported SQLite format version.');
|
|
464
|
-
validateOwnedSchema(db, version === V4_USER_VERSION ? FORMAT4_SCHEMA : version === V5_USER_VERSION ? FORMAT5_SCHEMA : version === V6_USER_VERSION ? FORMAT6_SCHEMA : EXPECTED_SCHEMA_SQL, location);
|
|
460
|
+
validateOwnedSchema(db, version === V4_USER_VERSION ? FORMAT4_SCHEMA : version === V5_USER_VERSION ? FORMAT5_SCHEMA : version === V6_USER_VERSION ? FORMAT6_SCHEMA : version === V7_USER_VERSION ? FORMAT7_SCHEMA : EXPECTED_SCHEMA_SQL, location);
|
|
465
461
|
}
|
|
466
462
|
export function validateSqliteHeader(db, location = '') {
|
|
467
463
|
const identity = db.prepare('SELECT application_id,user_version FROM pragma_application_id,pragma_user_version').get();
|
|
@@ -470,7 +466,7 @@ export function validateSqliteHeader(db, location = '') {
|
|
|
470
466
|
validateLegacyFraming(db, APPLICATION_ID, location);
|
|
471
467
|
throw new SquareError('invalid_args', `Square artifact at ${location} needs migration.\nStop sessions using it, back it up, then migrate and verify before restarting.`, { reason: 'migration_required' });
|
|
472
468
|
}
|
|
473
|
-
if (identity.user_version === V2_USER_VERSION || identity.user_version === V3_USER_VERSION || identity.user_version === V4_USER_VERSION || identity.user_version === V5_USER_VERSION || identity.user_version === V6_USER_VERSION) {
|
|
469
|
+
if (identity.user_version === V2_USER_VERSION || identity.user_version === V3_USER_VERSION || identity.user_version === V4_USER_VERSION || identity.user_version === V5_USER_VERSION || identity.user_version === V6_USER_VERSION || identity.user_version === V7_USER_VERSION) {
|
|
474
470
|
validateRowFraming(db, identity.user_version, location);
|
|
475
471
|
throw new SquareError('invalid_args', `Square artifact at ${location} needs migration.\nStop sessions using it, back it up, then migrate and verify before restarting.`, { reason: 'migration_required' });
|
|
476
472
|
}
|
|
@@ -494,8 +490,6 @@ function activitySelection(filter, alias = 'a') {
|
|
|
494
490
|
add(`${alias}.act_index <= ?`, integer(filter.throughIndex, -1));
|
|
495
491
|
if (filter.afterAt !== undefined)
|
|
496
492
|
add(`${alias}.at > ?`, finite(filter.afterAt));
|
|
497
|
-
if (filter.actorRequired)
|
|
498
|
-
terms.push(`${alias}.actor_json IS NOT NULL`);
|
|
499
493
|
for (const [column, values] of [['act_index', filter.indexes], ['kind', filter.kinds], [filter.actorMatch === 'exact' ? 'actor_json' : 'actor_key_json', filter.actors?.map(name => filter.actorMatch === 'exact' ? encode(name) : key(name))]]) {
|
|
500
494
|
if (values !== undefined) {
|
|
501
495
|
if (column === 'act_index')
|
|
@@ -540,7 +534,7 @@ export function createSqliteDriver(db) {
|
|
|
540
534
|
}
|
|
541
535
|
function directedHead(row, recipient) {
|
|
542
536
|
const h = head(row);
|
|
543
|
-
requireFact(h.kind === 'say'
|
|
537
|
+
requireFact(h.kind === 'say');
|
|
544
538
|
return immutable({
|
|
545
539
|
index: h.index, at: h.at, actor: h.actor, recipient, route: routeKind(row.route)
|
|
546
540
|
});
|
|
@@ -566,7 +560,7 @@ export function createSqliteDriver(db) {
|
|
|
566
560
|
const filter = activityIndex === undefined ? '' : ' AND act_index=?';
|
|
567
561
|
if (activityIndex !== undefined)
|
|
568
562
|
args.push(integer(Number(activityIndex.slice(4))));
|
|
569
|
-
return immutable(all(`SELECT * FROM delivery_evidence WHERE participant_key_json=? AND generation_json=?${filter} ORDER BY act_index,kind,attempt_n`, args).map(deliveryEvidence));
|
|
563
|
+
return immutable(all(`SELECT * FROM delivery_evidence WHERE participant_key_json=? AND generation_json=?${filter} ORDER BY act_index,kind,attempt_n`, args).map(row => deliveryEvidence(row)));
|
|
570
564
|
},
|
|
571
565
|
deliveryByToken(claimToken) {
|
|
572
566
|
const row = one('SELECT * FROM delivery_evidence WHERE claim_token_json=?', [encode(claimToken)]);
|
|
@@ -596,18 +590,6 @@ export function createSqliteDriver(db) {
|
|
|
596
590
|
roster: () => immutable(all('SELECT * FROM participants ORDER BY first_index').map(participant)),
|
|
597
591
|
attention: name => immutable(attentionRows('t.listener_key_json=?', [key(name)])),
|
|
598
592
|
incomingAttention: name => immutable(attentionRows('t.target_key_json=?', [key(name)])),
|
|
599
|
-
hold() {
|
|
600
|
-
const r = one("SELECT * FROM activities WHERE kind IN ('hold','resume') ORDER BY act_index DESC LIMIT 1");
|
|
601
|
-
if (!r)
|
|
602
|
-
return immutable({ active: false });
|
|
603
|
-
const a = activity(r);
|
|
604
|
-
if (a.kind === 'resume')
|
|
605
|
-
return immutable({ active: false });
|
|
606
|
-
requireFact(a.kind === 'hold');
|
|
607
|
-
return immutable({
|
|
608
|
-
active: true, at: a.at, ...(a.body === undefined ? {} : { reason: a.body }), ...(a.actor === undefined ? {} : { actor: a.actor })
|
|
609
|
-
});
|
|
610
|
-
},
|
|
611
593
|
activity(index) {
|
|
612
594
|
const r = one('SELECT * FROM activities WHERE act_index=?', [integer(index)]);
|
|
613
595
|
return r ? activity(r) : undefined;
|
|
@@ -685,7 +667,7 @@ export function createSqliteDriver(db) {
|
|
|
685
667
|
const groups = indices.map(r => {
|
|
686
668
|
const rows = all(`SELECT a.act_index,a.at,a.kind,a.actor_json,a.actor_key_json,a.reach,a.reply_index,p.name_json,d.route FROM ${pendingFrom} WHERE ${pendingWhere} AND d.act_index=? ORDER BY d.recipient_order`, [integer(r.act_index)]);
|
|
687
669
|
const h = head(rows[0]);
|
|
688
|
-
requireFact(h.kind === 'say'
|
|
670
|
+
requireFact(h.kind === 'say');
|
|
689
671
|
return {
|
|
690
672
|
index: h.index, at: h.at, actor: h.actor, recipients: rows.map(v => ({ recipient: decoded(v.name_json, true), route: routeKind(v.route) }))
|
|
691
673
|
};
|
|
@@ -788,7 +770,10 @@ export function createSqliteDriver(db) {
|
|
|
788
770
|
const a = validateStoredActivity(effect.activity);
|
|
789
771
|
requireFact(a.index >= read.header().nextActIndex && a.index < Number.MAX_SAFE_INTEGER, 'activity index cannot advance.');
|
|
790
772
|
const { index, at, kind, actor, reach, reply, ...payload } = a;
|
|
791
|
-
|
|
773
|
+
receive({
|
|
774
|
+
insertItem: () => { run('INSERT INTO activities(act_index,at,kind,actor_json,actor_key_json,reach,reply_index,payload_json) VALUES(?,?,?,?,?,?,?,?)', [index, at, kind, encode(actor), key(actor), reach ?? null, reply ?? null, encode(payload)]); },
|
|
775
|
+
insertAddress: (item, address, order) => { run('INSERT INTO landed_audience(act_index,recipient_key_json,recipient_order,route) VALUES(?,?,?,?)', [item, key(address.recipient), order, address.via]); },
|
|
776
|
+
}, { item: { key: index, data: a }, audience: effect.audience.map(r => ({ recipient: r.recipient, via: r.route })) });
|
|
792
777
|
if (effect.participant) {
|
|
793
778
|
const p = effect.participant;
|
|
794
779
|
string(p.name, true);
|
|
@@ -817,7 +802,6 @@ export function createSqliteDriver(db) {
|
|
|
817
802
|
requireFact(t.kind === 'listen' || t.kind === 'ignore');
|
|
818
803
|
run('INSERT INTO attention(listener_key_json,target_key_json,target_json,kind,since_index) VALUES(?,?,?,?,?) ON CONFLICT(listener_key_json,target_key_json) DO UPDATE SET target_json=excluded.target_json,kind=excluded.kind,since_index=excluded.since_index', [key(t.listener), key(t.target), encode(t.target), t.kind, t.sinceIndex]);
|
|
819
804
|
}
|
|
820
|
-
effect.audience.forEach((r, order) => run('INSERT INTO landed_audience(act_index,recipient_key_json,recipient_order,route) VALUES(?,?,?,?)', [index, key(r.recipient), order, r.route]));
|
|
821
805
|
run('UPDATE square_meta SET next_act_index=? WHERE id=1', [index + 1]);
|
|
822
806
|
changed = true;
|
|
823
807
|
},
|
|
@@ -825,11 +809,11 @@ export function createSqliteDriver(db) {
|
|
|
825
809
|
finite(at);
|
|
826
810
|
const next = read.header().nextActIndex;
|
|
827
811
|
indexes.forEach(index => requireFact(integer(index) < next, 'receipt references an unassigned activity index.'));
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
812
|
+
const canonical = owner(name);
|
|
813
|
+
string(canonical, true);
|
|
814
|
+
const count = recordConsumed({
|
|
815
|
+
insertReceipt: (ref, time) => run('INSERT OR IGNORE INTO receipts(owner_json,act_index,at) VALUES(?,?,?)', [encode(ref.recipient), ref.item, time]) > 0,
|
|
816
|
+
}, indexes.map(item => ({ recipient: canonical, item })), at);
|
|
833
817
|
if (count)
|
|
834
818
|
changed = true;
|
|
835
819
|
return count;
|
|
@@ -884,6 +868,40 @@ export function createSqliteDriver(db) {
|
|
|
884
868
|
return count > 0;
|
|
885
869
|
},
|
|
886
870
|
};
|
|
871
|
+
function attempt(row) {
|
|
872
|
+
const data = object(json(row.payload_json)), evidence = deliveryEvidence(row, data);
|
|
873
|
+
return { purpose: evidence.kind === 'wake' ? 'dispatch' : 'presentation', token: evidence.claimToken ?? '',
|
|
874
|
+
ordinal: evidence.attemptN, outcome: evidence.outcome, at: evidence.at, expiresAt: evidence.expiresAt, data };
|
|
875
|
+
}
|
|
876
|
+
function attemptRows(seat, index, kind, known) {
|
|
877
|
+
const scope = [key(seat.participant), encode(seat.generation), index, kind];
|
|
878
|
+
let sources = [];
|
|
879
|
+
return {
|
|
880
|
+
list() {
|
|
881
|
+
sources = all('SELECT * FROM delivery_evidence WHERE participant_key_json=? AND generation_json=? AND act_index=? AND kind=? ORDER BY attempt_n DESC', scope);
|
|
882
|
+
return sources.map(attempt);
|
|
883
|
+
},
|
|
884
|
+
evidence(value) {
|
|
885
|
+
const source = sources.find(row => row.attempt_n === value.ordinal);
|
|
886
|
+
return deliveryEvidence({ ...source, outcome: value.outcome, at: value.at, expires_at: value.expiresAt ?? null }, value.data);
|
|
887
|
+
},
|
|
888
|
+
byToken(token) {
|
|
889
|
+
if (known?.token === token)
|
|
890
|
+
return known;
|
|
891
|
+
const row = one('SELECT * FROM delivery_evidence WHERE participant_key_json=? AND generation_json=? AND act_index=? AND kind=? AND claim_token_json=?', [...scope, encode(token)]);
|
|
892
|
+
return row === undefined ? undefined : attempt(row);
|
|
893
|
+
},
|
|
894
|
+
insert(value) {
|
|
895
|
+
run('INSERT INTO delivery_evidence(participant_key_json,participant_json,generation_json,act_index,kind,attempt_n,outcome,at,expires_at,claim_token_json,payload_json) VALUES(?,?,?,?,?,?,?,?,?,?,?)', [scope[0], encode(seat.participant), scope[1], index, kind, value.ordinal, value.outcome, value.at, value.expiresAt ?? null, encode(value.token), encode(value.data)]);
|
|
896
|
+
deliveryChanged = true;
|
|
897
|
+
},
|
|
898
|
+
replace(value) {
|
|
899
|
+
const count = run('UPDATE delivery_evidence SET outcome=?,at=?,expires_at=?,payload_json=? WHERE participant_key_json=? AND generation_json=? AND act_index=? AND kind=? AND attempt_n=? AND claim_token_json IS ?', [value.outcome, value.at, value.expiresAt ?? null, encode(value.data), ...scope, value.ordinal, value.token === '' ? null : encode(value.token)]);
|
|
900
|
+
if (count)
|
|
901
|
+
deliveryChanged = true;
|
|
902
|
+
},
|
|
903
|
+
};
|
|
904
|
+
}
|
|
887
905
|
const delivery = {
|
|
888
906
|
claimDelivery(input) {
|
|
889
907
|
requireFact(input.kind === 'wake' || input.kind === 'presentation');
|
|
@@ -903,42 +921,24 @@ export function createSqliteDriver(db) {
|
|
|
903
921
|
const cancelledThrough = read.cancelledThrough(input.seat.participant, input.seat.generation);
|
|
904
922
|
if (cancelledThrough !== undefined && index <= cancelledThrough)
|
|
905
923
|
return { status: 'stale' };
|
|
906
|
-
const rows =
|
|
907
|
-
const
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
:
|
|
911
|
-
if (
|
|
912
|
-
return { status: '
|
|
913
|
-
const
|
|
914
|
-
|
|
915
|
-
return { status: 'busy', evidence: dispatching };
|
|
916
|
-
if (dispatching !== undefined) {
|
|
917
|
-
const updated = { ...dispatching, outcome: 'unknown', at: input.at, expiresAt: undefined, diagnostic: 'interrupted dispatch' };
|
|
918
|
-
const source = rows.find((row) => row.attempt_n === dispatching.attemptN);
|
|
919
|
-
run('UPDATE delivery_evidence SET outcome=?,at=?,expires_at=NULL,payload_json=? WHERE participant_key_json=? AND generation_json=? AND act_index=? AND kind=? AND attempt_n=?', ['unknown', input.at, encode({ ...object(json(source.payload_json)), diagnostic: 'interrupted dispatch' }), key(input.seat.participant), encode(input.seat.generation), index, input.kind, dispatching.attemptN]);
|
|
920
|
-
deliveryChanged = true;
|
|
921
|
-
return { status: 'terminal', evidence: updated };
|
|
922
|
-
}
|
|
923
|
-
const attemptN = (decoded[0]?.attemptN ?? -1) + 1;
|
|
924
|
-
const payload = input.route === undefined ? {} : { route: input.route };
|
|
925
|
-
const expiry = finite(input.at + leaseMs);
|
|
926
|
-
run('INSERT INTO delivery_evidence(participant_key_json,participant_json,generation_json,act_index,kind,attempt_n,outcome,at,expires_at,claim_token_json,payload_json) VALUES(?,?,?,?,?,?,?,?,?,?,?)', [key(input.seat.participant), encode(input.seat.participant), encode(input.seat.generation), index, input.kind, attemptN, 'dispatching', input.at, expiry, encode(input.claimToken), encode(payload)]);
|
|
927
|
-
deliveryChanged = true;
|
|
928
|
-
return { status: 'acquired', claimToken: input.claimToken, attemptN };
|
|
924
|
+
const rows = attemptRows(input.seat, index, input.kind);
|
|
925
|
+
const result = claimAttempt({ ...rows, replace(value) {
|
|
926
|
+
rows.replace({ ...value, data: { ...value.data, diagnostic: 'interrupted dispatch' } });
|
|
927
|
+
} }, { purpose: input.kind === 'wake' ? 'dispatch' : 'presentation', token: input.claimToken,
|
|
928
|
+
at: input.at, leaseMs, data: input.route === undefined ? {} : { route: input.route } });
|
|
929
|
+
if (result.status === 'acquired')
|
|
930
|
+
return { status: 'acquired', claimToken: result.token, attemptN: result.ordinal };
|
|
931
|
+
const evidence = result.interrupted ? { ...result.evidence, data: { ...result.evidence.data, diagnostic: 'interrupted dispatch' } } : result.evidence;
|
|
932
|
+
return { status: result.status, evidence: rows.evidence(evidence) };
|
|
929
933
|
},
|
|
930
934
|
prepareDelivery(input) {
|
|
931
935
|
const row = one('SELECT * FROM delivery_evidence WHERE participant_key_json=? AND generation_json=? AND claim_token_json=?', [key(input.seat.participant), encode(input.seat.generation), encode(input.claimToken)]);
|
|
932
936
|
if (row === undefined || row.outcome !== 'dispatching')
|
|
933
937
|
return false;
|
|
934
|
-
const correlation = nativeCorrelation(input.correlation);
|
|
935
|
-
const payload = object(json(row.payload_json));
|
|
938
|
+
const previous = attempt(row), correlation = nativeCorrelation(input.correlation), payload = previous.data;
|
|
936
939
|
if (stableJson(payload.correlation ?? null) === stableJson(correlation))
|
|
937
940
|
return true;
|
|
938
|
-
|
|
939
|
-
if (count)
|
|
940
|
-
deliveryChanged = true;
|
|
941
|
-
return count > 0;
|
|
941
|
+
return prepareAttempt(attemptRows(input.seat, integer(row.act_index), row.kind, previous), { token: input.claimToken, data: { ...payload, correlation } });
|
|
942
942
|
},
|
|
943
943
|
completeDelivery(input) {
|
|
944
944
|
const index = activityIndex(input.activity);
|
|
@@ -948,16 +948,9 @@ export function createSqliteDriver(db) {
|
|
|
948
948
|
const row = one('SELECT * FROM delivery_evidence WHERE participant_key_json=? AND generation_json=? AND act_index=? AND claim_token_json=?', [key(input.seat.participant), encode(input.seat.generation), index, encode(input.claimToken)]);
|
|
949
949
|
if (row === undefined)
|
|
950
950
|
return false;
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
return false;
|
|
955
|
-
const previous = object(json(row.payload_json));
|
|
956
|
-
const payload = { ...previous, ...(input.signature === undefined ? {} : { signature: input.signature }), ...(input.message === undefined ? {} : { message: input.message }), ...(input.diagnostic === undefined ? {} : { diagnostic: input.diagnostic }), ...(input.correlation === undefined ? {} : { correlation: input.correlation }) };
|
|
957
|
-
const count = run('UPDATE delivery_evidence SET outcome=?,at=?,expires_at=NULL,payload_json=? WHERE participant_key_json=? AND generation_json=? AND act_index=? AND claim_token_json=?', [input.outcome, input.at, encode(payload), key(input.seat.participant), encode(input.seat.generation), index, encode(input.claimToken)]);
|
|
958
|
-
if (count)
|
|
959
|
-
deliveryChanged = true;
|
|
960
|
-
return count > 0;
|
|
951
|
+
const previous = attempt(row);
|
|
952
|
+
const payload = { ...previous.data, ...(input.signature === undefined ? {} : { signature: input.signature }), ...(input.message === undefined ? {} : { message: input.message }), ...(input.diagnostic === undefined ? {} : { diagnostic: input.diagnostic }), ...(input.correlation === undefined ? {} : { correlation: input.correlation }) };
|
|
953
|
+
return settleAttempt(attemptRows(input.seat, index, row.kind, previous), { token: input.claimToken, outcome: input.outcome, at: input.at, data: payload });
|
|
961
954
|
},
|
|
962
955
|
observeNativeDelivery(input) {
|
|
963
956
|
const row = one('SELECT * FROM delivery_evidence WHERE participant_key_json=? AND generation_json=? AND claim_token_json=?', [key(input.seat.participant), encode(input.seat.generation), encode(input.claimToken)]);
|
|
@@ -1071,6 +1064,12 @@ function inspectRows(db, inspectSeat) {
|
|
|
1071
1064
|
const rebuilt = new MemoryDatabase(':memory:');
|
|
1072
1065
|
try {
|
|
1073
1066
|
initializeSqlite(rebuilt, state, d.header().revision);
|
|
1067
|
+
// A migration may remove the activity that established canonical casing.
|
|
1068
|
+
// Retained participant spelling stays authoritative while replay checks
|
|
1069
|
+
// every derived lifecycle/count fact against surviving activities.
|
|
1070
|
+
const retainName = rebuilt.prepare('UPDATE participants SET name_json=? WHERE name_key_json=?');
|
|
1071
|
+
for (const p of d.roster())
|
|
1072
|
+
retainName.run(encode(p.name), key(p.name));
|
|
1074
1073
|
for (const table of ['participants', 'attention', 'landed_audience']) {
|
|
1075
1074
|
const original = db.prepare(`SELECT * FROM ${table} ORDER BY 1,2`).all();
|
|
1076
1075
|
const expected = rebuilt.prepare(`SELECT * FROM ${table} ORDER BY 1,2`).all();
|
package/dist/square-tools.js
CHANGED
|
@@ -33,7 +33,7 @@ export const SQUARE_TOOLS = [
|
|
|
33
33
|
reply: activityId.optional().describe('The activity you are answering, like act/12. Its author hears you if still here.'),
|
|
34
34
|
noMention: z.boolean().optional().describe('Leave @names in the body as plain text. Mentions and reply still reach people.'),
|
|
35
35
|
force: z.boolean().optional().describe('Speak now, even if something reached you that you have not caught yet.'),
|
|
36
|
-
noWait: z.boolean().optional().describe('If
|
|
36
|
+
noWait: z.boolean().optional().describe('If you are speaking too fast, return at once instead of waiting.'),
|
|
37
37
|
}, async (square, { body, as, ...options }, control, identity) => (await selectOwnSeat(square, identity.owner, as)).express(body, { ...options, observe: true }, control), rendering((result) => renderExpression(result.observation?.location ?? '', result))),
|
|
38
38
|
tool('catch', 'Take in what reached you since you last looked: mentions, replies, bells, and words from those you listen to. What you catch no longer waits for you; history lets you look back.', {
|
|
39
39
|
as: participant,
|
|
@@ -64,16 +64,8 @@ export const SQUARE_TOOLS = [
|
|
|
64
64
|
const self = await selectOwnSeat(square, identity.owner, as);
|
|
65
65
|
return self[action](target, { observe: true }, control);
|
|
66
66
|
}, rendering((result, args) => renderListenerChange(result.observation?.location ?? '', args.action, result))),
|
|
67
|
-
tool('
|
|
68
|
-
|
|
69
|
-
reason: z.string().optional().describe('Why you are pausing. Everyone sees it.'),
|
|
70
|
-
release: z.boolean().optional().describe('true lowers the raised hand so the conversation goes on.'),
|
|
71
|
-
}, async (square, { as, reason, release }, control, identity) => {
|
|
72
|
-
const self = await selectOwnSeat(square, identity.owner, as);
|
|
73
|
-
return release === true ? self.resume({ observe: true }, control) : self.hold(reason, { observe: true }, control);
|
|
74
|
-
}, rendering((result, args) => renderLifecycleResult(result.observation?.location ?? '', args.release === true ? 'resume' : 'hold', result))),
|
|
75
|
-
tool('done', 'Step out of the square. Everyone sees you leave, and you stop listening to anyone. Leaving carries no words, so say goodbye with express first. Stay instead if someone may still need you; you can join again later.', { as: participant }, async (square, { as }, control, identity) => (await selectOwnSeat(square, identity.owner, as)).done(undefined, { observe: true }, control), rendering((result) => renderLifecycleResult(result.observation?.location ?? '', 'done', result))),
|
|
76
|
-
tool('status', 'Look around the square: who is here, whether a hand is raised, the latest activity, and who you listen to.', {
|
|
67
|
+
tool('done', 'Step out of the square. Everyone sees you leave, and you stop listening to anyone. Leaving carries no words, so say goodbye with express first. Stay instead if someone may still need you; you can join again later.', { as: participant }, async (square, { as }, control, identity) => (await selectOwnSeat(square, identity.owner, as)).done(undefined, { observe: true }, control), rendering((result) => renderLifecycleResult(result.observation?.location ?? '', result))),
|
|
68
|
+
tool('status', 'Look around the square: who is here, the latest activity, and who you listen to.', {
|
|
77
69
|
as: z.string().optional().describe('Whose listening to show. Leave it out if you hold exactly one name here.'),
|
|
78
70
|
}, async (square, { as }, control, identity) => {
|
|
79
71
|
const viewer = (await selectOwnSeat(square, identity.owner, as, { optional: true }))?.name;
|
package/dist/views.d.ts
CHANGED
|
@@ -4,8 +4,8 @@ import type { ActivityFilter, SquareRead } from './ports.js';
|
|
|
4
4
|
import type { Activity, ArchiveRecord, PerceivedActivity, HistoryQuery, HistoryContinuation, ParticipantsResult, SquareSnapshot, EntryPresentation, StatusResult, SquareObservation, CatchPresence } from './square-facade.js';
|
|
5
5
|
export declare const HISTORY_DEFAULT_LIMIT = 10;
|
|
6
6
|
export declare const HISTORY_MAX_LIMIT = 100;
|
|
7
|
-
export declare const NON_READ: readonly ["join", "done", "say", "
|
|
8
|
-
/** The one StoredAct-to-public-Activity field mapping. Rejects read
|
|
7
|
+
export declare const NON_READ: readonly ["join", "done", "say", "listen", "ignore", "unlisten"];
|
|
8
|
+
/** The one StoredAct-to-public-Activity field mapping. Rejects read bookkeeping. */
|
|
9
9
|
export declare function toPublicActivity(stored: StoredAct): Activity;
|
|
10
10
|
export declare function toPerceivedActivity(facts: SquareRead, stored: StoredAct, viewer: string): PerceivedActivity;
|
|
11
11
|
export declare function parseRequiredActivityId(id: ActivityId): number;
|
|
@@ -22,7 +22,6 @@ export declare function recoveryObservation(facts: SquareRead, name: string, now
|
|
|
22
22
|
participant: string;
|
|
23
23
|
activitySummaries: import("./square-facade.js").UnreadActivitySummary[];
|
|
24
24
|
standingCount: number;
|
|
25
|
-
held: boolean;
|
|
26
25
|
ownActivityCount: number;
|
|
27
26
|
hardCap: import("./model.js").HardCap;
|
|
28
27
|
};
|
package/dist/views.js
CHANGED
|
@@ -9,11 +9,11 @@ export const HISTORY_DEFAULT_LIMIT = 10;
|
|
|
9
9
|
export const HISTORY_MAX_LIMIT = 100;
|
|
10
10
|
const STATUS_HERE_WINDOW_MS = 30 * 60_000;
|
|
11
11
|
const PRESENCE_WINDOW_MS = 8 * 60 * 60_000;
|
|
12
|
-
export const NON_READ = ['join', 'done', 'say', '
|
|
13
|
-
const ARCHIVE = new Set(['join', 'done', 'say'
|
|
14
|
-
/** The one StoredAct-to-public-Activity field mapping. Rejects read
|
|
12
|
+
export const NON_READ = ['join', 'done', 'say', 'listen', 'ignore', 'unlisten'];
|
|
13
|
+
const ARCHIVE = new Set(['join', 'done', 'say']);
|
|
14
|
+
/** The one StoredAct-to-public-Activity field mapping. Rejects read bookkeeping. */
|
|
15
15
|
export function toPublicActivity(stored) {
|
|
16
|
-
if (stored.kind === 'read'
|
|
16
|
+
if (stored.kind === 'read')
|
|
17
17
|
throw new Error(`Cannot expose stored activity ${formatActivityId(stored.index)}`);
|
|
18
18
|
return { id: formatActivityId(stored.index), at: stored.at, kind: stored.kind, actor: stored.actor,
|
|
19
19
|
...(stored.kind === 'say' && stored.reach !== undefined ? { reach: stored.reach } : {}),
|
|
@@ -108,21 +108,20 @@ export function selectHistory(facts, query, limit, kinds, presentation = true) {
|
|
|
108
108
|
return { activities, visibleActivities, totalMatches, ...(continuation === undefined ? {} : { continuation }) };
|
|
109
109
|
}
|
|
110
110
|
export function publicActivities(activities) {
|
|
111
|
-
return activities.flatMap((activity) => activity.kind === 'read'
|
|
111
|
+
return activities.flatMap((activity) => activity.kind === 'read' ? [] : [toPublicActivity(activity)]);
|
|
112
112
|
}
|
|
113
113
|
export function snapshot(facts, now) {
|
|
114
|
-
const header = facts.header()
|
|
114
|
+
const header = facts.header();
|
|
115
115
|
const names = new Map(facts.roster().map((p) => [nameKey(p.name), p.name]));
|
|
116
116
|
const receipts = new Set(facts.receiptAssignments().map((r) => JSON.stringify([r.owner, r.index])));
|
|
117
117
|
return { context: [...header.preamble, ...header.warmup].join('\n'), actCount: facts.activityCount({ kinds: NON_READ }), hardCap: header.hardCap,
|
|
118
118
|
...(header.throttlePerMinute === undefined ? {} : { throttlePerMinute: header.throttlePerMinute }),
|
|
119
|
-
held: hold.active && hold.actor !== undefined ? { by: hold.actor, ...(hold.reason === undefined ? {} : { reason: hold.reason }) } : null,
|
|
120
119
|
participants: coreParticipants(facts, now).filter((p) => p.presence !== 'never-joined').map(({ name, presence, listening }) => ({ name, presence, listening })),
|
|
121
120
|
delivered(name, id) { return receipts.has(JSON.stringify([names.get(nameKey(name)) ?? name, parseRequiredActivityId(id)])); } };
|
|
122
121
|
}
|
|
123
122
|
export function recoveryObservation(facts, name, now) {
|
|
124
|
-
const known = resolveKnownName(facts, name)
|
|
125
|
-
return { participant: known, activitySummaries: unreadActivitySummaries(facts, known, now), standingCount: inSquareCount(facts),
|
|
123
|
+
const known = resolveKnownName(facts, name);
|
|
124
|
+
return { participant: known, activitySummaries: unreadActivitySummaries(facts, known, now), standingCount: inSquareCount(facts),
|
|
126
125
|
ownActivityCount: facts.participant(known).sayCount, hardCap: facts.header().hardCap };
|
|
127
126
|
}
|
|
128
127
|
export function entryPresentation(facts, name, lastN = HISTORY_DEFAULT_LIMIT) {
|
|
@@ -146,7 +145,7 @@ function classifyPresence(participants, now) {
|
|
|
146
145
|
export function queryParticipants(facts, context, observe = false) {
|
|
147
146
|
const participants = coreParticipants(facts, context.now);
|
|
148
147
|
return { participants, ...(!observe ? {} : { observation: { ...context, ...classifyPresence(participants, context.now),
|
|
149
|
-
standingCount: inSquareCount(facts)
|
|
148
|
+
standingCount: inSquareCount(facts) } }) };
|
|
150
149
|
}
|
|
151
150
|
export function listPresentation(facts) {
|
|
152
151
|
const participants = facts.roster().filter((p) => p.joined).map((p) => {
|
|
@@ -157,21 +156,16 @@ export function listPresentation(facts) {
|
|
|
157
156
|
}
|
|
158
157
|
export function queryStatus(facts, context, viewer = '', observe = false) {
|
|
159
158
|
const participants = coreParticipants(facts, context.now);
|
|
160
|
-
const header = facts.header(),
|
|
159
|
+
const header = facts.header(), standingCount = inSquareCount(facts);
|
|
161
160
|
const listener = participants.find((p) => p.presence !== 'never-joined' && nameKey(p.name) === nameKey(viewer));
|
|
162
161
|
const latest = facts.activityHeads({ kinds: ['say', 'done'] }, { edge: 'newest', limit: 1 }).items[0];
|
|
163
162
|
const latestAct = latest === undefined ? undefined : facts.activity(latest.index);
|
|
164
163
|
const data = {
|
|
165
164
|
hardCap: header.hardCap,
|
|
166
165
|
...(header.throttlePerMinute === undefined ? {} : { throttlePerMinute: header.throttlePerMinute }),
|
|
167
|
-
held: hold.active ? {
|
|
168
|
-
...(hold.actor === undefined ? {} : { by: hold.actor }),
|
|
169
|
-
...(hold.reason === undefined ? {} : { reason: hold.reason }),
|
|
170
|
-
...(hold.at === undefined ? {} : { at: hold.at }),
|
|
171
|
-
} : null,
|
|
172
166
|
...(listener === undefined ? {} : { listening: listener.listening }),
|
|
173
167
|
participants: participants.filter((p) => p.presence !== 'never-joined'), now: context.now,
|
|
174
|
-
...(latestAct === undefined || latestAct.
|
|
168
|
+
...(latestAct === undefined || latestAct.kind === 'read' ? {} : { latestActivity: toPublicActivity(latestAct) }),
|
|
175
169
|
};
|
|
176
170
|
if (!observe)
|
|
177
171
|
return data;
|
|
@@ -200,7 +194,7 @@ export function catchPresence(facts, now) {
|
|
|
200
194
|
return { participants, now };
|
|
201
195
|
}
|
|
202
196
|
export function streamProjection(facts, cursor, recipient) {
|
|
203
|
-
const page = facts.activityHeads({ afterIndex: cursor, kinds: NON_READ
|
|
197
|
+
const page = facts.activityHeads({ afterIndex: cursor, kinds: NON_READ }, { edge: 'oldest', limit: PAGE_SIZE });
|
|
204
198
|
const heads = recipient === undefined ? page.items : page.items.filter((head) => facts.directedAt(recipient, head.index) !== undefined);
|
|
205
199
|
const activities = facts.activities(heads.map((head) => head.index)).map((activity) => recipient === undefined
|
|
206
200
|
? toPublicActivity(activity) : toPerceivedActivity(facts, activity, recipient));
|
|
@@ -210,7 +204,7 @@ export function streamTailProjection(facts, last = HISTORY_DEFAULT_LIMIT, recipi
|
|
|
210
204
|
if (!Number.isSafeInteger(last) || last < 0 || last > PAGE_SIZE)
|
|
211
205
|
throw new SquareError('invalid_args', `Invalid stream tail: expected a non-negative safe integer no greater than ${PAGE_SIZE}.`);
|
|
212
206
|
const heads = last === 0 ? [] : recipient === undefined
|
|
213
|
-
? facts.activityHeads({ kinds: NON_READ
|
|
207
|
+
? facts.activityHeads({ kinds: NON_READ }, { edge: 'newest', limit: last }).items
|
|
214
208
|
: facts.directed(recipient, {}, { edge: 'newest', limit: last }).items;
|
|
215
209
|
const activities = facts.activities(heads.map((head) => head.index)).map((activity) => recipient === undefined
|
|
216
210
|
? toPublicActivity(activity) : toPerceivedActivity(facts, activity, recipient));
|
package/dist/wakes.d.ts
CHANGED
package/dist/wakes.js
CHANGED
|
@@ -21,15 +21,13 @@ export async function pulseWatchLease(artifact, seat, at, leaseId, options, hear
|
|
|
21
21
|
const known = resolveKnownName(facts, name), lease = freshWatchLease(facts, known, at);
|
|
22
22
|
if (lease?.leaseId !== leaseId)
|
|
23
23
|
return { type: 'replaced' };
|
|
24
|
-
const
|
|
25
|
-
const terminal = held ? undefined : watchTerminalStatus(facts, known);
|
|
24
|
+
const terminal = watchTerminalStatus(facts, known);
|
|
26
25
|
if (terminal !== undefined)
|
|
27
26
|
return { type: 'terminal', status: terminal };
|
|
28
|
-
const type = held ? 'held' : 'sleep';
|
|
29
27
|
if (!heartbeatDue)
|
|
30
|
-
return { type };
|
|
28
|
+
return { type: 'sleep' };
|
|
31
29
|
facts.putLease(known, newLease(leaseId, at, options), seat.generation);
|
|
32
|
-
return { type, heartbeatAt: at };
|
|
30
|
+
return { type: 'sleep', heartbeatAt: at };
|
|
33
31
|
}, signal);
|
|
34
32
|
return value;
|
|
35
33
|
}
|