@astrosheep/square 0.3.69 → 0.3.71
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/activity-feed.d.ts +4 -5
- package/dist/activity-feed.js +14 -10
- package/dist/artifact.d.ts +26 -33
- package/dist/artifact.js +346 -317
- package/dist/attention-presentation.js +2 -9
- package/dist/automatic-session.js +6 -6
- package/dist/boundary-presentation.js +3 -3
- package/dist/catch-decisions.d.ts +19 -12
- package/dist/catch-decisions.js +32 -68
- package/dist/claude-delivery.js +7 -7
- package/dist/claude-mod.js +18 -7
- package/dist/cli/catch-presentation.d.ts +7 -0
- package/dist/cli/catch-presentation.js +55 -0
- package/dist/cli/context.d.ts +1 -1
- package/dist/cli/context.js +5 -23
- package/dist/cli/express-presentation.d.ts +10 -0
- package/dist/cli/express-presentation.js +196 -0
- package/dist/cli/maintenance-commands.d.ts +4 -0
- package/dist/cli/maintenance-commands.js +29 -6
- package/dist/cli/observation-commands.d.ts +7 -5
- package/dist/cli/observation-commands.js +275 -262
- package/dist/cli/program.d.ts +1 -1
- package/dist/cli/program.js +43 -56
- package/dist/cli/registry.js +2 -1
- package/dist/cli/square-commands.d.ts +3 -9
- package/dist/cli/square-commands.js +134 -255
- package/dist/codex-queue.js +4 -1
- package/dist/decisions.d.ts +26 -74
- package/dist/decisions.js +190 -335
- package/dist/delivery-health.js +13 -22
- package/dist/delivery-operations.d.ts +0 -11
- package/dist/delivery-operations.js +44 -33
- package/dist/delivery.d.ts +33 -32
- package/dist/delivery.js +70 -81
- package/dist/harness-claude.js +1 -1
- package/dist/harness-codex.js +1 -1
- package/dist/harness-pi.js +1 -1
- package/dist/help.js +6 -1
- package/dist/inbox.d.ts +27 -0
- package/dist/inbox.js +55 -18
- package/dist/index.d.ts +2 -4
- package/dist/index.js +1 -1
- package/dist/list.d.ts +7 -1
- package/dist/list.js +49 -122
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +7 -37
- package/dist/model.d.ts +4 -44
- package/dist/model.js +1 -3
- package/dist/notifications.d.ts +1 -1
- package/dist/notifications.js +13 -7
- package/dist/open-square.d.ts +16 -2
- package/dist/open-square.js +40 -1
- package/dist/perception-projection.d.ts +6 -15
- package/dist/perception-projection.js +6 -26
- package/dist/{opencode-delivery.d.ts → plugins/opencode/delivery.d.ts} +1 -1
- package/dist/{opencode-delivery.js → plugins/opencode/delivery.js} +18 -18
- package/dist/{opencode.d.ts → plugins/opencode/index.d.ts} +2 -2
- package/dist/{opencode.js → plugins/opencode/index.js} +9 -9
- package/dist/ports.d.ts +156 -11
- package/dist/presentation-operations.d.ts +1 -1
- package/dist/presentation-operations.js +35 -44
- package/dist/presentation.d.ts +12 -19
- package/dist/presentation.js +33 -54
- package/dist/routes.d.ts +7 -14
- package/dist/routes.js +33 -62
- package/dist/runtime.d.ts +12 -39
- package/dist/runtime.js +33 -164
- package/dist/square-actions.d.ts +11 -21
- package/dist/square-actions.js +171 -271
- package/dist/square-application.d.ts +31 -55
- package/dist/square-application.js +637 -141
- package/dist/square-core.d.ts +3 -94
- package/dist/square-core.js +8 -353
- package/dist/square-domain.d.ts +24 -0
- package/dist/square-domain.js +77 -0
- package/dist/square-facade.d.ts +360 -9
- package/dist/square-migration.d.ts +24 -0
- package/dist/square-migration.js +211 -0
- package/dist/square-projections.d.ts +2 -42
- package/dist/square-projections.js +1 -37
- package/dist/square-sqlite.d.ts +59 -0
- package/dist/square-sqlite.js +702 -0
- package/dist/stream.d.ts +3 -10
- package/dist/stream.js +81 -52
- package/dist/views.d.ts +27 -95
- package/dist/views.js +217 -87
- package/dist/wake-eligibility.d.ts +15 -5
- package/dist/wake-eligibility.js +32 -16
- package/dist/wakes.d.ts +3 -3
- package/dist/wakes.js +31 -27
- package/guides/application-facade-delivery-evidence.md +200 -0
- package/guides/application-facade-design.md +95 -0
- package/guides/change-waits.md +4 -4
- package/guides/migration.md +77 -0
- package/guides/pi-native-delivery.md +1 -1
- package/guides/primitive-catch-consolidation.md +85 -0
- package/guides/primitive-history-consolidation.md +76 -0
- package/guides/primitive-status-consolidation.md +87 -0
- package/guides/primitive-storage-consolidation.md +128 -0
- package/guides/row-runtime-delivery-evidence.md +160 -0
- package/guides/row-storage-design.md +567 -0
- package/package.json +5 -7
- package/{claude-plugin → plugins/claude}/.claude-plugin/plugin.json +1 -1
- package/{codex-plugin → plugins/codex}/.codex-plugin/plugin.json +1 -1
- package/plugins/opencode/delivery.ts +149 -0
- package/plugins/opencode/index.ts +136 -0
- package/plugins/paseo/OVERVIEW.md +14 -0
- package/plugins/paseo/README.md +54 -0
- package/plugins/paseo/client/square-message.tsx +106 -0
- package/plugins/paseo/client/transform.ts +24 -0
- package/plugins/paseo/index.client.tsx +19 -0
- package/plugins/paseo/package.json +19 -0
- package/plugins/paseo/paseo-plugin.json +7 -0
- package/plugins/paseo/shared/message.ts +29 -0
- package/{extensions → plugins/pi}/square-pi.js +8 -5
- package/dist/activity.d.ts +0 -14
- package/dist/activity.js +0 -248
- package/dist/square-file-adapter.d.ts +0 -26
- package/dist/square-file-adapter.js +0 -85
- package/dist/square-state.d.ts +0 -6
- package/dist/square-state.js +0 -138
- package/dist/square-storage.d.ts +0 -14
- package/dist/square-storage.js +0 -188
- package/dist/square-wiring.d.ts +0 -33
- package/dist/square-wiring.js +0 -109
- package/dist/watch.d.ts +0 -8
- package/dist/watch.js +0 -252
- /package/{claude-plugin → plugins/claude}/hooks/hooks.json +0 -0
- /package/{claude-plugin → plugins/claude}/hooks/register.js +0 -0
- /package/{claude-plugin → plugins/claude}/skills/square/SKILL.md +0 -0
- /package/{claude-plugin → plugins/claude}/types.d.ts +0 -0
- /package/{codex-plugin → plugins/codex}/hooks/hooks.json +0 -0
package/dist/activity.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export interface ActivityOptions {
|
|
2
|
-
cwd?: string;
|
|
3
|
-
env?: NodeJS.ProcessEnv;
|
|
4
|
-
force?: boolean;
|
|
5
|
-
noWait?: boolean;
|
|
6
|
-
noMention?: boolean;
|
|
7
|
-
mentions?: readonly string[];
|
|
8
|
-
reach?: import('./model.js').Reach;
|
|
9
|
-
reply?: string;
|
|
10
|
-
}
|
|
11
|
-
export declare function saveActivityDraft(squarePath: string, name: string, body: string): string;
|
|
12
|
-
/** Retry commands preserve the original reach, reply, wait and force choices. */
|
|
13
|
-
export declare function activityRetryCommand(squarePath: string, name: string, opts: ActivityOptions, force?: boolean): string;
|
|
14
|
-
export declare function cmdActivity(squarePath: string, name: string, activity: string, resolveBody: (arg: string) => string, opts: ActivityOptions): Promise<void>;
|
package/dist/activity.js
DELETED
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
import crypto from 'node:crypto';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { SquareError, isSquareError, validateName } from './model.js';
|
|
5
|
-
import { expressHintLine, formatRefusal, renderActivityBlocked, renderActivityLimit, renderActivityUncertain, renderExpressNoWait, renderExpressWaiting, renderPendingFeed, joinRecoveryCommand, participantIdentity, participantsRecoveryCommand, participantCommandPrefix, commandPrefix, quoteShell, takeoverRecoveryLines, withPathOutput, } from './presentation.js';
|
|
6
|
-
import { nowMs, SLEEP_MS } from './runtime.js';
|
|
7
|
-
import { unreadActivitySummaries } from './decisions.js';
|
|
8
|
-
import { openSquare } from './square-file-adapter.js';
|
|
9
|
-
import { closeOpenSquare } from './open-square.js';
|
|
10
|
-
import { createSquareApplication } from './square-application.js';
|
|
11
|
-
import { activityPresentation, resolveParticipant } from './views.js';
|
|
12
|
-
import { formatActivityId, parseActivityId } from './square-core.js';
|
|
13
|
-
import { formatDuration } from './time.js';
|
|
14
|
-
import { style } from './tty-style.js';
|
|
15
|
-
import { hostLedgerForEnv } from './registry.js';
|
|
16
|
-
import { createDefaultWakeTransport } from './notifications.js';
|
|
17
|
-
export function saveActivityDraft(squarePath, name, body) {
|
|
18
|
-
const draftDir = path.join(path.dirname(squarePath), 'drafts');
|
|
19
|
-
fs.mkdirSync(draftDir, { recursive: true });
|
|
20
|
-
const hash = crypto.createHash('sha256').update(body).digest('hex').slice(0, 8);
|
|
21
|
-
const stamp = new Date(nowMs()).toISOString().replace(/\.\d{3}Z$/, 'Z').replace(/[:.]/g, '-');
|
|
22
|
-
const namePart = name.replace(/[^\p{L}\p{N}_-]+/gu, '-').replace(/^-+|-+$/g, '') || 'participant';
|
|
23
|
-
const base = `${stamp}-${namePart}-${hash}`;
|
|
24
|
-
for (let i = 0;; i++) {
|
|
25
|
-
const filename = `${base}${i === 0 ? '' : `-${i}`}.md`;
|
|
26
|
-
const target = path.join(draftDir, filename);
|
|
27
|
-
try {
|
|
28
|
-
fs.writeFileSync(target, body, { flag: 'wx' });
|
|
29
|
-
return target;
|
|
30
|
-
}
|
|
31
|
-
catch (error) {
|
|
32
|
-
if (error.code !== 'EEXIST')
|
|
33
|
-
throw error;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
/** Retry commands preserve the original reach, reply, wait and force choices. */
|
|
38
|
-
export function activityRetryCommand(squarePath, name, opts, force = opts.force ?? false) {
|
|
39
|
-
const reach = opts.reach === 'bell' ? ' --bell'
|
|
40
|
-
: opts.mentions?.length ? opts.mentions.map((target) => ` --mention ${quoteShell(target)}`).join('')
|
|
41
|
-
: opts.noMention === true ? ' --no-mention'
|
|
42
|
-
: '';
|
|
43
|
-
const replyIndex = opts.reply === undefined ? undefined : parseActivityId(opts.reply);
|
|
44
|
-
const reply = opts.reply === undefined ? '' : ` --reply ${replyIndex === undefined ? quoteShell(opts.reply) : formatActivityId(replyIndex)}`;
|
|
45
|
-
return `${participantCommandPrefix(squarePath, name)} express${force ? ' --force' : ''}${opts.noWait ? ' --no-wait' : ''}${reach}${reply} -`;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* A recipient conflict is the speaker's to edit: the printed recovery drops every
|
|
49
|
-
* mention flag (or keeps the bell) so the retry never repeats the rejected target.
|
|
50
|
-
*/
|
|
51
|
-
function mentionRepairCommand(squarePath, name, opts, bell) {
|
|
52
|
-
const replyIndex = opts.reply === undefined ? undefined : parseActivityId(opts.reply);
|
|
53
|
-
const reply = opts.reply === undefined ? '' : ` --reply ${replyIndex === undefined ? quoteShell(opts.reply) : formatActivityId(replyIndex)}`;
|
|
54
|
-
const reach = bell ? ' --bell' : ' --no-mention';
|
|
55
|
-
return `${participantCommandPrefix(squarePath, name)} express${opts.force ? ' --force' : ''}${opts.noWait ? ' --no-wait' : ''}${reach}${reply} -`;
|
|
56
|
-
}
|
|
57
|
-
/** A body that is nothing but *gestures* reads as an action, not as speech. */
|
|
58
|
-
function isGestureOnly(body) {
|
|
59
|
-
return /\*[^*]*\*/.test(body) && body.replace(/\*[^*]*\*/g, '').trim() === '';
|
|
60
|
-
}
|
|
61
|
-
export async function cmdActivity(squarePath, name, activity, resolveBody, opts) {
|
|
62
|
-
const rawInput = String(resolveBody(activity));
|
|
63
|
-
const env = { ...(opts.env ?? process.env) };
|
|
64
|
-
const hostLedger = hostLedgerForEnv(env);
|
|
65
|
-
const retryCommand = activityRetryCommand(squarePath, name, opts);
|
|
66
|
-
const forceCommand = activityRetryCommand(squarePath, name, opts, true);
|
|
67
|
-
let knownName = name;
|
|
68
|
-
let callerMissing = false;
|
|
69
|
-
let expressAttempted = false;
|
|
70
|
-
let landedId;
|
|
71
|
-
let announcedWait;
|
|
72
|
-
async function presentation() {
|
|
73
|
-
const square = await openSquare(squarePath, { clock: nowMs, env, hostLedger });
|
|
74
|
-
try {
|
|
75
|
-
return await activityPresentation(square, knownName);
|
|
76
|
-
}
|
|
77
|
-
finally {
|
|
78
|
-
await closeOpenSquare(square);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
try {
|
|
82
|
-
validateName(name);
|
|
83
|
-
for (const target of opts.mentions ?? [])
|
|
84
|
-
validateName(target);
|
|
85
|
-
if (opts.reply !== undefined && parseActivityId(opts.reply) === undefined) {
|
|
86
|
-
throw new SquareError('invalid_args', 'Invalid --reply: expected an activity id like act/12');
|
|
87
|
-
}
|
|
88
|
-
const reader = await openSquare(squarePath, { clock: nowMs, env, hostLedger });
|
|
89
|
-
try {
|
|
90
|
-
knownName = (await resolveParticipant(reader, name)).name;
|
|
91
|
-
}
|
|
92
|
-
catch (error) {
|
|
93
|
-
// The caller name was validated above; invalid_args here means it is absent
|
|
94
|
-
// from the roster. Keep the join recovery distinct from recipient validation.
|
|
95
|
-
callerMissing = isSquareError(error) && error.code === 'invalid_args';
|
|
96
|
-
throw error;
|
|
97
|
-
}
|
|
98
|
-
finally {
|
|
99
|
-
await closeOpenSquare(reader);
|
|
100
|
-
}
|
|
101
|
-
const application = createSquareApplication({ cwd: opts.cwd ?? process.cwd(), env, squarePath, clock: nowMs, hostLedger, wakeTransport: await createDefaultWakeTransport(hostLedger, nowMs), participant: name });
|
|
102
|
-
// The caller is roster-known at this point, so a landed join act is always a
|
|
103
|
-
// re-entry after done — never a first arrival. Announce it; a silent return
|
|
104
|
-
// would make `done` a fake exit.
|
|
105
|
-
const entry = await application.join();
|
|
106
|
-
const reentered = entry.kind === 'joined' && entry.activity !== null;
|
|
107
|
-
const before = await presentation();
|
|
108
|
-
const pendingPublic = before.pendingPublic;
|
|
109
|
-
expressAttempted = true;
|
|
110
|
-
const landed = await application.express(rawInput.replace(/\r\n/g, '\n').trim(), {
|
|
111
|
-
force: opts.force ?? false,
|
|
112
|
-
noWait: opts.noWait ?? false,
|
|
113
|
-
...(opts.mentions === undefined ? {} : { mentions: opts.mentions }),
|
|
114
|
-
...(opts.noMention === undefined ? {} : { noMention: opts.noMention }),
|
|
115
|
-
...(opts.reach === undefined ? {} : { reach: opts.reach }),
|
|
116
|
-
// The action boundary validates the raw id before attempting a commit.
|
|
117
|
-
...(opts.reply === undefined ? {} : { reply: opts.reply }),
|
|
118
|
-
}, {
|
|
119
|
-
onProgress: (progress) => {
|
|
120
|
-
if (announcedWait === progress.reason)
|
|
121
|
-
return;
|
|
122
|
-
const delayMs = progress.delayMs ?? SLEEP_MS;
|
|
123
|
-
process.stdout.write(renderExpressWaiting({
|
|
124
|
-
reason: progress.reason,
|
|
125
|
-
...(progress.reason === 'throttled' ? { delayMs } : {}),
|
|
126
|
-
...(progress.holder === undefined ? {} : { holder: progress.holder }),
|
|
127
|
-
...(progress.holdReason === undefined ? {} : { holdReason: progress.holdReason }),
|
|
128
|
-
}) + '\n');
|
|
129
|
-
announcedWait = progress.reason;
|
|
130
|
-
},
|
|
131
|
-
});
|
|
132
|
-
landedId = landed.activity.id;
|
|
133
|
-
const fresh = await presentation();
|
|
134
|
-
const headerCount = fresh.participantCount;
|
|
135
|
-
const held = fresh.held;
|
|
136
|
-
const ownActCount = fresh.ownActivityCount;
|
|
137
|
-
const hasPending = pendingPublic.length > 0;
|
|
138
|
-
const pendingSpeaker = pendingPublic[0]?.actor;
|
|
139
|
-
const pendingLead = pendingSpeaker === undefined
|
|
140
|
-
? ''
|
|
141
|
-
: `▲ while you spoke, ${participantIdentity(pendingSpeaker)} said something behind you\n\n`;
|
|
142
|
-
const pending = hasPending ? `\n\n${pendingLead}${renderPendingFeed([...fresh.activities], [...pendingPublic], knownName, fresh.state)}` : '';
|
|
143
|
-
const hint = expressHintLine(ownActCount);
|
|
144
|
-
const receiptId = landed.activity.id;
|
|
145
|
-
const capCount = fresh.hardCap === null ? '' : ` · ${ownActCount}/${fresh.hardCap}`;
|
|
146
|
-
const confirmation = landed.activity.reach === 'bell'
|
|
147
|
-
? `● you ring the bell — everyone turns · ${receiptId}${style('dim', capCount)}`
|
|
148
|
-
: `● ${landed.activity.mentions.length > 0
|
|
149
|
-
? `you walk over to ${landed.activity.mentions.map((target) => participantIdentity(target)).join(', ')}`
|
|
150
|
-
: isGestureOnly(landed.activity.body ?? '')
|
|
151
|
-
? 'your gesture lands'
|
|
152
|
-
: 'your words land'} — ${receiptId}${style('dim', capCount)}`;
|
|
153
|
-
const withHint = hint ? `${confirmation}\n${style('dim', hint)}` : confirmation;
|
|
154
|
-
const replyNotice = landed.replyAuthorNotHere === undefined
|
|
155
|
-
? ''
|
|
156
|
-
: `\n○ ${participantIdentity(landed.replyAuthorNotHere)} isn't in the square right now — the reply landed without reaching them`;
|
|
157
|
-
const reentry = reentered ? '● you stepped back into the square\n' : '';
|
|
158
|
-
process.stdout.write(withPathOutput(squarePath, reentry + withHint + replyNotice + pending, { participantCount: headerCount, held }));
|
|
159
|
-
}
|
|
160
|
-
catch (error) {
|
|
161
|
-
// Save before any recovery read: even a broken artifact must not eat the body.
|
|
162
|
-
const draftPath = saveActivityDraft(squarePath, name, rawInput);
|
|
163
|
-
const confirmedUnsent = landedId === undefined && (!expressAttempted || (isSquareError(error) && error.facts?.activityUnsent === true));
|
|
164
|
-
const fresh = await presentation().catch(() => undefined);
|
|
165
|
-
const output = { squarePath, name: knownName, draftPath, participantCount: fresh?.participantCount, held: fresh?.held };
|
|
166
|
-
if (!confirmedUnsent) {
|
|
167
|
-
process.stderr.write(renderActivityUncertain({ ...output, landedId, detail: error instanceof Error ? error.message : String(error) }));
|
|
168
|
-
process.exit(1);
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
if (isSquareError(error) && fresh !== undefined) {
|
|
172
|
-
if (error.code === 'behind') {
|
|
173
|
-
process.stdout.write(renderActivityBlocked({
|
|
174
|
-
...output,
|
|
175
|
-
retryCommand,
|
|
176
|
-
forceCommand,
|
|
177
|
-
activitySummaries: unreadActivitySummaries(fresh.state, knownName, nowMs()),
|
|
178
|
-
}));
|
|
179
|
-
process.exit(1);
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
if (error.code === 'capped') {
|
|
183
|
-
process.stdout.write(renderActivityLimit({
|
|
184
|
-
...output,
|
|
185
|
-
count: fresh.ownActivityCount,
|
|
186
|
-
...(fresh.hardCap === null ? {} : { hardCap: fresh.hardCap }),
|
|
187
|
-
}));
|
|
188
|
-
process.exit(1);
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
if (error.code === 'throttled' || error.code === 'held') {
|
|
192
|
-
process.stdout.write(renderExpressNoWait({
|
|
193
|
-
...output,
|
|
194
|
-
reason: error.code,
|
|
195
|
-
delayMs: error.facts?.retryAfterMs ?? SLEEP_MS,
|
|
196
|
-
...(error.facts?.holder === undefined ? {} : { holder: error.facts.holder }),
|
|
197
|
-
...(error.facts?.holdReason === undefined ? {} : { holdReason: error.facts.holdReason }),
|
|
198
|
-
retryCommand,
|
|
199
|
-
}));
|
|
200
|
-
process.exit(1);
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
if (error.code === 'bell_quota') {
|
|
204
|
-
process.stdout.write(withPathOutput(squarePath, [
|
|
205
|
-
'✕ the bell stays quiet for now',
|
|
206
|
-
` · you can ring it again in ${formatDuration(error.facts?.retryAfterMs ?? 1)}`,
|
|
207
|
-
`· draft kept: ${draftPath}`,
|
|
208
|
-
` · retry when the bell is ready:`,
|
|
209
|
-
`${retryCommand} < ${quoteShell(draftPath)}`,
|
|
210
|
-
].join('\n'), output));
|
|
211
|
-
process.exit(1);
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
const lines = [error instanceof Error ? error.message : String(error), `· draft kept: ${draftPath}`];
|
|
216
|
-
const reason = isSquareError(error) ? error.facts?.reason : undefined;
|
|
217
|
-
// `not_standing` is a flag mention; the body-scan and bell conflicts carry an @name in the draft.
|
|
218
|
-
const mentionRepair = reason === 'bell_mention_conflict' ? { bell: true, fromBody: true }
|
|
219
|
-
: reason === 'unmatched_mention' ? { bell: false, fromBody: true }
|
|
220
|
-
: reason === 'not_standing' ? { bell: false, fromBody: false }
|
|
221
|
-
: undefined;
|
|
222
|
-
if (isSquareError(error)) {
|
|
223
|
-
if (callerMissing || error.code === 'unknown_participant' || error.code === 'not_joined')
|
|
224
|
-
lines.push(joinRecoveryCommand(squarePath, name));
|
|
225
|
-
if (error.code === 'already_joined')
|
|
226
|
-
lines.push(...takeoverRecoveryLines(squarePath, name));
|
|
227
|
-
if (mentionRepair !== undefined) {
|
|
228
|
-
// The draft stays, but the retry no longer carries the target that was refused.
|
|
229
|
-
lines.push(participantsRecoveryCommand(squarePath));
|
|
230
|
-
lines.push(mentionRepair.fromBody
|
|
231
|
-
? " · edit the draft first — the body's @name must be removed or wrapped in backticks:"
|
|
232
|
-
: ' · pick someone standing here, or land it bare:');
|
|
233
|
-
lines.push(`${mentionRepairCommand(squarePath, name, opts, mentionRepair.bell)} < ${quoteShell(draftPath)}`);
|
|
234
|
-
}
|
|
235
|
-
else if (error.code === 'invalid_args' || error.code === 'invalid_name') {
|
|
236
|
-
if (opts.mentions?.length)
|
|
237
|
-
lines.push(participantsRecoveryCommand(squarePath));
|
|
238
|
-
if (opts.reply !== undefined)
|
|
239
|
-
lines.push(`${commandPrefix(squarePath)} history --limit 10`);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
if (mentionRepair === undefined) {
|
|
243
|
-
lines.push(' · correct the problem above before retrying; keep your intended recipients and reply:', `${retryCommand} < ${quoteShell(draftPath)}`);
|
|
244
|
-
}
|
|
245
|
-
process.stderr.write(formatRefusal(squarePath, lines, output));
|
|
246
|
-
process.exit(2);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
export { observeSquareRevision as observeSquareChanges } from './artifact.js';
|
|
2
|
-
import { type BuildOptions, type HardCap } from './model.js';
|
|
3
|
-
import type { OpenSquare } from './open-square.js';
|
|
4
|
-
import type { OperationControl } from './square-facade.js';
|
|
5
|
-
import type { HostLedgerPort, SquareArtifactPort } from './ports.js';
|
|
6
|
-
/** File-owned artifact creation for the CLI and path-backed public facade. */
|
|
7
|
-
export declare function createSquare(squarePath: string, options: BuildOptions & {
|
|
8
|
-
hardCap: HardCap;
|
|
9
|
-
}, snippet: string): Promise<void>;
|
|
10
|
-
export interface SquareBuildOptions {
|
|
11
|
-
markdown: string;
|
|
12
|
-
hardCap?: number | null;
|
|
13
|
-
throttlePerMinute?: number;
|
|
14
|
-
clock?: () => number;
|
|
15
|
-
hostLedger?: HostLedgerPort;
|
|
16
|
-
wakeTransport?: import('./ports.js').WakeTransportPort;
|
|
17
|
-
env?: NodeJS.ProcessEnv;
|
|
18
|
-
/** One caller deadline (e.g. a native hook budget) propagated into artifact busy retries. */
|
|
19
|
-
signal?: AbortSignal;
|
|
20
|
-
}
|
|
21
|
-
export declare function openSquare(squarePath: string, options?: Pick<SquareBuildOptions, 'clock' | 'hostLedger' | 'wakeTransport' | 'env' | 'signal'>, control?: OperationControl): Promise<OpenSquare>;
|
|
22
|
-
export declare function probeSquare(squarePath: string): Promise<OpenSquare | undefined>;
|
|
23
|
-
export declare function buildSquare(squarePath: string, options: SquareBuildOptions): Promise<OpenSquare>;
|
|
24
|
-
export declare function buildMemorySquare(options: SquareBuildOptions): OpenSquare;
|
|
25
|
-
/** A projection reads and validates its snapshot once; no preliminary duplicate read. */
|
|
26
|
-
export declare function openSquareArtifact(squarePath: string, signal?: AbortSignal): SquareArtifactPort;
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
export { observeSquareRevision as observeSquareChanges } from './artifact.js';
|
|
2
|
-
import { hostLedgerRoot } from './host-ledger-root.js';
|
|
3
|
-
import { createSquareState, createSquareSnapshot, probeSquareFile, writeSquareSnapshot, createFileCell, createMemoryCell, } from './square-storage.js';
|
|
4
|
-
import { InternalSquareError, SquareError, } from './model.js';
|
|
5
|
-
import { createHostLedgerPort } from './host-ledger-file-adapter.js';
|
|
6
|
-
/** File-owned artifact creation for the CLI and path-backed public facade. */
|
|
7
|
-
export async function createSquare(squarePath, options, snippet) {
|
|
8
|
-
const state = createSquareState(options, snippet);
|
|
9
|
-
if (options.force) {
|
|
10
|
-
await writeSquareSnapshot(squarePath, state);
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
if (!await createSquareSnapshot(squarePath, state)) {
|
|
14
|
-
throw new InternalSquareError('conflict', `Refusing to overwrite existing square: ${squarePath}\nPass -f to overwrite.`);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
function validateBuildOptions(options) {
|
|
18
|
-
if (options.hardCap !== undefined && options.hardCap !== null
|
|
19
|
-
&& (!Number.isSafeInteger(options.hardCap) || options.hardCap < 1)) {
|
|
20
|
-
throw new SquareError('invalid_args', 'hardCap must be a positive integer or null');
|
|
21
|
-
}
|
|
22
|
-
if (options.throttlePerMinute !== undefined
|
|
23
|
-
&& (!Number.isSafeInteger(options.throttlePerMinute) || options.throttlePerMinute < 1)) {
|
|
24
|
-
throw new SquareError('invalid_args', 'throttlePerMinute must be a positive integer');
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
export async function openSquare(squarePath, options = {}, control) {
|
|
28
|
-
const env = options.env ?? process.env;
|
|
29
|
-
const ledgerRoot = hostLedgerRoot(env);
|
|
30
|
-
const artifact = openSquareArtifact(squarePath, options.signal);
|
|
31
|
-
try {
|
|
32
|
-
await artifact.read(control?.signal);
|
|
33
|
-
return {
|
|
34
|
-
artifact,
|
|
35
|
-
clock: options.clock ?? Date.now,
|
|
36
|
-
location: squarePath,
|
|
37
|
-
env,
|
|
38
|
-
hostLedger: options.hostLedger ?? createHostLedgerPort({
|
|
39
|
-
rootPath: ledgerRoot,
|
|
40
|
-
}),
|
|
41
|
-
wakeTransport: options.wakeTransport,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
catch (error) {
|
|
45
|
-
await artifact.close();
|
|
46
|
-
if (error instanceof InternalSquareError && error.code === 'not_found') {
|
|
47
|
-
throw new SquareError('unavailable', `Square is unavailable at ${squarePath}`);
|
|
48
|
-
}
|
|
49
|
-
throw error;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
export async function probeSquare(squarePath) {
|
|
53
|
-
const state = await probeSquareFile(squarePath);
|
|
54
|
-
return state === undefined ? undefined : { artifact: createMemoryCell(state), clock: Date.now, location: squarePath, hostLedger: createHostLedgerPort({ rootPath: hostLedgerRoot(process.env) }) };
|
|
55
|
-
}
|
|
56
|
-
export async function buildSquare(squarePath, options) {
|
|
57
|
-
validateBuildOptions(options);
|
|
58
|
-
try {
|
|
59
|
-
await createSquare(squarePath, {
|
|
60
|
-
force: false,
|
|
61
|
-
hardCap: options.hardCap ?? null,
|
|
62
|
-
...(options.throttlePerMinute === undefined ? {} : { throttlePerMinute: options.throttlePerMinute }),
|
|
63
|
-
}, options.markdown);
|
|
64
|
-
}
|
|
65
|
-
catch (error) {
|
|
66
|
-
if (error instanceof InternalSquareError && error.code === 'conflict') {
|
|
67
|
-
throw new SquareError('io', `Cannot build over an existing square at ${squarePath}`);
|
|
68
|
-
}
|
|
69
|
-
throw error;
|
|
70
|
-
}
|
|
71
|
-
return openSquare(squarePath, options);
|
|
72
|
-
}
|
|
73
|
-
export function buildMemorySquare(options) {
|
|
74
|
-
validateBuildOptions(options);
|
|
75
|
-
const squareState = createSquareState({
|
|
76
|
-
force: false,
|
|
77
|
-
hardCap: options.hardCap ?? null,
|
|
78
|
-
...(options.throttlePerMinute === undefined ? {} : { throttlePerMinute: options.throttlePerMinute }),
|
|
79
|
-
}, options.markdown);
|
|
80
|
-
return { artifact: createMemoryCell(squareState), clock: options.clock ?? Date.now, location: 'memory', hostLedger: options.hostLedger, wakeTransport: options.wakeTransport };
|
|
81
|
-
}
|
|
82
|
-
/** A projection reads and validates its snapshot once; no preliminary duplicate read. */
|
|
83
|
-
export function openSquareArtifact(squarePath, signal) {
|
|
84
|
-
return createFileCell(squarePath, signal);
|
|
85
|
-
}
|
package/dist/square-state.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { type BuildOptions, type HardCap, type SquareRuntimeState, type SquareState } from './model.js';
|
|
2
|
-
export declare function validateSquareState(value: unknown): SquareState;
|
|
3
|
-
export declare function emptyRuntimeState(nextActIndex?: number): SquareRuntimeState;
|
|
4
|
-
export declare function createSquareState(options: BuildOptions & {
|
|
5
|
-
hardCap: HardCap;
|
|
6
|
-
}, snippet: string): SquareState;
|
package/dist/square-state.js
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import { isWakeRouteKind, SquareError, } from './model.js';
|
|
3
|
-
import { parseActivityId } from './square-core.js';
|
|
4
|
-
const guideNames = ['participant', 'architect', 'brainstorm'];
|
|
5
|
-
const guideContents = new Map(await Promise.all(guideNames.map(async (name) => [name, (await fs.promises.readFile(new URL(`../guides/${name}.md`, import.meta.url), 'utf8')).trim()])));
|
|
6
|
-
function invalidArtifact(detail) {
|
|
7
|
-
return new SquareError('invalid_args', `Invalid square artifact: ${detail}`);
|
|
8
|
-
}
|
|
9
|
-
function isObject(value) {
|
|
10
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
11
|
-
}
|
|
12
|
-
function hasExactKeys(value, required, optional = []) {
|
|
13
|
-
const allowed = new Set([...required, ...optional]);
|
|
14
|
-
return required.every((key) => Object.hasOwn(value, key)) && Object.keys(value).every((key) => allowed.has(key));
|
|
15
|
-
}
|
|
16
|
-
function isFiniteNumber(value) {
|
|
17
|
-
return typeof value === 'number' && Number.isFinite(value);
|
|
18
|
-
}
|
|
19
|
-
function isNonNegativeInteger(value) {
|
|
20
|
-
return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;
|
|
21
|
-
}
|
|
22
|
-
function isNonblankString(value) {
|
|
23
|
-
return typeof value === 'string' && value.length > 0;
|
|
24
|
-
}
|
|
25
|
-
function isStringArray(value) {
|
|
26
|
-
return Array.isArray(value) && value.every((item) => typeof item === 'string');
|
|
27
|
-
}
|
|
28
|
-
function validateWatchLease(value) {
|
|
29
|
-
if (!isObject(value) || !hasExactKeys(value, ['leaseId', 'heartbeatAt', 'expiresAt'], ['filter'])
|
|
30
|
-
|| !isNonblankString(value.leaseId) || !isFiniteNumber(value.heartbeatAt)
|
|
31
|
-
|| !isFiniteNumber(value.expiresAt) || value.expiresAt < value.heartbeatAt)
|
|
32
|
-
return false;
|
|
33
|
-
if (value.filter === undefined)
|
|
34
|
-
return true;
|
|
35
|
-
return isObject(value.filter) && hasExactKeys(value.filter, [], ['participants', 'mention'])
|
|
36
|
-
&& (value.filter.participants === undefined || isStringArray(value.filter.participants))
|
|
37
|
-
&& (value.filter.mention === undefined || typeof value.filter.mention === 'string');
|
|
38
|
-
}
|
|
39
|
-
function validateRecord(value, item) {
|
|
40
|
-
return isObject(value) && Object.entries(value).every(([key, candidate]) => key.length > 0 && item(candidate));
|
|
41
|
-
}
|
|
42
|
-
function validateRuntime(value) {
|
|
43
|
-
return isObject(value) && hasExactKeys(value, ['nextActIndex', 'observations', 'leases'])
|
|
44
|
-
&& isNonNegativeInteger(value.nextActIndex)
|
|
45
|
-
&& validateRecord(value.observations, (candidate) => isObject(candidate)
|
|
46
|
-
&& Object.entries(candidate).every(([id, observation]) => parseActivityId(id) !== undefined && isObject(observation) && hasExactKeys(observation, ['state', 'at']) && observation.state === 'seen' && isFiniteNumber(observation.at)))
|
|
47
|
-
&& validateRecord(value.leases, validateWatchLease);
|
|
48
|
-
}
|
|
49
|
-
function validateAssignedRuntimeReferences(runtime) {
|
|
50
|
-
for (const observations of Object.values(runtime.observations)) {
|
|
51
|
-
for (const id of Object.keys(observations)) {
|
|
52
|
-
const index = parseActivityId(id);
|
|
53
|
-
if (index === undefined)
|
|
54
|
-
return 'malformed';
|
|
55
|
-
if (index >= runtime.nextActIndex)
|
|
56
|
-
return 'future';
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return 'ok';
|
|
60
|
-
}
|
|
61
|
-
function validateActor(value, required) {
|
|
62
|
-
return required ? isNonblankString(value) : value === undefined || isNonblankString(value);
|
|
63
|
-
}
|
|
64
|
-
function validateStoredAct(value) {
|
|
65
|
-
if (!isObject(value) || typeof value.kind !== 'string' || !isNonNegativeInteger(value.index) || !isFiniteNumber(value.at))
|
|
66
|
-
return false;
|
|
67
|
-
switch (value.kind) {
|
|
68
|
-
case 'join': return hasExactKeys(value, ['kind', 'actor', 'at', 'index']) && validateActor(value.actor, true);
|
|
69
|
-
case 'done': return hasExactKeys(value, ['kind', 'actor', 'at', 'index'], ['body']) && validateActor(value.actor, true) && (value.body === undefined || typeof value.body === 'string');
|
|
70
|
-
case 'say': return hasExactKeys(value, ['kind', 'actor', 'at', 'body', 'index'], ['mentions', 'reach', 'reply'])
|
|
71
|
-
&& validateActor(value.actor, true) && typeof value.body === 'string'
|
|
72
|
-
&& (value.mentions === undefined || isStringArray(value.mentions)) && (value.reach === undefined || value.reach === 'bell')
|
|
73
|
-
&& (value.reply === undefined || isNonNegativeInteger(value.reply));
|
|
74
|
-
case 'hold': return hasExactKeys(value, ['kind', 'at', 'index'], ['actor', 'body']) && validateActor(value.actor, false) && (value.body === undefined || typeof value.body === 'string');
|
|
75
|
-
case 'resume': return hasExactKeys(value, ['kind', 'at', 'index'], ['actor']) && validateActor(value.actor, false);
|
|
76
|
-
case 'read': return hasExactKeys(value, ['kind', 'actor', 'at', 'through', 'index']) && validateActor(value.actor, true) && isNonNegativeInteger(value.through);
|
|
77
|
-
case 'listen':
|
|
78
|
-
case 'ignore': return hasExactKeys(value, ['kind', 'actor', 'target', 'at', 'index']) && validateActor(value.actor, true) && validateActor(value.target, true);
|
|
79
|
-
default: return false;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
function validateActs(value) {
|
|
83
|
-
if (!Array.isArray(value) || !value.every(validateStoredAct))
|
|
84
|
-
return false;
|
|
85
|
-
let previous = -1;
|
|
86
|
-
for (const act of value) {
|
|
87
|
-
if (act.index <= previous || (act.kind === 'say' && act.reply !== undefined && act.reply >= act.index))
|
|
88
|
-
return false;
|
|
89
|
-
previous = act.index;
|
|
90
|
-
}
|
|
91
|
-
return true;
|
|
92
|
-
}
|
|
93
|
-
export function validateSquareState(value) {
|
|
94
|
-
if (!isObject(value) || !hasExactKeys(value, ['hardCap', 'preamble', 'warmup', 'acts', 'runtime'], ['throttlePerMinute', 'routes'])
|
|
95
|
-
|| !(value.hardCap === null || (Number.isSafeInteger(value.hardCap) && value.hardCap > 0))
|
|
96
|
-
|| (value.throttlePerMinute !== undefined && (!Number.isSafeInteger(value.throttlePerMinute) || value.throttlePerMinute <= 0))
|
|
97
|
-
|| !isStringArray(value.preamble) || !isStringArray(value.warmup) || !validateActs(value.acts)
|
|
98
|
-
|| (value.routes !== undefined && (!Array.isArray(value.routes) || !value.routes.every((route) => isObject(route) && isWakeRouteKind(route.kind)
|
|
99
|
-
&& typeof route.location === 'string' && typeof route.participant === 'string' && typeof route.sessionId === 'string'
|
|
100
|
-
&& typeof route.channel === 'string' && isObject(route.address) && Object.values(route.address).every((item) => typeof item === 'string')
|
|
101
|
-
&& typeof route.updatedAt === 'number')))
|
|
102
|
-
|| !validateRuntime(value.runtime))
|
|
103
|
-
throw invalidArtifact('snapshot schema is malformed.');
|
|
104
|
-
const historyBoundary = value.acts.at(-1)?.index ?? -1;
|
|
105
|
-
if (value.runtime.nextActIndex <= historyBoundary)
|
|
106
|
-
throw invalidArtifact('nextActIndex is behind the activity history.');
|
|
107
|
-
const references = validateAssignedRuntimeReferences(value.runtime);
|
|
108
|
-
if (references === 'malformed')
|
|
109
|
-
throw invalidArtifact('snapshot schema is malformed.');
|
|
110
|
-
if (references === 'future')
|
|
111
|
-
throw invalidArtifact('runtime references an unassigned activity index.');
|
|
112
|
-
return value;
|
|
113
|
-
}
|
|
114
|
-
export function emptyRuntimeState(nextActIndex = 0) {
|
|
115
|
-
return { nextActIndex, observations: {}, leases: {} };
|
|
116
|
-
}
|
|
117
|
-
function normalizedLines(value) {
|
|
118
|
-
const normalized = value.replace(/\r\n/g, '\n').trim();
|
|
119
|
-
return normalized === '' ? [] : normalized.split('\n');
|
|
120
|
-
}
|
|
121
|
-
function readGuide(name) {
|
|
122
|
-
const guide = guideContents.get(name);
|
|
123
|
-
if (guide === undefined)
|
|
124
|
-
throw new SquareError('invalid_args', `Unknown square guide: ${name}`);
|
|
125
|
-
return guide;
|
|
126
|
-
}
|
|
127
|
-
export function createSquareState(options, snippet) {
|
|
128
|
-
const guides = [readGuide('participant')];
|
|
129
|
-
if (options.template !== undefined)
|
|
130
|
-
guides.push(readGuide(options.template));
|
|
131
|
-
return {
|
|
132
|
-
hardCap: options.hardCap,
|
|
133
|
-
...(options.throttlePerMinute === undefined ? {} : { throttlePerMinute: options.throttlePerMinute }),
|
|
134
|
-
preamble: normalizedLines(snippet),
|
|
135
|
-
warmup: normalizedLines(guides.join('\n\n')),
|
|
136
|
-
acts: [], routes: [], runtime: emptyRuntimeState(),
|
|
137
|
-
};
|
|
138
|
-
}
|
package/dist/square-storage.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { createSquareState, diagnoseSquareFile as diagnoseArtifactFile } from './artifact.js';
|
|
2
|
-
import type { SquareState } from './model.js';
|
|
3
|
-
import type { SquareArtifactPort } from './ports.js';
|
|
4
|
-
export { createSquareState };
|
|
5
|
-
export declare function probeSquareFile(squarePath: string): Promise<SquareState | undefined>;
|
|
6
|
-
export declare function diagnoseSquareFile(squarePath: string): ReturnType<typeof diagnoseArtifactFile>;
|
|
7
|
-
/** Test-helper snapshot replacement backed by one SQLite artifact. */
|
|
8
|
-
export declare function writeSquareSnapshot(squarePath: string, squareState: SquareState): Promise<void>;
|
|
9
|
-
/** Race-safe non-force creation for the file adapter. */
|
|
10
|
-
export declare function createSquareSnapshot(squarePath: string, squareState: SquareState): Promise<boolean>;
|
|
11
|
-
/** In-process artifact for fast behavior tests and embedded consumers. */
|
|
12
|
-
export declare function createMemoryCell(initial: SquareState): SquareArtifactPort;
|
|
13
|
-
/** SQLite-backed artifact. Revisions are read from the authoritative database, not file metadata. */
|
|
14
|
-
export declare function createFileCell(squarePath: string, externalSignal?: AbortSignal): SquareArtifactPort;
|