@astrosheep/square 0.3.12 → 0.3.13
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/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/codex-plugin/hooks/hooks.json +9 -0
- package/dist/activity-feed.d.ts +14 -0
- package/dist/activity-feed.js +5 -5
- package/dist/activity.d.ts +9 -0
- package/dist/activity.js +111 -90
- package/dist/artifact.d.ts +20 -0
- package/dist/artifact.js +10 -29
- package/dist/automatic-session.d.ts +5 -0
- package/dist/automatic-session.js +86 -0
- package/dist/boundary-presentation.d.ts +5 -0
- package/dist/boundary-presentation.js +5 -4
- package/dist/claude-hook.d.ts +9 -0
- package/dist/claude-hook.js +35 -3
- package/dist/cli/context.d.ts +35 -0
- package/dist/cli/context.js +25 -30
- package/dist/cli/harness-command.d.ts +28 -0
- package/dist/cli/harness-command.js +29 -14
- package/dist/cli/maintenance-commands.d.ts +7 -0
- package/dist/cli/maintenance-commands.js +3 -3
- package/dist/cli/meta-commands.d.ts +5 -0
- package/dist/cli/observation-commands.d.ts +20 -0
- package/dist/cli/observation-commands.js +155 -128
- package/dist/cli/program.d.ts +2 -0
- package/dist/cli/program.js +2 -12
- package/dist/cli/registry.d.ts +9 -0
- package/dist/cli/registry.js +1 -2
- package/dist/cli/square-commands.d.ts +34 -0
- package/dist/cli/square-commands.js +81 -74
- package/dist/cmd/notify-once.d.ts +2 -0
- package/dist/codex-hook.d.ts +10 -0
- package/dist/codex-hook.js +35 -3
- package/dist/decisions.d.ts +92 -0
- package/dist/decisions.js +83 -77
- package/dist/delivery-health.d.ts +21 -0
- package/dist/delivery-health.js +40 -34
- package/dist/delivery.d.ts +81 -0
- package/dist/delivery.js +60 -22
- package/dist/file-lock.d.ts +6 -0
- package/dist/harness-claude.d.ts +19 -0
- package/dist/harness-claude.js +8 -3
- package/dist/harness-codex.d.ts +29 -0
- package/dist/harness-codex.js +2 -2
- package/dist/harness-links.d.ts +17 -0
- package/dist/harness-links.js +2 -2
- package/dist/harness-pi.d.ts +11 -0
- package/dist/harness-pi.js +2 -2
- package/dist/harness-stage.d.ts +6 -0
- package/dist/harness.d.ts +20 -0
- package/dist/harness.js +5 -3
- package/dist/help.d.ts +6 -0
- package/dist/help.js +6 -5
- package/dist/identity.d.ts +13 -0
- package/dist/inbox.d.ts +2 -0
- package/dist/inbox.js +15 -19
- package/dist/index.d.ts +13 -0
- package/dist/index.js +31 -69
- package/dist/landing.d.ts +10 -0
- package/dist/landing.js +95 -0
- package/dist/list.d.ts +1 -0
- package/dist/list.js +40 -18
- package/dist/model.d.ts +147 -0
- package/dist/model.js +12 -2
- package/dist/notifications.d.ts +30 -0
- package/dist/notifications.js +75 -91
- package/dist/open-square.d.ts +11 -0
- package/dist/open-square.js +4 -0
- package/dist/participant-identity.d.ts +1 -0
- package/dist/participant-identity.js +3 -0
- package/dist/paseo-connection.d.ts +15 -0
- package/dist/paseo-delivery.d.ts +14 -0
- package/dist/paseo-state.d.ts +11 -0
- package/dist/paseo-timeline.d.ts +16 -0
- package/dist/presence.d.ts +3 -0
- package/dist/presence.js +42 -0
- package/dist/presentation.d.ts +91 -0
- package/dist/presentation.js +33 -29
- package/dist/presented.d.ts +11 -0
- package/dist/registry.d.ts +58 -0
- package/dist/registry.js +36 -3
- package/dist/routes.d.ts +42 -0
- package/dist/runtime.d.ts +45 -0
- package/dist/runtime.js +42 -34
- package/dist/search.d.ts +11 -0
- package/dist/square-core.d.ts +111 -0
- package/dist/square-core.js +7 -0
- package/dist/square-facade.d.ts +86 -0
- package/dist/square-facade.js +1 -0
- package/dist/square-file-adapter.d.ts +18 -0
- package/dist/square-file-adapter.js +70 -0
- package/dist/square-storage.d.ts +20 -0
- package/dist/square-storage.js +171 -0
- package/dist/square-wiring.d.ts +19 -0
- package/dist/square-wiring.js +70 -0
- package/dist/square.d.ts +2 -0
- package/dist/state-cell.d.ts +14 -0
- package/dist/state-cell.js +1 -0
- package/dist/stream.d.ts +3 -0
- package/dist/stream.js +19 -23
- package/dist/time.d.ts +6 -0
- package/dist/views.d.ts +95 -0
- package/dist/views.js +81 -0
- package/dist/wake-attempts.d.ts +39 -0
- package/dist/wake-evidence.d.ts +12 -0
- package/dist/wake-evidence.js +7 -10
- package/dist/wake-port.d.ts +20 -0
- package/dist/wake-sink.d.ts +12 -0
- package/dist/wakes.d.ts +40 -0
- package/dist/wakes.js +62 -0
- package/dist/watch.d.ts +2 -0
- package/dist/watch.js +89 -210
- package/extensions/square-opencode.js +27 -1
- package/extensions/square-pi.js +13 -0
- package/package.json +5 -2
- package/skills/brainstorm/SKILL.md +26 -26
- package/skills/square/.claude-plugin/plugin.json +1 -1
- package/skills/square/SKILL.md +47 -29
- package/skills/square/hooks/hooks.json +9 -0
- package/skills/square-feedback/SKILL.md +2 -2
- package/dist/compact.js +0 -26
- package/dist/square-application.js +0 -193
package/dist/presentation.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { sameName } from './model.js';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import { participantIdentity } from './participant-identity.js';
|
|
5
|
+
export { participantIdentity } from './participant-identity.js';
|
|
4
6
|
import { audienceIncludes, audienceOf, perceive } from './square-core.js';
|
|
5
7
|
import { actId, publicActs, readCursor, rosterNames, sayNumberFor } from './runtime.js';
|
|
6
8
|
import { formatDuration, formatRelativeTime, formatTimestamp } from './time.js';
|
|
@@ -49,7 +51,7 @@ function pluralize(count, singular, plural = `${singular}s`) {
|
|
|
49
51
|
const PRESENCE_WINDOW_MS = 8 * 60 * 60 * 1000;
|
|
50
52
|
function presenceGlyph(participant) {
|
|
51
53
|
if (participant.state === 'done')
|
|
52
|
-
return '
|
|
54
|
+
return '○';
|
|
53
55
|
if (participant.presence === 'watching')
|
|
54
56
|
return '◎';
|
|
55
57
|
if (participant.presenceAt !== undefined)
|
|
@@ -77,7 +79,7 @@ export function renderPresenceLines(participants, now, limit = 5) {
|
|
|
77
79
|
const shown = recent.slice(0, limit);
|
|
78
80
|
if (shown.length === 0)
|
|
79
81
|
return [' ○ nobody nearby'];
|
|
80
|
-
const lines = shown.map((p) => ` ${presenceGlyph(p)} ${p.name} · ${presenceText(p, now)}`);
|
|
82
|
+
const lines = shown.map((p) => ` ${presenceGlyph(p)} ${participantIdentity(p.name)} · ${presenceText(p, now)}`);
|
|
81
83
|
const remaining = recent.length - shown.length;
|
|
82
84
|
if (remaining > 0)
|
|
83
85
|
lines.push(` ○ …and ${remaining} more`);
|
|
@@ -116,15 +118,16 @@ export function previewActivityBody(body) {
|
|
|
116
118
|
}
|
|
117
119
|
export function renderRoomChangeText(event) {
|
|
118
120
|
const actor = event.actor ?? 'someone';
|
|
121
|
+
const identity = actor === 'someone' ? actor : participantIdentity(actor);
|
|
119
122
|
switch (event.kind) {
|
|
120
123
|
case 'join':
|
|
121
|
-
return `${
|
|
124
|
+
return `${identity} stepped into the square`;
|
|
122
125
|
case 'done':
|
|
123
|
-
return `${
|
|
126
|
+
return `${identity} stepped out of the square`;
|
|
124
127
|
case 'hold':
|
|
125
|
-
return `${
|
|
128
|
+
return `${identity} raised a hand${event.body ? ` — ${event.body}` : ''}`;
|
|
126
129
|
case 'resume':
|
|
127
|
-
return `${
|
|
130
|
+
return `${identity} lowered the hand`;
|
|
128
131
|
}
|
|
129
132
|
}
|
|
130
133
|
function renderedBody(body, maxChars) {
|
|
@@ -151,31 +154,31 @@ export function renderEventCli(event, opts = {}) {
|
|
|
151
154
|
const body = renderedBody(event.body, maxBody);
|
|
152
155
|
const mention = opts.mention;
|
|
153
156
|
const mentionSuffix = mention !== undefined && audienceIncludes(audienceOf(event), mention)
|
|
154
|
-
? ` · calls your name across the square —
|
|
157
|
+
? ` · calls your name across the square — ${participantIdentity(mention)}`
|
|
155
158
|
: '';
|
|
156
159
|
const replySuffix = event.reply === undefined ? '' : ` · replies to ${actId(event.reply)}`;
|
|
157
|
-
return `● ${event.actor} #${opts.actNumber ?? 1} · ${actId(event)} · ${formatRelativeTime(event.at, now)}${mentionSuffix}${replySuffix}${bodySuffix(body)}`;
|
|
160
|
+
return `● ${participantIdentity(event.actor)} #${opts.actNumber ?? 1} · ${actId(event)} · ${formatRelativeTime(event.at, now)}${mentionSuffix}${replySuffix}${bodySuffix(body)}`;
|
|
158
161
|
}
|
|
159
162
|
case 'done': {
|
|
160
163
|
const body = renderedBody(event.body, maxBody);
|
|
161
|
-
return
|
|
164
|
+
return `○ ${participantIdentity(event.actor)} stepped out of the square — done · ${actId(event)} · ${formatRelativeTime(event.at, now)}${bodySuffix(body)}`;
|
|
162
165
|
}
|
|
163
166
|
case 'read':
|
|
164
167
|
return '';
|
|
165
168
|
}
|
|
166
169
|
}
|
|
167
|
-
function renderPresenceOnlySay(event) {
|
|
170
|
+
function renderPresenceOnlySay(event, opts = {}) {
|
|
168
171
|
const audience = audienceOf(event);
|
|
169
172
|
const targets = audience.kind === 'bell' ? [] : audience.names;
|
|
170
|
-
const dest = targets.length === 0 ? '' : `
|
|
171
|
-
return
|
|
173
|
+
const dest = targets.length === 0 ? '' : ` ${targets.map((name) => participantIdentity(name)).join(' and ')}`;
|
|
174
|
+
return `● ${participantIdentity(event.actor)} #${opts.actNumber ?? 1} · ${actId(event)} · ${formatRelativeTime(event.at, opts.now)}\n talked to${dest}`;
|
|
172
175
|
}
|
|
173
176
|
export function renderAmbientEvent(event, viewer, opts = {}) {
|
|
174
177
|
if (event.kind !== 'say')
|
|
175
178
|
return renderEventCli(event, opts);
|
|
176
179
|
const seen = perceive(event, viewer);
|
|
177
180
|
if (seen === 'presence')
|
|
178
|
-
return renderPresenceOnlySay(event);
|
|
181
|
+
return renderPresenceOnlySay(event, opts);
|
|
179
182
|
return renderEventCli(event, opts);
|
|
180
183
|
}
|
|
181
184
|
function draftSavedLines(draftPath) {
|
|
@@ -190,10 +193,11 @@ function renderUnreadSummary(opts) {
|
|
|
190
193
|
...item.previews.slice(-1).map((preview) => {
|
|
191
194
|
const rendered = renderAmbientEvent(preview.act, opts.viewer, { actNumber: preview.number });
|
|
192
195
|
if (rendered === '')
|
|
193
|
-
return ` · ${item.name} spoke — ${formatAge(item.latestActivityAgeMs)} ago`;
|
|
194
|
-
if (
|
|
195
|
-
return ` · ${item.name} spoke — ${formatAge(item.latestActivityAgeMs)} ago · ${rendered}`;
|
|
196
|
-
|
|
196
|
+
return ` · ${participantIdentity(item.name)} spoke — ${formatAge(item.latestActivityAgeMs)} ago`;
|
|
197
|
+
if (preview.act.kind === 'say' && perceive(preview.act, opts.viewer) === 'presence') {
|
|
198
|
+
return ` · ${participantIdentity(item.name)} spoke — ${formatAge(item.latestActivityAgeMs)} ago · ${rendered.replace(/\n/g, ' ')}`;
|
|
199
|
+
}
|
|
200
|
+
return ` · ${participantIdentity(item.name)} spoke — ${formatAge(item.latestActivityAgeMs)} ago · "${previewActivityBody(preview.act.body)}"`;
|
|
197
201
|
}),
|
|
198
202
|
]),
|
|
199
203
|
...opts.roomChanges.map((act) => ` · ${renderRoomChangeText(act)}`),
|
|
@@ -260,10 +264,10 @@ export function renderPublicTail(events, lastN, now, viewer = '') {
|
|
|
260
264
|
.filter(Boolean)
|
|
261
265
|
.join('\n\n');
|
|
262
266
|
}
|
|
263
|
-
function lastPresenceAnchor(
|
|
264
|
-
const cursor = readCursor(
|
|
265
|
-
for (let i =
|
|
266
|
-
const event =
|
|
267
|
+
function lastPresenceAnchor(squareState, name) {
|
|
268
|
+
const cursor = readCursor(squareState, name);
|
|
269
|
+
for (let i = squareState.acts.length - 1; i >= 0; i--) {
|
|
270
|
+
const event = squareState.acts[i];
|
|
267
271
|
const index = event.index;
|
|
268
272
|
if (index > cursor)
|
|
269
273
|
continue;
|
|
@@ -273,15 +277,15 @@ function lastPresenceAnchor(doc, name) {
|
|
|
273
277
|
return -1;
|
|
274
278
|
}
|
|
275
279
|
function renderLastPresenceMarker(name) {
|
|
276
|
-
return
|
|
280
|
+
return `→ ${participantIdentity(name)} was here`;
|
|
277
281
|
}
|
|
278
|
-
export function renderActivitiesView(
|
|
282
|
+
export function renderActivitiesView(squareState, visible, lastN, full, squarePath, viewer = '', mode = 'ambient') {
|
|
279
283
|
const publicVisible = visible.filter((act) => act.kind === 'say' || act.kind === 'done');
|
|
280
284
|
const shown = lastN == null ? publicVisible : publicVisible.slice(-lastN);
|
|
281
285
|
const previewLen = full ? undefined : BODY_PREVIEW_LENGTH;
|
|
282
286
|
const markers = new Map();
|
|
283
|
-
for (const participant of rosterNames(
|
|
284
|
-
const anchor = lastPresenceAnchor(
|
|
287
|
+
for (const participant of rosterNames(squareState)) {
|
|
288
|
+
const anchor = lastPresenceAnchor(squareState, participant);
|
|
285
289
|
if (anchor >= 0)
|
|
286
290
|
markers.set(anchor, [...(markers.get(anchor) ?? []), participant]);
|
|
287
291
|
}
|
|
@@ -289,7 +293,7 @@ export function renderActivitiesView(doc, visible, lastN, full, squarePath, view
|
|
|
289
293
|
for (const act of shown) {
|
|
290
294
|
const opts = {
|
|
291
295
|
preview: previewLen,
|
|
292
|
-
actNumber: act.kind === 'say' ? sayNumberFor(
|
|
296
|
+
actNumber: act.kind === 'say' ? sayNumberFor(squareState.acts, act) : undefined,
|
|
293
297
|
};
|
|
294
298
|
const rendered = mode === 'archive'
|
|
295
299
|
? renderEventCli(act, opts)
|
|
@@ -326,13 +330,13 @@ export function renderGrepActivitiesView(visible, totalMatches, full, squarePath
|
|
|
326
330
|
const rawBody = act.body ?? '';
|
|
327
331
|
if (full === true) {
|
|
328
332
|
const body = rawBody.split('\n').map((line) => ` ${line}`).join('\n');
|
|
329
|
-
chunks.push(`${actId(act.index)} · ${act.actor
|
|
333
|
+
chunks.push(`${actId(act.index)} · ${act.actor === undefined ? 'unknown' : participantIdentity(act.actor)} · ${formatTimestamp(act.at)}\n${body}`);
|
|
330
334
|
continue;
|
|
331
335
|
}
|
|
332
336
|
const snippet = grepSnippet(rawBody, pattern, GREP_PREVIEW_CHARS, fixed);
|
|
333
337
|
if (snippet === undefined) {
|
|
334
338
|
const preview = previewBody(rawBody, GREP_PREVIEW_CHARS);
|
|
335
|
-
chunks.push(`${actId(act.index)} · ${act.actor
|
|
339
|
+
chunks.push(`${actId(act.index)} · ${act.actor === undefined ? 'unknown' : participantIdentity(act.actor)} · ${formatTimestamp(act.at)}${preview === '' ? '' : `\n ${preview}`}`);
|
|
336
340
|
continue;
|
|
337
341
|
}
|
|
338
342
|
const clippedBefore = snippet.beforeOmitted > 0;
|
|
@@ -342,7 +346,7 @@ export function renderGrepActivitiesView(visible, totalMatches, full, squarePath
|
|
|
342
346
|
const omitted = clippedBefore || clippedAfter
|
|
343
347
|
? `\n · ${snippet.beforeOmitted} chars before · ${snippet.afterOmitted} chars after`
|
|
344
348
|
: '';
|
|
345
|
-
chunks.push(`${actId(act.index)} · ${act.actor
|
|
349
|
+
chunks.push(`${actId(act.index)} · ${act.actor === undefined ? 'unknown' : participantIdentity(act.actor)} · ${formatTimestamp(act.at)}\n ${text.trim()}${omitted}`);
|
|
346
350
|
}
|
|
347
351
|
if (publicVisible.length === 1) {
|
|
348
352
|
chunks.push(`» ${commandPrefix(squarePath)} history --at ${actId(publicVisible[0].index)} -C 2${truncated ? ' --full' : ''}`);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type InboxMembership } from './model.js';
|
|
2
|
+
export declare function presentedPath(env?: NodeJS.ProcessEnv): string;
|
|
3
|
+
export declare function hasPresentedForOwner(ownerId: string, squarePath: string, name: string, actIndex: number, env?: NodeJS.ProcessEnv, now?: number): boolean;
|
|
4
|
+
/** True when any current participant owner has already received this attention. */
|
|
5
|
+
export declare function hasPresentedAttention(squarePath: string, name: string, actIndex: number, env?: NodeJS.ProcessEnv, now?: number): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Serialize presentation only for the affected participants. Delivery runs
|
|
8
|
+
* outside the short ledger-write lock, so unrelated owners never wait on an
|
|
9
|
+
* adapter. A throwing callback leaves no row and remains unpresented.
|
|
10
|
+
*/
|
|
11
|
+
export declare function presentOnce<T>(sessionId: string, lookup: (sessionId: string) => InboxMembership[], deliver: (inbox: InboxMembership[]) => T, env?: NodeJS.ProcessEnv, at?: number): T | undefined;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Machine-local participant discovery cache.
|
|
3
|
+
*
|
|
4
|
+
* The Square artifact remains authoritative for membership. This append-only
|
|
5
|
+
* cache only maps native harness sessions and optional Paseo agent ids back to
|
|
6
|
+
* active (square path, participant name) pairs.
|
|
7
|
+
*/
|
|
8
|
+
import { type StoredAct } from './model.js';
|
|
9
|
+
export type SessionChannel = 'claude-code' | 'codex' | 'opencode' | 'pi' | 'paseo' | 'unknown';
|
|
10
|
+
export interface RegistryBinding {
|
|
11
|
+
sessionId: string;
|
|
12
|
+
name: string;
|
|
13
|
+
squarePath: string;
|
|
14
|
+
channel: SessionChannel;
|
|
15
|
+
child: boolean;
|
|
16
|
+
paseoAgentId?: string;
|
|
17
|
+
ownerId: string;
|
|
18
|
+
updatedAt: number;
|
|
19
|
+
}
|
|
20
|
+
export interface RegistryWriteOptions {
|
|
21
|
+
channel?: SessionChannel;
|
|
22
|
+
child?: boolean;
|
|
23
|
+
paseoAgentId?: string;
|
|
24
|
+
ownerId?: string;
|
|
25
|
+
at?: number;
|
|
26
|
+
}
|
|
27
|
+
export declare function registryPath(): string;
|
|
28
|
+
export declare function canonicalSquarePath(squarePath: string): string;
|
|
29
|
+
export declare function recordJoin(sessionId: string, name: string, squarePath: string, options?: RegistryWriteOptions): void;
|
|
30
|
+
export declare function recordDone(sessionId: string, name: string, squarePath: string, options?: RegistryWriteOptions): void;
|
|
31
|
+
export declare function lookupSessionBindings(sessionId: string, now?: number): RegistryBinding[];
|
|
32
|
+
export declare function lookupSession(sessionId: string, now?: number): Array<{
|
|
33
|
+
name: string;
|
|
34
|
+
squarePath: string;
|
|
35
|
+
}>;
|
|
36
|
+
export declare function lookupParticipant(squarePath: string, name: string, now?: number): RegistryBinding[];
|
|
37
|
+
/** Resolve the current local harness owner for a participant, if one is registered. */
|
|
38
|
+
export declare function localParticipantOwner(squarePath: string, name: string, env?: NodeJS.ProcessEnv, now?: number): string | undefined;
|
|
39
|
+
export declare function localParticipantName(squarePath: string, env?: NodeJS.ProcessEnv): string | undefined;
|
|
40
|
+
export interface RegistryPruneResult {
|
|
41
|
+
removed: number;
|
|
42
|
+
kept: number;
|
|
43
|
+
}
|
|
44
|
+
/** Compact the registry and remove only bindings disproved by their authoritative artifact. */
|
|
45
|
+
export declare function pruneRegistry(readActs: (squarePath: string) => StoredAct[] | undefined, now?: number): RegistryPruneResult;
|
|
46
|
+
export interface LocalSessionIdentity {
|
|
47
|
+
sessionId: string;
|
|
48
|
+
channel: SessionChannel;
|
|
49
|
+
child: boolean;
|
|
50
|
+
paseoAgentId?: string;
|
|
51
|
+
}
|
|
52
|
+
export declare function localSessionIdentities(env?: NodeJS.ProcessEnv): LocalSessionIdentity[];
|
|
53
|
+
/** True when this process belongs to a harness that can deliver Square attention without a foreground catch. */
|
|
54
|
+
export declare function hasAutomaticDeliveryIdentity(env?: NodeJS.ProcessEnv): boolean;
|
|
55
|
+
export declare function recordLocalJoin(name: string, squarePath: string, env?: NodeJS.ProcessEnv): void;
|
|
56
|
+
export declare function recordLocalDone(name: string, squarePath: string, env?: NodeJS.ProcessEnv): void;
|
|
57
|
+
export declare function recordSessionJoin(sessionId: string, name: string, squarePath: string, channel: SessionChannel, env?: NodeJS.ProcessEnv): string;
|
|
58
|
+
export declare function recordSessionDone(sessionId: string, name: string, squarePath: string, channel: SessionChannel, env?: NodeJS.ProcessEnv): boolean;
|
package/dist/registry.js
CHANGED
|
@@ -36,8 +36,8 @@ export function canonicalSquarePath(squarePath) {
|
|
|
36
36
|
return absolute;
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
function bindingKey(sessionId, squarePath, name) {
|
|
40
|
-
return JSON.stringify([sessionId, canonicalSquarePath(squarePath), nameKey(name)]);
|
|
39
|
+
function bindingKey(sessionId, squarePath, name, channel) {
|
|
40
|
+
return JSON.stringify([sessionId, canonicalSquarePath(squarePath), nameKey(name), channel]);
|
|
41
41
|
}
|
|
42
42
|
function participantKey(squarePath, name) {
|
|
43
43
|
return JSON.stringify([canonicalSquarePath(squarePath), nameKey(name)]);
|
|
@@ -94,7 +94,7 @@ function foldRegistry(raw, now) {
|
|
|
94
94
|
continue;
|
|
95
95
|
order++;
|
|
96
96
|
const ownerId = entry.owner_id ?? `legacy:${order}`;
|
|
97
|
-
state.set(bindingKey(entry.session_id, entry.square_path, entry.name), {
|
|
97
|
+
state.set(bindingKey(entry.session_id, entry.square_path, entry.name, entry.channel), {
|
|
98
98
|
entry,
|
|
99
99
|
updatedAt: Date.parse(entry.ts),
|
|
100
100
|
ownerId,
|
|
@@ -231,6 +231,11 @@ export function localParticipantOwner(squarePath, name, env = process.env, now =
|
|
|
231
231
|
return undefined;
|
|
232
232
|
return lookupParticipant(squarePath, name, now).find((binding) => sessionIds.has(binding.sessionId))?.ownerId;
|
|
233
233
|
}
|
|
234
|
+
export function localParticipantName(squarePath, env = process.env) {
|
|
235
|
+
const identities = localSessionIdentities(env);
|
|
236
|
+
const names = new Set(identities.flatMap((identity) => lookupSession(identity.sessionId).filter((item) => canonicalSquarePath(item.squarePath) === canonicalSquarePath(squarePath)).map((item) => item.name)));
|
|
237
|
+
return names.size === 1 ? [...names][0] : undefined;
|
|
238
|
+
}
|
|
234
239
|
function bindingIsProvablyObsolete(binding, acts) {
|
|
235
240
|
return acts !== undefined && !isCurrentlyJoined(acts, binding.name);
|
|
236
241
|
}
|
|
@@ -306,3 +311,31 @@ export function recordLocalDone(name, squarePath, env = process.env) {
|
|
|
306
311
|
retireOwnerWakeRoutes(ownerId, { at, env });
|
|
307
312
|
}
|
|
308
313
|
}
|
|
314
|
+
export function recordSessionJoin(sessionId, name, squarePath, channel, env = process.env) {
|
|
315
|
+
const at = Date.now();
|
|
316
|
+
const ownerId = nextOwnerId();
|
|
317
|
+
const current = lookupParticipant(squarePath, name, at);
|
|
318
|
+
for (const binding of current) {
|
|
319
|
+
recordDone(binding.sessionId, binding.name, binding.squarePath, {
|
|
320
|
+
channel: binding.channel,
|
|
321
|
+
child: binding.child,
|
|
322
|
+
...(binding.paseoAgentId ? { paseoAgentId: binding.paseoAgentId } : {}),
|
|
323
|
+
at,
|
|
324
|
+
});
|
|
325
|
+
retireOwnerWakeRoutes(binding.ownerId, { at, env });
|
|
326
|
+
}
|
|
327
|
+
recordJoin(sessionId, name, squarePath, { channel, at, ownerId });
|
|
328
|
+
publishWakeRoutes(ownerId, { at, env });
|
|
329
|
+
return ownerId;
|
|
330
|
+
}
|
|
331
|
+
export function recordSessionDone(sessionId, name, squarePath, channel, env = process.env) {
|
|
332
|
+
const binding = lookupSessionBindings(sessionId).find((item) => canonicalSquarePath(item.squarePath) === canonicalSquarePath(squarePath)
|
|
333
|
+
&& sameName(item.name, name)
|
|
334
|
+
&& item.channel === channel);
|
|
335
|
+
if (binding === undefined)
|
|
336
|
+
return false;
|
|
337
|
+
const at = Date.now();
|
|
338
|
+
recordDone(sessionId, binding.name, binding.squarePath, { channel, at });
|
|
339
|
+
retireOwnerWakeRoutes(binding.ownerId, { at, env });
|
|
340
|
+
return true;
|
|
341
|
+
}
|
package/dist/routes.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type WakeRoute, type WakeRouteKind } from './model.js';
|
|
2
|
+
export { isWakeRouteKind, WAKE_ROUTE_KINDS } from './model.js';
|
|
3
|
+
export type { WakeRoute, WakeRouteKind } from './model.js';
|
|
4
|
+
export declare const ROUTE_FRESH_MS: number;
|
|
5
|
+
export declare function routesPath(env?: NodeJS.ProcessEnv): string;
|
|
6
|
+
export declare function readWakeRoutes(opts?: {
|
|
7
|
+
ownerId?: string;
|
|
8
|
+
freshOnly?: boolean;
|
|
9
|
+
now?: number;
|
|
10
|
+
env?: NodeJS.ProcessEnv;
|
|
11
|
+
}): WakeRoute[];
|
|
12
|
+
export declare function upsertWakeRoute(route: Omit<WakeRoute, 'updatedAt'>, opts?: {
|
|
13
|
+
at?: number;
|
|
14
|
+
env?: NodeJS.ProcessEnv;
|
|
15
|
+
}): void;
|
|
16
|
+
export declare function retireOwnerWakeRoutes(ownerId: string, opts?: {
|
|
17
|
+
at?: number;
|
|
18
|
+
env?: NodeJS.ProcessEnv;
|
|
19
|
+
}): void;
|
|
20
|
+
export interface WakeRouteEvidence {
|
|
21
|
+
sessionId: string;
|
|
22
|
+
address: Record<string, string>;
|
|
23
|
+
}
|
|
24
|
+
/** A provider's self-check: complete, callable endpoint evidence for one kind, or nothing. */
|
|
25
|
+
export type WakeRouteProbe = (env: NodeJS.ProcessEnv) => WakeRouteEvidence | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* One probe per kind. A probe publishes only when its provider's complete
|
|
28
|
+
* endpoint evidence is present; session identity alone never publishes. The
|
|
29
|
+
* four native transports have no endpoint lifecycle in this delivery, so
|
|
30
|
+
* their probes publish nothing until those transports land.
|
|
31
|
+
*/
|
|
32
|
+
export declare const WAKE_ROUTE_PROBES: Readonly<Record<WakeRouteKind, WakeRouteProbe>>;
|
|
33
|
+
/** The kind-neutral publication loop; probes supply complete evidence per kind. */
|
|
34
|
+
export declare function publishWakeRoutesFrom(ownerId: string, probes: Readonly<Record<WakeRouteKind, WakeRouteProbe>>, opts?: {
|
|
35
|
+
at?: number;
|
|
36
|
+
env?: NodeJS.ProcessEnv;
|
|
37
|
+
}): void;
|
|
38
|
+
/** Publication boundary: every route written is complete provider evidence. */
|
|
39
|
+
export declare function publishWakeRoutes(ownerId: string, opts?: {
|
|
40
|
+
at?: number;
|
|
41
|
+
env?: NodeJS.ProcessEnv;
|
|
42
|
+
}): void;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type ActivityId, type Reach } from './square-core.js';
|
|
2
|
+
import { type StoredAct, type SquareState, type HoldState, type ReadCursor, type WatchLease } from './model.js';
|
|
3
|
+
export declare const WATCH_HEARTBEAT_MS: number;
|
|
4
|
+
export declare const SLEEP_MS: number;
|
|
5
|
+
export declare const STALE_MS: number;
|
|
6
|
+
export declare const WATCH_STALE_MS: number;
|
|
7
|
+
export declare const LOCK_RETRY_MS: number;
|
|
8
|
+
export declare const LOCK_STALE_MS: number;
|
|
9
|
+
export declare const THROTTLE_WINDOW_MS: number;
|
|
10
|
+
export declare const UNREAD_BLOCK_GRACE_MS: number;
|
|
11
|
+
export declare function nowMs(): number;
|
|
12
|
+
/** Pure directed-activity filter. Bell matches every viewer; mentions match by audience. */
|
|
13
|
+
export declare function matchesMentionTarget(act: {
|
|
14
|
+
body: string;
|
|
15
|
+
reach?: Reach;
|
|
16
|
+
}, mention: string | true): boolean;
|
|
17
|
+
export declare function foldedState(squareState: SquareState): import("./square-core.js").FoldedSquareState;
|
|
18
|
+
export declare function rosterNames(squareState: SquareState): string[];
|
|
19
|
+
export declare function inSquareCount(squareState: SquareState): number;
|
|
20
|
+
export declare function resolveRosterName(squareState: SquareState, name: string): string | undefined;
|
|
21
|
+
export declare function hasQuorum(squareState: SquareState, name: string, outs: Set<string>): boolean;
|
|
22
|
+
export declare function countSays(acts: StoredAct[], name: string): number;
|
|
23
|
+
export declare function sayNumberFor(acts: StoredAct[], target: StoredAct): number;
|
|
24
|
+
/** Shared terminal condition for a participant's watch lifecycle. */
|
|
25
|
+
export declare function watchTerminalStatus(squareState: SquareState, name: string): 'capped' | 'quorum' | undefined;
|
|
26
|
+
export declare function doneNames(acts: StoredAct[]): Set<string>;
|
|
27
|
+
export declare function joinedNames(acts: StoredAct[]): Set<string>;
|
|
28
|
+
export declare function isCurrentlyJoined(acts: StoredAct[], name: string): boolean;
|
|
29
|
+
/** Stable index of the recipient's most recent join act, if any. */
|
|
30
|
+
export declare function lastJoinIndex(acts: StoredAct[], name: string): number | undefined;
|
|
31
|
+
export declare function actStableIndex(act: StoredAct): number;
|
|
32
|
+
export declare function actId(actOrIndex: StoredAct | number): ActivityId;
|
|
33
|
+
export declare function getReadState(squareState: SquareState, name: string): ReadCursor | undefined;
|
|
34
|
+
export declare function readCursor(squareState: SquareState, name: string): number;
|
|
35
|
+
export declare function currentHold(acts: StoredAct[]): HoldState;
|
|
36
|
+
export declare function publicActs(acts: StoredAct[]): Array<Extract<StoredAct, {
|
|
37
|
+
kind: 'say' | 'done';
|
|
38
|
+
}>>;
|
|
39
|
+
export declare function advanceCursor(squareState: SquareState, name: string, index: number, updatedAt?: number): boolean;
|
|
40
|
+
export declare function touchPresenceCursor(squareState: SquareState, name: string, at: number, consumedThroughIndex?: number): boolean;
|
|
41
|
+
export declare function latestActIndex(acts: StoredAct[]): number;
|
|
42
|
+
export declare function freshWatchLease(squareState: SquareState, name: string, at?: number): WatchLease | undefined;
|
|
43
|
+
export declare function watchLease(squareState: SquareState, name: string): WatchLease | undefined;
|
|
44
|
+
export declare function writeWatchLease(squareState: SquareState, name: string, lease: WatchLease): void;
|
|
45
|
+
export declare function removeWatchLease(squareState: SquareState, name: string, leaseId: string): boolean;
|
package/dist/runtime.js
CHANGED
|
@@ -58,20 +58,20 @@ export function matchesMentionTarget(act, mention) {
|
|
|
58
58
|
return audience.kind === 'bell' || audience.names.length > 0;
|
|
59
59
|
return audienceIncludes(audience, mention);
|
|
60
60
|
}
|
|
61
|
-
export function foldedState(
|
|
62
|
-
return fold(
|
|
61
|
+
export function foldedState(squareState) {
|
|
62
|
+
return fold(squareState.acts);
|
|
63
63
|
}
|
|
64
|
-
export function rosterNames(
|
|
65
|
-
return foldedState(
|
|
64
|
+
export function rosterNames(squareState) {
|
|
65
|
+
return foldedState(squareState).participants.map((participant) => participant.name);
|
|
66
66
|
}
|
|
67
|
-
export function inSquareCount(
|
|
68
|
-
return foldedState(
|
|
67
|
+
export function inSquareCount(squareState) {
|
|
68
|
+
return foldedState(squareState).joined.length;
|
|
69
69
|
}
|
|
70
|
-
export function resolveRosterName(
|
|
71
|
-
return findParticipantName(rosterNames(
|
|
70
|
+
export function resolveRosterName(squareState, name) {
|
|
71
|
+
return findParticipantName(rosterNames(squareState), name);
|
|
72
72
|
}
|
|
73
|
-
export function hasQuorum(
|
|
74
|
-
const peers = rosterNames(
|
|
73
|
+
export function hasQuorum(squareState, name, outs) {
|
|
74
|
+
const peers = rosterNames(squareState).filter((participant) => !sameName(participant, name));
|
|
75
75
|
return peers.length > 0 && peers.every((peer) => outs.has(nameKey(peer)));
|
|
76
76
|
}
|
|
77
77
|
export function countSays(acts, name) {
|
|
@@ -91,6 +91,14 @@ export function sayNumberFor(acts, target) {
|
|
|
91
91
|
}
|
|
92
92
|
throw new Error('target say act is not present in act history');
|
|
93
93
|
}
|
|
94
|
+
/** Shared terminal condition for a participant's watch lifecycle. */
|
|
95
|
+
export function watchTerminalStatus(squareState, name) {
|
|
96
|
+
if (squareState.hardCap !== null && countSays(squareState.acts, name) >= squareState.hardCap)
|
|
97
|
+
return 'capped';
|
|
98
|
+
const done = doneNames(squareState.acts);
|
|
99
|
+
done.delete(nameKey(name));
|
|
100
|
+
return hasQuorum(squareState, name, done) ? 'quorum' : undefined;
|
|
101
|
+
}
|
|
94
102
|
export function doneNames(acts) {
|
|
95
103
|
return new Set(fold(acts).done.map((participant) => nameKey(participant)));
|
|
96
104
|
}
|
|
@@ -118,11 +126,11 @@ export function actId(actOrIndex) {
|
|
|
118
126
|
const index = typeof actOrIndex === 'number' ? actOrIndex : actStableIndex(actOrIndex);
|
|
119
127
|
return formatActivityId(index);
|
|
120
128
|
}
|
|
121
|
-
export function getReadState(
|
|
122
|
-
return
|
|
129
|
+
export function getReadState(squareState, name) {
|
|
130
|
+
return squareState.runtime.cursors[name] ?? Object.entries(squareState.runtime.cursors).find(([participant]) => sameName(participant, name))?.[1];
|
|
123
131
|
}
|
|
124
|
-
export function readCursor(
|
|
125
|
-
return getReadState(
|
|
132
|
+
export function readCursor(squareState, name) {
|
|
133
|
+
return getReadState(squareState, name)?.consumedThroughIndex ?? -1;
|
|
126
134
|
}
|
|
127
135
|
export function currentHold(acts) {
|
|
128
136
|
const hold = fold(acts).hold;
|
|
@@ -131,48 +139,48 @@ export function currentHold(acts) {
|
|
|
131
139
|
export function publicActs(acts) {
|
|
132
140
|
return acts.filter((act) => act.kind === 'say' || act.kind === 'done');
|
|
133
141
|
}
|
|
134
|
-
function canonicalRuntimeName(
|
|
135
|
-
return resolveRosterName(
|
|
142
|
+
function canonicalRuntimeName(squareState, name) {
|
|
143
|
+
return resolveRosterName(squareState, name) ?? name;
|
|
136
144
|
}
|
|
137
|
-
export function advanceCursor(
|
|
138
|
-
return touchPresenceCursor(
|
|
145
|
+
export function advanceCursor(squareState, name, index, updatedAt = Date.now()) {
|
|
146
|
+
return touchPresenceCursor(squareState, name, updatedAt, index);
|
|
139
147
|
}
|
|
140
|
-
export function touchPresenceCursor(
|
|
148
|
+
export function touchPresenceCursor(squareState, name, at, consumedThroughIndex) {
|
|
141
149
|
if (!Number.isFinite(at))
|
|
142
150
|
return false;
|
|
143
151
|
if (consumedThroughIndex !== undefined && (!Number.isInteger(consumedThroughIndex) || consumedThroughIndex < 0))
|
|
144
152
|
return false;
|
|
145
|
-
const key = canonicalRuntimeName(
|
|
146
|
-
const current = getReadState(
|
|
153
|
+
const key = canonicalRuntimeName(squareState, name);
|
|
154
|
+
const current = getReadState(squareState, key);
|
|
147
155
|
const nextIndex = consumedThroughIndex === undefined
|
|
148
|
-
? (current?.consumedThroughIndex ?? readCursor(
|
|
156
|
+
? (current?.consumedThroughIndex ?? readCursor(squareState, key))
|
|
149
157
|
: Math.max(current?.consumedThroughIndex ?? -1, consumedThroughIndex);
|
|
150
158
|
const updatedAt = current === undefined ? at : Math.max(current.updatedAt, at);
|
|
151
159
|
if (current?.consumedThroughIndex === nextIndex && current.updatedAt === updatedAt)
|
|
152
160
|
return false;
|
|
153
|
-
|
|
161
|
+
squareState.runtime.cursors[key] = { consumedThroughIndex: nextIndex, updatedAt };
|
|
154
162
|
return true;
|
|
155
163
|
}
|
|
156
164
|
export function latestActIndex(acts) {
|
|
157
165
|
return acts.reduce((max, act) => Math.max(max, act.index), -1);
|
|
158
166
|
}
|
|
159
|
-
export function freshWatchLease(
|
|
160
|
-
const key = canonicalRuntimeName(
|
|
161
|
-
const lease = watchLease(
|
|
167
|
+
export function freshWatchLease(squareState, name, at = Date.now()) {
|
|
168
|
+
const key = canonicalRuntimeName(squareState, name);
|
|
169
|
+
const lease = watchLease(squareState, key);
|
|
162
170
|
if (lease === undefined || lease.expiresAt <= at || at - lease.heartbeatAt > WATCH_STALE_MS)
|
|
163
171
|
return undefined;
|
|
164
172
|
return lease;
|
|
165
173
|
}
|
|
166
|
-
export function watchLease(
|
|
167
|
-
return
|
|
174
|
+
export function watchLease(squareState, name) {
|
|
175
|
+
return squareState.runtime.leases[canonicalRuntimeName(squareState, name)];
|
|
168
176
|
}
|
|
169
|
-
export function writeWatchLease(
|
|
170
|
-
|
|
177
|
+
export function writeWatchLease(squareState, name, lease) {
|
|
178
|
+
squareState.runtime.leases[canonicalRuntimeName(squareState, name)] = lease;
|
|
171
179
|
}
|
|
172
|
-
export function removeWatchLease(
|
|
173
|
-
const key = canonicalRuntimeName(
|
|
174
|
-
if (
|
|
180
|
+
export function removeWatchLease(squareState, name, leaseId) {
|
|
181
|
+
const key = canonicalRuntimeName(squareState, name);
|
|
182
|
+
if (squareState.runtime.leases[key]?.leaseId !== leaseId)
|
|
175
183
|
return false;
|
|
176
|
-
delete
|
|
184
|
+
delete squareState.runtime.leases[key];
|
|
177
185
|
return true;
|
|
178
186
|
}
|
package/dist/search.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function compileGrepPattern(pattern: string): RegExp;
|
|
2
|
+
export declare function compileFixedPattern(pattern: string): RegExp;
|
|
3
|
+
export declare function compileSearchPattern(pattern: string, fixed: boolean): RegExp;
|
|
4
|
+
export interface GrepSnippet {
|
|
5
|
+
before: string;
|
|
6
|
+
match: string;
|
|
7
|
+
after: string;
|
|
8
|
+
beforeOmitted: number;
|
|
9
|
+
afterOmitted: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function grepSnippet(body: string, pattern: string, maxChars: number, fixed?: boolean): GrepSnippet | undefined;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
export type Participant = string;
|
|
2
|
+
export type Reach = 'bell';
|
|
3
|
+
export type Audience = {
|
|
4
|
+
readonly kind: 'bell';
|
|
5
|
+
} | {
|
|
6
|
+
readonly kind: 'mentions';
|
|
7
|
+
readonly names: readonly string[];
|
|
8
|
+
};
|
|
9
|
+
export type ActivityId = `act/${number}`;
|
|
10
|
+
export declare function formatActivityId(index: number): ActivityId;
|
|
11
|
+
export declare function parseActivityId(value: string): number | undefined;
|
|
12
|
+
export interface Hold {
|
|
13
|
+
active: boolean;
|
|
14
|
+
at?: number;
|
|
15
|
+
reason?: string;
|
|
16
|
+
actor?: Participant;
|
|
17
|
+
}
|
|
18
|
+
export type Act = {
|
|
19
|
+
kind: 'join';
|
|
20
|
+
actor: Participant;
|
|
21
|
+
at?: number;
|
|
22
|
+
} | {
|
|
23
|
+
kind: 'done';
|
|
24
|
+
actor: Participant;
|
|
25
|
+
at?: number;
|
|
26
|
+
body?: string;
|
|
27
|
+
} | {
|
|
28
|
+
kind: 'say';
|
|
29
|
+
actor: Participant;
|
|
30
|
+
at?: number;
|
|
31
|
+
body: string;
|
|
32
|
+
reach?: Reach;
|
|
33
|
+
reply?: number;
|
|
34
|
+
} | {
|
|
35
|
+
kind: 'hold';
|
|
36
|
+
actor?: Participant;
|
|
37
|
+
at?: number;
|
|
38
|
+
body?: string;
|
|
39
|
+
} | {
|
|
40
|
+
kind: 'resume';
|
|
41
|
+
actor?: Participant;
|
|
42
|
+
at?: number;
|
|
43
|
+
} | {
|
|
44
|
+
kind: 'read';
|
|
45
|
+
actor: Participant;
|
|
46
|
+
at?: number;
|
|
47
|
+
through: number;
|
|
48
|
+
};
|
|
49
|
+
export interface SquareValidationOptions {
|
|
50
|
+
hardCap?: number | null;
|
|
51
|
+
throttlePerMinute?: number;
|
|
52
|
+
throttleWindowMs?: number;
|
|
53
|
+
}
|
|
54
|
+
export type Perception = 'full' | 'presence';
|
|
55
|
+
interface ParticipantSnapshot {
|
|
56
|
+
name: Participant;
|
|
57
|
+
key: string;
|
|
58
|
+
joined: boolean;
|
|
59
|
+
done: boolean;
|
|
60
|
+
activityCount: number;
|
|
61
|
+
lastActiveAt?: number;
|
|
62
|
+
lastReadThrough: number;
|
|
63
|
+
}
|
|
64
|
+
export interface FoldedSquareState {
|
|
65
|
+
participants: ParticipantSnapshot[];
|
|
66
|
+
hold: Hold;
|
|
67
|
+
joined: Participant[];
|
|
68
|
+
done: Participant[];
|
|
69
|
+
throttleActivityAts: number[];
|
|
70
|
+
bellSayAtsByActor: Map<string, number[]>;
|
|
71
|
+
}
|
|
72
|
+
export type ValidationResult = {
|
|
73
|
+
ok: true;
|
|
74
|
+
} | {
|
|
75
|
+
ok: false;
|
|
76
|
+
reason: 'already_joined';
|
|
77
|
+
} | {
|
|
78
|
+
ok: false;
|
|
79
|
+
reason: 'not_joined';
|
|
80
|
+
} | {
|
|
81
|
+
ok: false;
|
|
82
|
+
reason: 'done';
|
|
83
|
+
} | {
|
|
84
|
+
ok: false;
|
|
85
|
+
reason: 'held';
|
|
86
|
+
hold: Hold;
|
|
87
|
+
} | {
|
|
88
|
+
ok: false;
|
|
89
|
+
reason: 'hard_cap';
|
|
90
|
+
count: number;
|
|
91
|
+
hardCap: number;
|
|
92
|
+
} | {
|
|
93
|
+
ok: false;
|
|
94
|
+
reason: 'throttled';
|
|
95
|
+
delayMs: number;
|
|
96
|
+
} | {
|
|
97
|
+
ok: false;
|
|
98
|
+
reason: 'bell_quota';
|
|
99
|
+
nextAt: number;
|
|
100
|
+
};
|
|
101
|
+
export declare function extractMentions(body: string): string[];
|
|
102
|
+
export declare function audienceOf(say: {
|
|
103
|
+
body: string;
|
|
104
|
+
reach?: Reach;
|
|
105
|
+
}): Audience;
|
|
106
|
+
export declare function audienceIncludes(audience: Audience, name: string): boolean;
|
|
107
|
+
export declare function resolveAudience(audience: Audience, candidateNames: readonly string[]): string[];
|
|
108
|
+
export declare function fold(acts: readonly Act[]): FoldedSquareState;
|
|
109
|
+
export declare function validate(state: FoldedSquareState, act: Act, options?: SquareValidationOptions): ValidationResult;
|
|
110
|
+
export declare function perceive(act: Act, viewer: Participant | string): Perception;
|
|
111
|
+
export {};
|