@astrosheep/square 0.3.86 → 0.3.88
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifact.d.ts +6 -1
- package/dist/artifact.js +79 -10
- package/dist/boundary-presentation.d.ts +5 -4
- package/dist/boundary-presentation.js +21 -25
- package/dist/catch-decisions.js +2 -1
- package/dist/claude-delivery-status.d.ts +8 -2
- package/dist/claude-delivery-status.js +26 -10
- package/dist/claude-delivery.d.ts +8 -4
- package/dist/claude-delivery.js +114 -104
- package/dist/claude-mcp-context.js +2 -2
- package/dist/claude-mod.js +119 -105
- package/dist/cli/context.d.ts +3 -1
- package/dist/cli/context.js +25 -1
- package/dist/cli/express-presentation.d.ts +3 -1
- package/dist/cli/express-presentation.js +14 -10
- package/dist/cli/mcp-server-command.js +2 -2
- package/dist/cli/observation-commands.js +1 -0
- package/dist/cli/registry.js +0 -2
- package/dist/cli/square-commands.d.ts +4 -2
- package/dist/cli/square-commands.js +21 -6
- package/dist/codex-hook.d.ts +3 -3
- package/dist/codex-hook.js +4 -4
- package/dist/codex-queue.d.ts +5 -6
- package/dist/codex-queue.js +9 -5
- package/dist/decisions.d.ts +3 -2
- package/dist/decisions.js +18 -6
- package/dist/delivery-health.d.ts +32 -2
- package/dist/delivery-health.js +108 -45
- package/dist/delivery-operations.d.ts +1 -2
- package/dist/delivery-operations.js +155 -196
- package/dist/delivery.d.ts +14 -23
- package/dist/delivery.js +48 -18
- package/dist/file-lock.d.ts +8 -1
- package/dist/file-lock.js +2 -0
- package/dist/help.js +8 -11
- package/dist/inbox.d.ts +35 -9
- package/dist/inbox.js +228 -144
- package/dist/index.d.ts +3 -4
- package/dist/index.js +1 -3
- package/dist/list.js +9 -4
- package/dist/mcp.js +12 -1
- package/dist/model.d.ts +39 -5
- package/dist/model.js +84 -0
- package/dist/notifications.d.ts +8 -6
- package/dist/notifications.js +56 -143
- package/dist/operation-presentation.js +7 -4
- package/dist/participant-identity.d.ts +0 -3
- package/dist/participant-identity.js +0 -31
- package/dist/paseo-delivery.d.ts +5 -6
- package/dist/paseo-delivery.js +13 -8
- package/dist/paseo.d.ts +1 -1
- package/dist/paseo.js +1 -1
- package/dist/plugins/opencode/delivery.d.ts +8 -3
- package/dist/plugins/opencode/delivery.js +187 -132
- package/dist/plugins/opencode/index.js +12 -38
- package/dist/ports.d.ts +112 -40
- package/dist/presentation-operations.d.ts +1 -1
- package/dist/presentation-operations.js +51 -35
- package/dist/presentation.js +8 -3
- package/dist/seats.d.ts +7 -0
- package/dist/seats.js +27 -0
- package/dist/session-lifecycle.d.ts +2 -2
- package/dist/session-lifecycle.js +10 -12
- package/dist/sqlite-square.d.ts +20 -9
- package/dist/sqlite-square.js +476 -341
- package/dist/square-api.d.ts +1 -0
- package/dist/square-api.js +3 -4
- package/dist/square-core.d.ts +3 -2
- package/dist/square-domain.d.ts +12 -5
- package/dist/square-domain.js +163 -7
- package/dist/square-facade.d.ts +16 -10
- package/dist/square-index.d.ts +24 -0
- package/dist/square-index.js +131 -0
- package/dist/square-legacy-framing.d.ts +9 -0
- package/dist/square-legacy-framing.js +56 -0
- package/dist/square-migration-host-ledger.d.ts +26 -0
- package/dist/square-migration-host-ledger.js +168 -0
- package/dist/square-migration.d.ts +5 -5
- package/dist/square-migration.js +373 -71
- package/dist/square-sqlite.d.ts +9 -9
- package/dist/square-sqlite.js +329 -79
- package/dist/square-stdio.d.ts +1 -1
- package/dist/square-stdio.js +12 -7
- package/dist/square-tools.d.ts +1 -1
- package/dist/square-tools.js +80 -27
- package/dist/stdio-server.js +4 -2
- package/dist/stream.d.ts +3 -2
- package/dist/stream.js +9 -10
- package/dist/views.d.ts +9 -14
- package/dist/views.js +52 -70
- package/dist/wakes.d.ts +6 -5
- package/dist/wakes.js +20 -17
- package/guides/application-facade-delivery-evidence.md +1 -1
- package/guides/claude-native.md +9 -9
- package/guides/mcp.md +2 -2
- package/guides/migration.md +107 -67
- package/guides/pi-tools.md +1 -1
- package/guides/row-storage-design.md +1 -1
- package/guides/square-api.md +61 -28
- package/package.json +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/hooks/register.js +1 -1
- package/plugins/claude/skills/square/SKILL.md +4 -4
- package/plugins/claude/types.d.ts +1 -1
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/opencode/delivery.ts +170 -118
- package/plugins/opencode/index.ts +12 -30
- package/plugins/pi/square-pi.js +56 -18
- package/skills/square/SKILL.md +4 -4
- package/dist/cli/ledger-command.d.ts +0 -12
- package/dist/cli/ledger-command.js +0 -72
- package/dist/host-ledger-file-adapter.d.ts +0 -47
- package/dist/host-ledger-file-adapter.js +0 -384
- package/dist/host-ledger-migration.d.ts +0 -27
- package/dist/host-ledger-migration.js +0 -264
- package/dist/host-ledger-root.d.ts +0 -2
- package/dist/host-ledger-root.js +0 -9
- package/dist/host-ledger-sqlite.d.ts +0 -65
- package/dist/host-ledger-sqlite.js +0 -368
- package/dist/host-ledger.d.ts +0 -195
- package/dist/host-ledger.js +0 -1
- package/dist/ledger-maintenance.d.ts +0 -30
- package/dist/ledger-maintenance.js +0 -110
- package/dist/open-square.d.ts +0 -38
- package/dist/open-square.js +0 -81
- package/dist/participant-host.d.ts +0 -23
- package/dist/participant-host.js +0 -92
- package/dist/registry.d.ts +0 -55
- package/dist/registry.js +0 -123
- package/dist/routes.d.ts +0 -39
- package/dist/routes.js +0 -99
- package/dist/square-actions.d.ts +0 -56
- package/dist/square-actions.js +0 -353
- package/dist/square-projections.d.ts +0 -31
- package/dist/square-projections.js +0 -64
- package/dist/wake-attempts.d.ts +0 -10
- package/dist/wake-attempts.js +0 -39
- package/dist/wake-eligibility.d.ts +0 -53
- package/dist/wake-eligibility.js +0 -123
- package/dist/wake-evidence.d.ts +0 -33
- package/dist/wake-evidence.js +0 -32
- package/guides/host-ledger.md +0 -58
- package/guides/indexed-host-ledger-performance.md +0 -39
package/dist/square-stdio.js
CHANGED
|
@@ -3,9 +3,10 @@ import path from 'node:path';
|
|
|
3
3
|
import { createInterface } from 'node:readline';
|
|
4
4
|
import { SquareError } from './model.js';
|
|
5
5
|
import { harnessSessionSources } from './participant-identity.js';
|
|
6
|
+
import { requireJoinName } from './seats.js';
|
|
6
7
|
export const STDIO_PROTOCOL_VERSION = 1;
|
|
7
8
|
export const STDIO_MAX_FRAME_BYTES = 8 * 1024 * 1024;
|
|
8
|
-
export const identityVariables = [...harnessSessionSources.flatMap(source => [source.variable, ...(source.childVariable ? [source.childVariable] : [])])
|
|
9
|
+
export const identityVariables = [...harnessSessionSources.flatMap(source => [source.variable, ...(source.childVariable ? [source.childVariable] : [])])];
|
|
9
10
|
export function wireIdentity(env) {
|
|
10
11
|
return Object.fromEntries(identityVariables.map(key => [key, env[key] ?? '']));
|
|
11
12
|
}
|
|
@@ -162,7 +163,11 @@ class StdioSquare {
|
|
|
162
163
|
participantHandle(name) {
|
|
163
164
|
return {
|
|
164
165
|
name,
|
|
165
|
-
express: (
|
|
166
|
+
express: ((bodyOrInput, optionsOrControl, control) => {
|
|
167
|
+
if (typeof bodyOrInput === 'object' && bodyOrInput !== null)
|
|
168
|
+
return this.call('express', [bodyOrInput, undefined], optionsOrControl, name);
|
|
169
|
+
return this.call('express', [bodyOrInput, optionsOrControl], control, name);
|
|
170
|
+
}),
|
|
166
171
|
catch: (options, control) => this.call('catch', [options], control, name),
|
|
167
172
|
listen: (target, options, control) => this.call('listen', [target, options], control, name),
|
|
168
173
|
ignore: (target, options, control) => this.call('ignore', [target, options], control, name),
|
|
@@ -176,6 +181,7 @@ class StdioSquare {
|
|
|
176
181
|
}
|
|
177
182
|
async participant(name) { return this.participantHandle(await this.call('participant', [name])); }
|
|
178
183
|
async join(...[name, options, control]) {
|
|
184
|
+
requireJoinName(name);
|
|
179
185
|
const result = await this.call('join', [name, options], control);
|
|
180
186
|
return { ...result, participant: this.participantHandle(result.participant) };
|
|
181
187
|
}
|
|
@@ -187,14 +193,13 @@ class StdioSquare {
|
|
|
187
193
|
stream() { unsupported('stream'); }
|
|
188
194
|
recognize() { return Promise.reject(new SquareError('invalid_args', 'stdio does not support host recognition; select a participant explicitly')); }
|
|
189
195
|
implicitJoin() { unsupported('host implicit join'); }
|
|
190
|
-
doneOwnedSession() { unsupported('host session cleanup'); }
|
|
191
196
|
endOwnedSession() { unsupported('host session cleanup'); }
|
|
192
|
-
|
|
197
|
+
cancelDeliveries() { unsupported('host delivery cancellation'); }
|
|
193
198
|
close() { return this.connection.close(); }
|
|
194
199
|
}
|
|
195
200
|
export async function openStdioSquare(location, selected, input, backends, build, control) {
|
|
196
|
-
if (input.clock || input.
|
|
197
|
-
unsupported('client clock
|
|
201
|
+
if (input.clock || input.wakeTransport)
|
|
202
|
+
unsupported('client clock or wakeTransport injection');
|
|
198
203
|
if (control?.signal?.aborted)
|
|
199
204
|
throw control.signal.reason ?? new Error('Operation cancelled');
|
|
200
205
|
const backend = Object.hasOwn(backends, selected.backend) ? backends[selected.backend] : undefined;
|
|
@@ -205,7 +210,7 @@ export async function openStdioSquare(location, selected, input, backends, build
|
|
|
205
210
|
const connection = new Connection(command, backend.args ?? [], path.resolve(cwd, backend.cwd ?? '.'), { ...(input.env ?? process.env), ...backend.env });
|
|
206
211
|
try {
|
|
207
212
|
const result = await connection.request('square/open', { version: STDIO_PROTOCOL_VERSION, key: selected.key, location,
|
|
208
|
-
identity: wireIdentity(input.env ?? process.env), participant: input.participant, build }, control);
|
|
213
|
+
identity: wireIdentity(input.env ?? process.env), body: input.body, participant: input.participant, build }, control);
|
|
209
214
|
if (result.version !== STDIO_PROTOCOL_VERSION || typeof result.handle !== 'string')
|
|
210
215
|
throw new SquareError('unavailable', 'Backend protocol version mismatch');
|
|
211
216
|
return new StdioSquare(location, connection, result.handle);
|
package/dist/square-tools.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ interface ToolDefinition {
|
|
|
16
16
|
export interface ToolControl extends OperationControl {
|
|
17
17
|
readonly observe?: boolean;
|
|
18
18
|
}
|
|
19
|
-
export declare const SQUARE_TOOL_INSTRUCTIONS = "
|
|
19
|
+
export declare const SQUARE_TOOL_INSTRUCTIONS = "Join once. Then express to speak, and catch to take in what reached you. History looks back without taking anything in.";
|
|
20
20
|
export declare const SQUARE_TOOLS: readonly ToolDefinition[];
|
|
21
21
|
export declare function squareToolFailure(error: unknown, signal?: AbortSignal): JsonObject;
|
|
22
22
|
/** Readable failure text with the same recovery commands the CLI offers, never raw JSON. */
|
package/dist/square-tools.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { z } from 'zod/v4';
|
|
2
2
|
import { isSquareError } from './model.js';
|
|
3
3
|
import { CATCH_DEFAULT_LIMIT } from './square-api.js';
|
|
4
|
-
import { renderCatchOutcome, renderExpression, renderFailureLines, renderHistoryResult, renderJoinResult, renderLifecycleResult, renderListenerChange,
|
|
5
|
-
const location = z.string().optional().describe('
|
|
6
|
-
const participant = z.string().optional().describe('
|
|
4
|
+
import { renderCatchOutcome, renderExpression, renderFailureLines, renderHistoryResult, renderJoinResult, renderLifecycleResult, renderListenerChange, renderStatusResult, } from './operation-presentation.js';
|
|
5
|
+
const location = z.string().optional().describe('Which square: a .square file path, or a backend URI like stdio://name/key. Relative paths start from the working directory. Leave it out for the configured square.');
|
|
6
|
+
const participant = z.string().optional().describe('Your name in the square. Leave it out if you hold exactly one name here.');
|
|
7
7
|
const activityId = z.string().regex(/^act\/(0|[1-9][0-9]*)$/);
|
|
8
8
|
const stringArray = z.array(z.string());
|
|
9
9
|
/** Keep the observation request internal: it is not an argument a caller decides. */
|
|
@@ -14,50 +14,103 @@ function observation(control) {
|
|
|
14
14
|
function rendering(render) {
|
|
15
15
|
return (result, args) => render(result, args);
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Tool receipts are the one machine surface without activity metadata: a receipt copy drops every
|
|
19
|
+
* property named `metadata` while domain activities keep theirs for SDK, CLI, stdio and stream readers.
|
|
20
|
+
*/
|
|
21
|
+
function withoutActivityMetadata(value) {
|
|
22
|
+
if (Array.isArray(value))
|
|
23
|
+
return value.map(withoutActivityMetadata);
|
|
24
|
+
if (value === null || typeof value !== 'object')
|
|
25
|
+
return value;
|
|
26
|
+
const prototype = Object.getPrototypeOf(value);
|
|
27
|
+
if (prototype !== Object.prototype && prototype !== null)
|
|
28
|
+
return value;
|
|
29
|
+
return Object.fromEntries(Object.entries(value).filter(([key]) => key !== 'metadata')
|
|
30
|
+
.map(([key, item]) => [key, withoutActivityMetadata(item)]));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Human text renders from the unfiltered receipt, so metadata-only says keep the placeholder the
|
|
34
|
+
* CLI prints and every other sentence stays identical. Machines only ever receive the filtered copy.
|
|
35
|
+
*/
|
|
36
|
+
const renderingReceipts = new WeakMap();
|
|
37
|
+
function renderingReceipt(receipt) {
|
|
38
|
+
return (renderingReceipts.get(receipt) ?? receipt);
|
|
39
|
+
}
|
|
17
40
|
function tool(name, description, shape, run, render) {
|
|
18
41
|
const inputSchema = z.object({ ...shape, location }).strict();
|
|
19
|
-
return { name, description, inputSchema, render
|
|
42
|
+
return { name, description, inputSchema, render: (receipt, args) => render(renderingReceipt(receipt), args),
|
|
43
|
+
run: async (square, args, control) => {
|
|
20
44
|
const { location: _location, ...operationArgs } = inputSchema.parse(args);
|
|
21
|
-
|
|
45
|
+
const receipt = await run(square, operationArgs, control);
|
|
46
|
+
const filtered = withoutActivityMetadata(receipt);
|
|
47
|
+
renderingReceipts.set(filtered, receipt);
|
|
48
|
+
return filtered;
|
|
22
49
|
} };
|
|
23
50
|
}
|
|
24
|
-
export const SQUARE_TOOL_INSTRUCTIONS = '
|
|
51
|
+
export const SQUARE_TOOL_INSTRUCTIONS = 'Join once. Then express to speak, and catch to take in what reached you. History looks back without taking anything in.';
|
|
52
|
+
const target = z.string().describe('The participant to turn toward or away from.');
|
|
25
53
|
export const SQUARE_TOOLS = [
|
|
26
|
-
tool('join', '
|
|
54
|
+
tool('join', 'Step into the square under your name. Do this once before you speak or catch. You arrive seeing what was said most recently.', {
|
|
55
|
+
as: z.string().optional().describe('Your name in the square. Give it explicitly when joining.'), takeover: z.boolean().optional().describe('Claim this name even though another session holds it now. That session loses it.'),
|
|
56
|
+
}, async (square, { as, takeover }, control) => {
|
|
27
57
|
const { participant, ...receipt } = await square.join(as, { takeover, ...observation(control) }, control);
|
|
28
58
|
return { ...receipt, participant: participant.name };
|
|
29
59
|
}, rendering((result) => renderJoinResult(result))),
|
|
30
|
-
tool('express', '
|
|
31
|
-
as: participant,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
60
|
+
tool('express', 'Speak or act in the square. Plain words, half-formed ideas and *actions* all belong here. Mention or reply to someone to be heard by them; ring the bell to be heard by everyone. Said to no one in particular, your words reach those who listen to you.', {
|
|
61
|
+
as: participant,
|
|
62
|
+
body: z.string().describe('What you say or do. Markdown works; *asterisks* mark an action.'),
|
|
63
|
+
mentions: stringArray.optional().describe('Who you are speaking to. An @name in the body that matches someone here counts too.'),
|
|
64
|
+
reach: z.enum(['bell']).optional().describe('"bell" calls everyone in the square. Cannot be used with mentions.'),
|
|
65
|
+
reply: activityId.optional().describe('The activity you are answering, like act/12. Its author hears you if still here.'),
|
|
66
|
+
noMention: z.boolean().optional().describe('Leave @names in the body as plain text. Mentions and reply still reach people.'),
|
|
67
|
+
force: z.boolean().optional().describe('Speak now, even if something reached you that you have not caught yet.'),
|
|
68
|
+
noWait: z.boolean().optional().describe('If a hand is raised or you are speaking too fast, return at once instead of waiting.'),
|
|
35
69
|
}, async (square, { body, as, ...options }, control) => {
|
|
36
70
|
const { delivery: _delivery, ...result } = await (await square.participant(as)).express(body, { ...options, ...observation(control) }, control);
|
|
37
71
|
return result;
|
|
38
72
|
}, rendering((result) => renderExpression(result.observation?.location ?? '', result))),
|
|
39
|
-
tool('catch', '
|
|
40
|
-
as: participant,
|
|
41
|
-
|
|
73
|
+
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.', {
|
|
74
|
+
as: participant,
|
|
75
|
+
id: activityId.optional().describe('Take in this activity in full, like act/12, even if you caught it before. Do not combine with idle, from, mention or limit.'),
|
|
76
|
+
idle: z.number().finite().min(0).optional().describe('When nothing is waiting, wait until something arrives or until it has been quiet this many milliseconds. Leave it out to return at once.'),
|
|
77
|
+
from: stringArray.optional().describe('Only what these participants said.'),
|
|
78
|
+
mention: z.boolean().optional().describe('Only what was addressed to you, and bells.'),
|
|
79
|
+
limit: z.number().int().min(1).optional().describe('At most this many at once (default 10, up to 100). The rest keeps waiting.'),
|
|
42
80
|
}, async (square, { as, ...options }, control) => (await square.participant(as)).catch({ ...options, ...observation(control) }, control), rendering((result, args) => {
|
|
43
81
|
// The CLI always spells the default cap in its continuation, so the tool does too.
|
|
44
82
|
const options = args;
|
|
45
83
|
const renderOptions = options.id === undefined ? { ...options, limit: options.limit ?? CATCH_DEFAULT_LIMIT } : options;
|
|
46
84
|
return renderCatchOutcome(result.observation?.location ?? '', result, renderOptions).stdout;
|
|
47
85
|
})),
|
|
48
|
-
tool('history', '
|
|
49
|
-
limit: z.number().int().min(1).optional()
|
|
50
|
-
|
|
86
|
+
tool('history', 'Look back at what was said. Reading here takes nothing in: whatever waits for catch keeps waiting. Page with before or after.', {
|
|
87
|
+
limit: z.number().int().min(1).optional().describe('How many per page (default 10, up to 100).'),
|
|
88
|
+
order: z.enum(['asc', 'desc']).optional().describe('"asc" for oldest first (default), "desc" for newest first.'),
|
|
89
|
+
before: activityId.optional().describe('The page just before this activity, like act/12.'),
|
|
90
|
+
after: activityId.optional().describe('The page just after this activity, like act/12.'),
|
|
91
|
+
grep: z.string().optional().describe('Only activities matching this regular expression in their names, ids or words. Use grep or fixed, not both.'),
|
|
92
|
+
fixed: z.string().optional().describe('Only activities containing this exact text.'),
|
|
93
|
+
from: stringArray.optional().describe('Only what these participants said.'),
|
|
94
|
+
mention: z.string().optional().describe('Only what was addressed to this participant.'),
|
|
51
95
|
}, (square, args, control) => square.history({ ...args, ...observation(control) }, control), rendering((result, args) => renderHistoryResult(result.observation?.location ?? '', result, { query: args }))),
|
|
52
|
-
tool('
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
tool('
|
|
59
|
-
|
|
60
|
-
|
|
96
|
+
tool('attention', 'Turn toward someone or away from them, from now on. Listen: hear everything they say, even to others. Unlisten: stop listening without clearing an ignore; mentions and replies still reach you unless ignored. Ignore: hear nothing from them but bells. What was already said stays as it was.', {
|
|
97
|
+
as: participant, action: z.enum(['listen', 'unlisten', 'ignore']).describe('"listen", "unlisten" or "ignore". Listening again ends an ignore.'), target,
|
|
98
|
+
}, async (square, { as, action, target }, control) => {
|
|
99
|
+
const self = await square.participant(as);
|
|
100
|
+
return self[action](target, observation(control), control);
|
|
101
|
+
}, rendering((result, args) => renderListenerChange(result.observation?.location ?? '', args.action, result))),
|
|
102
|
+
tool('hold', 'Raise a hand to pause the conversation; lower it with release. While a hand is up, new words wait to land.', {
|
|
103
|
+
as: participant,
|
|
104
|
+
reason: z.string().optional().describe('Why you are pausing. Everyone sees it.'),
|
|
105
|
+
release: z.boolean().optional().describe('true lowers the raised hand so the conversation goes on.'),
|
|
106
|
+
}, async (square, { as, reason, release }, control) => {
|
|
107
|
+
const self = await square.participant(as);
|
|
108
|
+
return release === true ? self.resume(observation(control), control) : self.hold(reason, observation(control), control);
|
|
109
|
+
}, rendering((result, args) => renderLifecycleResult(result.observation?.location ?? '', args.release === true ? 'resume' : 'hold', result))),
|
|
110
|
+
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) => (await square.participant(as)).done(undefined, observation(control), control), rendering((result) => renderLifecycleResult(result.observation?.location ?? '', 'done', result))),
|
|
111
|
+
tool('status', 'Look around the square: who is here, whether a hand is raised, the latest activity, and who you listen to.', {
|
|
112
|
+
as: z.string().optional().describe('Whose listening to show. Leave it out if you hold exactly one name here.'),
|
|
113
|
+
}, (square, { as }, control) => square.status({ viewer: as, ...observation(control) }, control), rendering((result) => renderStatusResult(result.observation?.location ?? '', result))),
|
|
61
114
|
];
|
|
62
115
|
export function squareToolFailure(error, signal) {
|
|
63
116
|
const normalized = signal?.aborted
|
package/dist/stdio-server.js
CHANGED
|
@@ -4,7 +4,7 @@ import fs from 'node:fs/promises';
|
|
|
4
4
|
import { createInterface } from 'node:readline';
|
|
5
5
|
import { pathToFileURL } from 'node:url';
|
|
6
6
|
import { SqliteSquare } from './sqlite-square.js';
|
|
7
|
-
import { SquareError } from './model.js';
|
|
7
|
+
import { SquareError, validateBody } from './model.js';
|
|
8
8
|
import { canonicalPath } from './canonical-path.js';
|
|
9
9
|
import { STDIO_PROTOCOL_VERSION, STDIO_MAX_FRAME_BYTES, encodeError, identityVariables } from './square-stdio.js';
|
|
10
10
|
import { parseSquareLocation } from './square-location.js';
|
|
@@ -71,7 +71,9 @@ export async function runSquareStdioServer(root) {
|
|
|
71
71
|
throw new SquareError('invalid_args', 'Invalid operation identity');
|
|
72
72
|
env[key] = value ?? '';
|
|
73
73
|
}
|
|
74
|
-
const input = { path: target, env, participant: params.participant };
|
|
74
|
+
const input = { path: target, env, body: params.body, participant: params.participant };
|
|
75
|
+
if (params.body !== undefined)
|
|
76
|
+
validateBody(params.body);
|
|
75
77
|
square = params.build === undefined ? await SqliteSquare.at(input, control, false)
|
|
76
78
|
: await SqliteSquare.build({ ...params.build, ...input }, control, false);
|
|
77
79
|
location = parsed.uri;
|
package/dist/stream.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { SquareArtifactPort } from './ports.js';
|
|
2
|
+
import type { OperationControl, StreamOptions, StreamRecord } from './square-facade.js';
|
|
2
3
|
/** Pull-based observation; return interrupts an outstanding pull before awaiting its cleanup. */
|
|
3
|
-
export declare function streamSquare(squarePath: string, options?: StreamOptions, control?: OperationControl,
|
|
4
|
+
export declare function streamSquare(squarePath: string, options?: StreamOptions, control?: OperationControl, borrowed?: SquareArtifactPort): AsyncIterable<StreamRecord>;
|
package/dist/stream.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import { SquareError } from './model.js';
|
|
3
|
-
import { observeSquareRevision } from './artifact.js';
|
|
4
|
-
import { openSquare, closeOpenSquare } from './open-square.js';
|
|
3
|
+
import { createArtifactPort, observeSquareRevision } from './artifact.js';
|
|
5
4
|
import { streamProjection, streamTailProjection } from './views.js';
|
|
6
5
|
/** Pull-based observation; return interrupts an outstanding pull before awaiting its cleanup. */
|
|
7
|
-
export function streamSquare(squarePath, options = {}, control,
|
|
6
|
+
export function streamSquare(squarePath, options = {}, control, borrowed) {
|
|
8
7
|
return {
|
|
9
8
|
[Symbol.asyncIterator]() {
|
|
10
9
|
const disposal = new AbortController();
|
|
11
10
|
const signal = control?.signal === undefined ? disposal.signal : AbortSignal.any([control.signal, disposal.signal]);
|
|
12
|
-
const iterationControl = { ...control, signal };
|
|
13
11
|
const records = observe();
|
|
14
12
|
return {
|
|
15
13
|
next: () => records.next(),
|
|
@@ -43,19 +41,20 @@ export function streamSquare(squarePath, options = {}, control, openOptions = {}
|
|
|
43
41
|
let projection;
|
|
44
42
|
try {
|
|
45
43
|
baseline = await observer.read(signal);
|
|
46
|
-
const
|
|
44
|
+
const artifact = borrowed ?? createArtifactPort(squarePath, undefined, signal);
|
|
47
45
|
try {
|
|
48
|
-
projection = initial === undefined
|
|
49
|
-
?
|
|
50
|
-
:
|
|
46
|
+
projection = (await artifact.read(facts => initial === undefined
|
|
47
|
+
? streamProjection(facts, cursor, options.recipient)
|
|
48
|
+
: streamTailProjection(facts, initial.last, options.recipient), signal)).value;
|
|
51
49
|
}
|
|
52
50
|
finally {
|
|
53
|
-
|
|
51
|
+
if (borrowed === undefined)
|
|
52
|
+
await artifact.close();
|
|
54
53
|
}
|
|
55
54
|
}
|
|
56
55
|
catch (error) {
|
|
57
56
|
signal.throwIfAborted();
|
|
58
|
-
if (error instanceof SquareError && error.code === 'invalid_args')
|
|
57
|
+
if (borrowed !== undefined || error instanceof SquareError && error.code === 'invalid_args')
|
|
59
58
|
throw error;
|
|
60
59
|
// Repair/replacement is a hint, not a reason to scan history on a timer.
|
|
61
60
|
await observer.hinted(generation, Infinity, signal);
|
package/dist/views.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { type ActivityId } from './square-core.js';
|
|
2
2
|
import { type StoredAct } from './model.js';
|
|
3
|
-
import type { OpenSquare } from './open-square.js';
|
|
4
3
|
import type { ActivityFilter, SquareRead } from './ports.js';
|
|
5
|
-
import type { Activity, ArchiveRecord, HistoryQuery, HistoryContinuation,
|
|
4
|
+
import type { Activity, ArchiveRecord, HistoryQuery, HistoryContinuation, ParticipantsResult, SquareSnapshot, EntryPresentation, ListPresentation, StatusResult, SquareObservation, CatchPresence, StreamProjection } from './square-facade.js';
|
|
6
5
|
export type { EntryPresentation, ListPresentation, CatchPresence, StreamProjection } from './square-facade.js';
|
|
7
6
|
export declare const HISTORY_DEFAULT_LIMIT = 10;
|
|
8
7
|
export declare const HISTORY_MAX_LIMIT = 100;
|
|
@@ -18,12 +17,8 @@ export declare function selectHistory(facts: SquareRead, query: HistoryQuery, li
|
|
|
18
17
|
totalMatches: number;
|
|
19
18
|
};
|
|
20
19
|
export declare function publicActivities(activities: readonly ArchiveRecord[]): Activity[];
|
|
21
|
-
export declare function
|
|
22
|
-
|
|
23
|
-
}>;
|
|
24
|
-
export declare function currentParticipant(square: OpenSquare, name: string, control?: OperationControl): Promise<string | undefined>;
|
|
25
|
-
export declare function snapshot(square: OpenSquare, control?: OperationControl): Promise<SquareSnapshot>;
|
|
26
|
-
export declare function recoveryObservation(square: OpenSquare, name: string, control?: OperationControl): Promise<{
|
|
20
|
+
export declare function snapshot(facts: SquareRead, now: number): SquareSnapshot;
|
|
21
|
+
export declare function recoveryObservation(facts: SquareRead, name: string, now: number): {
|
|
27
22
|
ownActivityCount: number;
|
|
28
23
|
hardCap: import("./model.js").HardCap;
|
|
29
24
|
holdReason?: string | undefined;
|
|
@@ -32,12 +27,12 @@ export declare function recoveryObservation(square: OpenSquare, name: string, co
|
|
|
32
27
|
activitySummaries: import("./square-facade.js").UnreadActivitySummary[];
|
|
33
28
|
standingCount: number;
|
|
34
29
|
held: boolean;
|
|
35
|
-
}
|
|
36
|
-
export declare function entryPresentation(
|
|
30
|
+
};
|
|
31
|
+
export declare function entryPresentation(facts: SquareRead, name: string, lastN?: number | null): EntryPresentation;
|
|
37
32
|
type ParticipantQueryContext = Pick<SquareObservation, 'location' | 'now' | 'automaticDeliveryAvailable'>;
|
|
38
33
|
export declare function queryParticipants(facts: SquareRead, context: ParticipantQueryContext, observe?: boolean): ParticipantsResult;
|
|
39
|
-
export declare function listPresentation(
|
|
34
|
+
export declare function listPresentation(facts: SquareRead): ListPresentation;
|
|
40
35
|
export declare function queryStatus(facts: SquareRead, context: ParticipantQueryContext, viewer?: string, observe?: boolean): StatusResult;
|
|
41
|
-
export declare function catchPresence(
|
|
42
|
-
export declare function streamProjection(
|
|
43
|
-
export declare function streamTailProjection(
|
|
36
|
+
export declare function catchPresence(facts: SquareRead, now: number): CatchPresence;
|
|
37
|
+
export declare function streamProjection(facts: SquareRead, cursor: number, recipient?: string): StreamProjection;
|
|
38
|
+
export declare function streamTailProjection(facts: SquareRead, last?: number, recipient?: string): StreamProjection;
|
package/dist/views.js
CHANGED
|
@@ -18,6 +18,7 @@ export function toPublicActivity(stored) {
|
|
|
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 } : {}),
|
|
20
20
|
...('body' in stored && stored.body !== undefined ? { body: stored.body } : {}),
|
|
21
|
+
...(stored.kind === 'say' && stored.metadata !== undefined ? { metadata: stored.metadata } : {}),
|
|
21
22
|
mentions: stored.kind === 'say' ? stored.mentions ?? [] : [], ...('target' in stored ? { target: stored.target } : {}),
|
|
22
23
|
...(stored.kind === 'say' && stored.reply !== undefined ? { reply: formatActivityId(stored.reply) } : {}) };
|
|
23
24
|
}
|
|
@@ -104,46 +105,34 @@ export function selectHistory(facts, query, limit, kinds, presentation = true) {
|
|
|
104
105
|
export function publicActivities(activities) {
|
|
105
106
|
return activities.flatMap((activity) => activity.kind === 'read' || activity.actor === undefined ? [] : [toPublicActivity(activity)]);
|
|
106
107
|
}
|
|
107
|
-
export async function resolveParticipant(square, name, control) {
|
|
108
|
-
return (await square.artifact.read((facts) => ({ name: resolveKnownName(facts, name) }), control?.signal)).value;
|
|
109
|
-
}
|
|
110
|
-
export async function currentParticipant(square, name, control) {
|
|
111
|
-
return (await square.artifact.read((facts) => { const p = facts.participant(name); return p?.joined ? p.name : undefined; }, control?.signal)).value;
|
|
112
|
-
}
|
|
113
108
|
function statuses(facts, now) {
|
|
114
109
|
return coreParticipants(facts, now).filter((p) => p.state !== 'not joined').map((p) => {
|
|
115
110
|
const cursor = readCursor(facts, p.name);
|
|
116
111
|
return { name: p.name, state: p.state === 'done' ? 'done' : 'joined', consumedThrough: cursor < 0 ? null : formatActivityId(cursor), watching: p.presence === 'watching', listening: p.listening };
|
|
117
112
|
});
|
|
118
113
|
}
|
|
119
|
-
export
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
},
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
return { joinedBefore: facts.activityCount({ kinds: ['join'], actors: [name] }) > 1,
|
|
142
|
-
scene: header.warmup.join('\n').trim(),
|
|
143
|
-
joinContext: (header.preamble.at(-1) === '---' ? header.preamble.slice(0, -1) : header.preamble).join('\n').trim(), recentActivities,
|
|
144
|
-
perceptions: Object.fromEntries(recentActivities.map((a) => [a.index, perceiveActivity(facts, a, name)])),
|
|
145
|
-
sayNumbers: facts.sayOrdinals(recentActivities.map((a) => a.index)) };
|
|
146
|
-
}, control?.signal)).value;
|
|
114
|
+
export function snapshot(facts, now) {
|
|
115
|
+
const header = facts.header(), hold = facts.hold();
|
|
116
|
+
const names = new Map(facts.roster().map((p) => [nameKey(p.name), p.name]));
|
|
117
|
+
const receipts = new Set(facts.receiptAssignments().map((r) => JSON.stringify([r.owner, r.index])));
|
|
118
|
+
return { context: [...header.preamble, ...header.warmup].join('\n'), actCount: facts.activityCount({ kinds: NON_READ }), hardCap: header.hardCap,
|
|
119
|
+
...(header.throttlePerMinute === undefined ? {} : { throttlePerMinute: header.throttlePerMinute }),
|
|
120
|
+
held: hold.active && hold.actor !== undefined ? { by: hold.actor, ...(hold.reason === undefined ? {} : { reason: hold.reason }) } : null,
|
|
121
|
+
participants: statuses(facts, now), delivered(name, id) { return receipts.has(JSON.stringify([names.get(nameKey(name)) ?? name, parseRequiredActivityId(id)])); } };
|
|
122
|
+
}
|
|
123
|
+
export function recoveryObservation(facts, name, now) {
|
|
124
|
+
const known = resolveKnownName(facts, name), hold = facts.hold();
|
|
125
|
+
return { participant: known, roster: rosterNames(facts), activitySummaries: unreadActivitySummaries(facts, known, now), standingCount: inSquareCount(facts), held: hold.active,
|
|
126
|
+
...(hold.reason === undefined ? {} : { holdReason: hold.reason }), ownActivityCount: facts.participant(known).sayCount, hardCap: facts.header().hardCap };
|
|
127
|
+
}
|
|
128
|
+
export function entryPresentation(facts, name, lastN = HISTORY_DEFAULT_LIMIT) {
|
|
129
|
+
const header = facts.header();
|
|
130
|
+
const recentActivities = selectHistory(facts, {}, lastN, ['say', 'done']).activities;
|
|
131
|
+
return { joinedBefore: facts.activityCount({ kinds: ['join'], actors: [name] }) > 1,
|
|
132
|
+
scene: header.warmup.join('\n').trim(),
|
|
133
|
+
joinContext: (header.preamble.at(-1) === '---' ? header.preamble.slice(0, -1) : header.preamble).join('\n').trim(), recentActivities,
|
|
134
|
+
perceptions: Object.fromEntries(recentActivities.map((a) => [a.index, perceiveActivity(facts, a, name)])),
|
|
135
|
+
sayNumbers: facts.sayOrdinals(recentActivities.map((a) => a.index)) };
|
|
147
136
|
}
|
|
148
137
|
function classifyPresence(participants, now) {
|
|
149
138
|
const here = participants.filter((participant) => participant.state === 'active' && (participant.presence === 'watching'
|
|
@@ -159,18 +148,17 @@ export function queryParticipants(facts, context, observe = false) {
|
|
|
159
148
|
return { participants, ...(!observe ? {} : { observation: { ...context, ...classifyPresence(participants, context.now),
|
|
160
149
|
standingCount: inSquareCount(facts), held: facts.hold().active } }) };
|
|
161
150
|
}
|
|
162
|
-
export
|
|
163
|
-
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
return { context: facts.header().preamble, participants, activities: facts.activityCount({ kinds: ['say'] }) };
|
|
169
|
-
}, control?.signal)).value;
|
|
151
|
+
export function listPresentation(facts) {
|
|
152
|
+
const participants = facts.roster().filter((p) => p.joined).map((p) => {
|
|
153
|
+
const receiptAt = facts.receiptFacts(p.name, p.lastJoinIndex ?? -1).latestAt;
|
|
154
|
+
return { name: p.name, lastActiveAt: receiptAt === undefined ? p.lastActiveAt : Math.max(p.lastActiveAt ?? -Infinity, receiptAt) };
|
|
155
|
+
}).sort(compareParticipantActivity).map((p) => p.name);
|
|
156
|
+
return { context: facts.header().preamble, participants, activities: facts.activityCount({ kinds: ['say'] }) };
|
|
170
157
|
}
|
|
171
158
|
export function queryStatus(facts, context, viewer = '', observe = false) {
|
|
172
159
|
const participants = coreParticipants(facts, context.now);
|
|
173
160
|
const header = facts.header(), hold = facts.hold(), standingCount = inSquareCount(facts);
|
|
161
|
+
const listener = participants.find((p) => p.state !== 'not joined' && nameKey(p.name) === nameKey(viewer));
|
|
174
162
|
const latest = facts.activityHeads({ kinds: ['say', 'done'] }, { edge: 'newest', limit: 1 }).items[0];
|
|
175
163
|
const latestAct = latest === undefined ? undefined : facts.activity(latest.index);
|
|
176
164
|
const data = {
|
|
@@ -182,6 +170,7 @@ export function queryStatus(facts, context, viewer = '', observe = false) {
|
|
|
182
170
|
...(hold.reason === undefined ? {} : { reason: hold.reason }),
|
|
183
171
|
...(hold.at === undefined ? {} : { at: hold.at }),
|
|
184
172
|
} : null,
|
|
173
|
+
...(listener === undefined ? {} : { listening: listener.listening }),
|
|
185
174
|
participants: participants.filter((p) => p.state !== 'not joined'), now: context.now,
|
|
186
175
|
...(latestAct === undefined || latestAct.actor === undefined || latestAct.kind === 'read' ? {} : { latestActivity: toPublicActivity(latestAct) }),
|
|
187
176
|
};
|
|
@@ -204,33 +193,26 @@ export function queryStatus(facts, context, viewer = '', observe = false) {
|
|
|
204
193
|
},
|
|
205
194
|
};
|
|
206
195
|
}
|
|
207
|
-
export
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|| (participant.lastActiveAt !== undefined && now - participant.lastActiveAt <= PRESENCE_WINDOW_MS));
|
|
212
|
-
return { participants, now };
|
|
213
|
-
}, control?.signal)).value;
|
|
214
|
-
}
|
|
215
|
-
export async function streamProjection(square, cursor, recipient, control) {
|
|
216
|
-
return (await square.artifact.read((facts) => {
|
|
217
|
-
const page = facts.activityHeads({ afterIndex: cursor }, { edge: 'oldest', limit: PAGE_SIZE });
|
|
218
|
-
const heads = page.items.flatMap((head) => {
|
|
219
|
-
const route = recipient === undefined ? undefined : facts.directedAt(recipient, head.index)?.route;
|
|
220
|
-
return recipient !== undefined && route === undefined ? [] : [{ index: head.index, route }];
|
|
221
|
-
});
|
|
222
|
-
return { activities: facts.activities(heads.map((h) => h.index)).map((activity) => {
|
|
223
|
-
const route = heads.find((h) => h.index === activity.index).route;
|
|
224
|
-
return { activity, ...(route === undefined ? {} : { route }) };
|
|
225
|
-
}), cursor: page.items.at(-1)?.index ?? cursor, hasMore: page.hasMore };
|
|
226
|
-
}, control?.signal)).value;
|
|
196
|
+
export function catchPresence(facts, now) {
|
|
197
|
+
const participants = coreParticipants(facts, now).filter((participant) => participant.state === 'done' || participant.presence === 'watching'
|
|
198
|
+
|| (participant.lastActiveAt !== undefined && now - participant.lastActiveAt <= PRESENCE_WINDOW_MS));
|
|
199
|
+
return { participants, now };
|
|
227
200
|
}
|
|
228
|
-
export
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
201
|
+
export function streamProjection(facts, cursor, recipient) {
|
|
202
|
+
const page = facts.activityHeads({ afterIndex: cursor }, { edge: 'oldest', limit: PAGE_SIZE });
|
|
203
|
+
const heads = page.items.flatMap((head) => {
|
|
204
|
+
const route = recipient === undefined ? undefined : facts.directedAt(recipient, head.index)?.route;
|
|
205
|
+
return recipient !== undefined && route === undefined ? [] : [{ index: head.index, route }];
|
|
206
|
+
});
|
|
207
|
+
return { activities: facts.activities(heads.map((h) => h.index)).map((activity) => {
|
|
208
|
+
const route = heads.find((h) => h.index === activity.index).route;
|
|
209
|
+
return { activity, ...(route === undefined ? {} : { route }) };
|
|
210
|
+
}), cursor: page.items.at(-1)?.index ?? cursor, hasMore: page.hasMore };
|
|
211
|
+
}
|
|
212
|
+
export function streamTailProjection(facts, last = HISTORY_DEFAULT_LIMIT, recipient) {
|
|
213
|
+
if (!Number.isSafeInteger(last) || last < 0 || last > PAGE_SIZE)
|
|
214
|
+
throw new SquareError('invalid_args', `Invalid stream tail: expected a non-negative safe integer no greater than ${PAGE_SIZE}.`);
|
|
215
|
+
const heads = last === 0 ? [] : recipient === undefined ? facts.activityHeads({}, { edge: 'newest', limit: last }).items : facts.directed(recipient, {}, { edge: 'newest', limit: last }).items;
|
|
216
|
+
return { activities: facts.activities(heads.map((h) => h.index)).map((activity) => ({ activity,
|
|
217
|
+
...(recipient === undefined ? {} : { route: facts.directedAt(recipient, activity.index).route }) })), cursor: facts.header().lastActIndex, hasMore: false };
|
|
236
218
|
}
|
package/dist/wakes.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type WatchLease, type WatchOptions } from './model.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { SquareArtifactPort } from './ports.js';
|
|
3
|
+
import type { Seat } from './model.js';
|
|
3
4
|
export type WatchLeaseStart = {
|
|
4
5
|
readonly type: 'started';
|
|
5
6
|
readonly leaseId: string;
|
|
@@ -21,7 +22,7 @@ export type WatchLeasePulse = {
|
|
|
21
22
|
readonly type: 'sleep';
|
|
22
23
|
readonly heartbeatAt?: number;
|
|
23
24
|
};
|
|
24
|
-
export declare function acquireWatchLease(
|
|
25
|
-
export declare function pulseWatchLease(
|
|
26
|
-
export declare function releaseWatchLease(
|
|
27
|
-
export declare function ownsWatchLease(
|
|
25
|
+
export declare function acquireWatchLease(artifact: SquareArtifactPort, seat: Seat, at: number, leaseId: string, options: WatchOptions, signal?: AbortSignal): Promise<WatchLeaseStart>;
|
|
26
|
+
export declare function pulseWatchLease(artifact: SquareArtifactPort, seat: Seat, at: number, leaseId: string, options: WatchOptions, heartbeatDue: boolean, signal?: AbortSignal): Promise<WatchLeasePulse>;
|
|
27
|
+
export declare function releaseWatchLease(artifact: SquareArtifactPort, seat: Seat, leaseId: string): Promise<void>;
|
|
28
|
+
export declare function ownsWatchLease(artifact: SquareArtifactPort, seat: Seat, at: number, leaseId: string, signal?: AbortSignal): Promise<boolean>;
|