@astrosheep/square 0.3.70 → 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.
Files changed (115) hide show
  1. package/dist/activity-feed.d.ts +4 -5
  2. package/dist/activity-feed.js +14 -10
  3. package/dist/artifact.d.ts +26 -33
  4. package/dist/artifact.js +346 -317
  5. package/dist/attention-presentation.js +2 -9
  6. package/dist/automatic-session.js +6 -6
  7. package/dist/boundary-presentation.js +3 -3
  8. package/dist/catch-decisions.d.ts +19 -12
  9. package/dist/catch-decisions.js +32 -68
  10. package/dist/claude-delivery.js +7 -7
  11. package/dist/claude-mod.js +18 -7
  12. package/dist/cli/catch-presentation.d.ts +7 -0
  13. package/dist/cli/catch-presentation.js +55 -0
  14. package/dist/cli/context.d.ts +1 -1
  15. package/dist/cli/context.js +5 -23
  16. package/dist/cli/express-presentation.d.ts +10 -0
  17. package/dist/cli/express-presentation.js +196 -0
  18. package/dist/cli/maintenance-commands.d.ts +4 -0
  19. package/dist/cli/maintenance-commands.js +29 -6
  20. package/dist/cli/observation-commands.d.ts +7 -5
  21. package/dist/cli/observation-commands.js +275 -262
  22. package/dist/cli/program.d.ts +1 -1
  23. package/dist/cli/program.js +43 -56
  24. package/dist/cli/registry.js +2 -1
  25. package/dist/cli/square-commands.d.ts +3 -9
  26. package/dist/cli/square-commands.js +134 -255
  27. package/dist/codex-queue.js +4 -1
  28. package/dist/decisions.d.ts +26 -74
  29. package/dist/decisions.js +190 -335
  30. package/dist/delivery-health.js +13 -22
  31. package/dist/delivery-operations.d.ts +0 -11
  32. package/dist/delivery-operations.js +44 -33
  33. package/dist/delivery.d.ts +33 -32
  34. package/dist/delivery.js +70 -81
  35. package/dist/help.js +6 -1
  36. package/dist/inbox.d.ts +27 -0
  37. package/dist/inbox.js +55 -18
  38. package/dist/index.d.ts +2 -4
  39. package/dist/index.js +1 -1
  40. package/dist/list.d.ts +7 -1
  41. package/dist/list.js +49 -122
  42. package/dist/mcp.d.ts +1 -1
  43. package/dist/mcp.js +7 -37
  44. package/dist/model.d.ts +4 -44
  45. package/dist/model.js +1 -3
  46. package/dist/notifications.d.ts +1 -1
  47. package/dist/notifications.js +13 -7
  48. package/dist/open-square.d.ts +16 -2
  49. package/dist/open-square.js +40 -1
  50. package/dist/perception-projection.d.ts +6 -15
  51. package/dist/perception-projection.js +6 -26
  52. package/dist/plugins/opencode/delivery.js +9 -9
  53. package/dist/plugins/opencode/index.js +3 -3
  54. package/dist/ports.d.ts +156 -11
  55. package/dist/presentation-operations.d.ts +1 -1
  56. package/dist/presentation-operations.js +35 -44
  57. package/dist/presentation.d.ts +12 -19
  58. package/dist/presentation.js +33 -54
  59. package/dist/routes.d.ts +7 -14
  60. package/dist/routes.js +33 -62
  61. package/dist/runtime.d.ts +12 -39
  62. package/dist/runtime.js +33 -164
  63. package/dist/square-actions.d.ts +11 -21
  64. package/dist/square-actions.js +171 -271
  65. package/dist/square-application.d.ts +31 -55
  66. package/dist/square-application.js +637 -141
  67. package/dist/square-core.d.ts +3 -94
  68. package/dist/square-core.js +8 -353
  69. package/dist/square-domain.d.ts +24 -0
  70. package/dist/square-domain.js +77 -0
  71. package/dist/square-facade.d.ts +360 -9
  72. package/dist/square-migration.d.ts +24 -0
  73. package/dist/square-migration.js +211 -0
  74. package/dist/square-projections.d.ts +2 -42
  75. package/dist/square-projections.js +1 -37
  76. package/dist/square-sqlite.d.ts +59 -0
  77. package/dist/square-sqlite.js +702 -0
  78. package/dist/stream.d.ts +3 -10
  79. package/dist/stream.js +81 -52
  80. package/dist/views.d.ts +27 -95
  81. package/dist/views.js +217 -87
  82. package/dist/wake-eligibility.d.ts +15 -5
  83. package/dist/wake-eligibility.js +32 -16
  84. package/dist/wakes.d.ts +3 -3
  85. package/dist/wakes.js +31 -27
  86. package/guides/application-facade-delivery-evidence.md +200 -0
  87. package/guides/application-facade-design.md +95 -0
  88. package/guides/change-waits.md +4 -4
  89. package/guides/migration.md +77 -0
  90. package/guides/primitive-catch-consolidation.md +85 -0
  91. package/guides/primitive-history-consolidation.md +76 -0
  92. package/guides/primitive-status-consolidation.md +87 -0
  93. package/guides/primitive-storage-consolidation.md +128 -0
  94. package/guides/row-runtime-delivery-evidence.md +160 -0
  95. package/guides/row-storage-design.md +567 -0
  96. package/package.json +1 -1
  97. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  98. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  99. package/plugins/opencode/delivery.ts +11 -11
  100. package/plugins/opencode/index.ts +3 -3
  101. package/plugins/paseo/client/square-message.tsx +70 -52
  102. package/plugins/paseo/shared/message.ts +15 -0
  103. package/plugins/pi/square-pi.js +3 -0
  104. package/dist/activity.d.ts +0 -14
  105. package/dist/activity.js +0 -248
  106. package/dist/square-file-adapter.d.ts +0 -26
  107. package/dist/square-file-adapter.js +0 -85
  108. package/dist/square-state.d.ts +0 -6
  109. package/dist/square-state.js +0 -138
  110. package/dist/square-storage.d.ts +0 -14
  111. package/dist/square-storage.js +0 -188
  112. package/dist/square-wiring.d.ts +0 -33
  113. package/dist/square-wiring.js +0 -109
  114. package/dist/watch.d.ts +0 -8
  115. package/dist/watch.js +0 -252
@@ -0,0 +1,196 @@
1
+ import crypto from 'node:crypto';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { isSquareError } from '../model.js';
5
+ import { formatActivityId, parseActivityId } from '../square-application.js';
6
+ import { expressHintLine, formatRefusal, refusalText, renderActivityBlocked, renderActivityLimit, renderActivityUncertain, renderExpressNoWait, renderExpressWaiting, renderPendingFeed, joinRecoveryCommand, participantIdentity, participantsRecoveryCommand, participantCommandPrefix, commandPrefix, quoteShell, takeoverRecoveryLines, withPathOutput, } from '../presentation.js';
7
+ import { formatDuration } from '../time.js';
8
+ import { style } from '../tty-style.js';
9
+ export function saveActivityDraft(squarePath, name, body, now = Date.now()) {
10
+ const draftDir = path.join(path.dirname(squarePath), 'drafts');
11
+ fs.mkdirSync(draftDir, { recursive: true });
12
+ const hash = crypto.createHash('sha256').update(body).digest('hex').slice(0, 8);
13
+ const stamp = new Date(now).toISOString().replace(/\.\d{3}Z$/, 'Z').replace(/[:.]/g, '-');
14
+ const namePart = name.replace(/[^\p{L}\p{N}_-]+/gu, '-').replace(/^-+|-+$/g, '') || 'participant';
15
+ const base = `${stamp}-${namePart}-${hash}`;
16
+ for (let index = 0;; index++) {
17
+ const target = path.join(draftDir, `${base}${index === 0 ? '' : `-${index}`}.md`);
18
+ try {
19
+ fs.writeFileSync(target, body, { flag: 'wx' });
20
+ return target;
21
+ }
22
+ catch (error) {
23
+ if (error.code !== 'EEXIST')
24
+ throw error;
25
+ }
26
+ }
27
+ }
28
+ function replyArgument(reply) {
29
+ if (reply === undefined)
30
+ return '';
31
+ const index = parseActivityId(reply);
32
+ return ` --reply ${index === undefined ? quoteShell(reply) : formatActivityId(index)}`;
33
+ }
34
+ /** Retry choices are transport syntax, never a second admission decision. */
35
+ export function activityRetryCommand(squarePath, name, opts, force = opts.force ?? false) {
36
+ const reach = opts.reach === 'bell' ? ' --bell'
37
+ : opts.mentions?.length ? opts.mentions.map((target) => ` --mention ${quoteShell(target)}`).join('')
38
+ : opts.noMention === true ? ' --no-mention' : '';
39
+ return `${participantCommandPrefix(squarePath, name)} express${force ? ' --force' : ''}${opts.noWait ? ' --no-wait' : ''}${reach}${replyArgument(opts.reply)} -`;
40
+ }
41
+ function mentionRepairCommand(squarePath, name, opts, bell) {
42
+ return `${participantCommandPrefix(squarePath, name)} express${opts.force ? ' --force' : ''}${opts.noWait ? ' --no-wait' : ''}${bell ? ' --bell' : ' --no-mention'}${replyArgument(opts.reply)} -`;
43
+ }
44
+ function isGestureOnly(body) {
45
+ return /\*[^*]*\*/.test(body) && body.replace(/\*[^*]*\*/g, '').trim() === '';
46
+ }
47
+ export function renderExpression(squarePath, outcome) {
48
+ const { data, projection } = outcome;
49
+ const name = projection.participant;
50
+ const pendingSpeaker = projection.pendingPublic[0]?.actor;
51
+ const pendingLead = pendingSpeaker === undefined ? ''
52
+ : `▲ while you spoke, ${participantIdentity(pendingSpeaker)} said something behind you\n\n`;
53
+ const pending = projection.pendingPublic.length === 0 ? ''
54
+ : `\n\n${pendingLead}${renderPendingFeed(projection.pendingPublic, projection.sayNumbers, projection.perceptions, name)}`;
55
+ const ownCount = projection.ownActivityCount;
56
+ const capCount = projection.hardCap === null ? '' : ` · ${ownCount}/${projection.hardCap}`;
57
+ const activity = data.activity;
58
+ const confirmation = activity.reach === 'bell'
59
+ ? `● you ring the bell — everyone turns · ${activity.id}${style('dim', capCount)}`
60
+ : `● ${activity.mentions.length > 0
61
+ ? `you walk over to ${activity.mentions.map((target) => participantIdentity(target)).join(', ')}`
62
+ : isGestureOnly(activity.body ?? '') ? 'your gesture lands' : 'your words land'} — ${activity.id}${style('dim', capCount)}`;
63
+ const hint = expressHintLine(ownCount);
64
+ const replyNotice = data.replyAuthorNotHere === undefined ? ''
65
+ : `\n○ ${participantIdentity(data.replyAuthorNotHere)} isn't in the square right now — the reply landed without reaching them`;
66
+ return withPathOutput(projection.location ?? squarePath, (projection.reentry === undefined ? '' : '● you stepped back into the square\n')
67
+ + confirmation + (hint ? `\n${style('dim', hint)}` : '') + replyNotice + pending, { participantCount: projection.standingCount, held: projection.held });
68
+ }
69
+ /** Body adaptation, terminal IO and drafts only; the application owns every attempt and wait. */
70
+ export async function executeExpression(application, squarePath, requestedName, rawInput, opts) {
71
+ let announcedWait;
72
+ let landedId;
73
+ let name = requestedName;
74
+ const controller = new AbortController();
75
+ const interrupt = () => controller.abort();
76
+ process.once('SIGINT', interrupt);
77
+ try {
78
+ const outcome = await application.express(rawInput.replace(/\r\n/g, '\n').trim(), {
79
+ ...opts, reply: opts.reply, requireExplicitReach: true,
80
+ }, {
81
+ signal: controller.signal,
82
+ onProgress(progress) {
83
+ if (progress.kind !== 'waiting' || announcedWait === progress.reason)
84
+ return;
85
+ process.stdout.write(renderExpressWaiting(progress) + '\n');
86
+ announcedWait = progress.reason;
87
+ },
88
+ });
89
+ landedId = outcome.data.activity.id;
90
+ name = outcome.projection.participant;
91
+ process.stdout.write(renderExpression(squarePath, outcome));
92
+ }
93
+ catch (error) {
94
+ const detail = error?.operation;
95
+ name = detail?.participant ?? name ?? 'participant';
96
+ // This MUST precede recovery: even an unreadable artifact cannot eat the raw body.
97
+ const draftPath = saveActivityDraft(squarePath, name, rawInput);
98
+ const recovered = await application.recovery().catch(() => undefined);
99
+ const fresh = recovered?.data;
100
+ name = fresh?.participant ?? name;
101
+ const output = { squarePath, name, draftPath, participantCount: fresh?.standingCount, held: fresh?.held };
102
+ const confirmedUnsent = landedId === undefined && detail?.landedId === undefined
103
+ && detail?.expressEffect !== 'landed' && detail?.expressEffect !== 'unknown'
104
+ && (detail?.expressEffect === 'unsent' || (isSquareError(error) && error.facts?.activityUnsent === true));
105
+ const retryCommand = activityRetryCommand(squarePath, name, opts);
106
+ const exitCode = (code) => { process.exitCode = controller.signal.aborted ? 130 : code; };
107
+ if (!confirmedUnsent) {
108
+ process.stderr.write(renderActivityUncertain({ ...output, landedId: landedId ?? detail?.landedId,
109
+ detail: error instanceof Error ? error.message : String(error) }));
110
+ exitCode(1);
111
+ return;
112
+ }
113
+ if (isSquareError(error)) {
114
+ if (error.code === 'behind' && fresh?.activitySummaries !== undefined) {
115
+ process.stdout.write(renderActivityBlocked({ ...output, retryCommand,
116
+ forceCommand: activityRetryCommand(squarePath, name, opts, true),
117
+ activitySummaries: fresh.activitySummaries.map((summary) => ({ ...summary, previews: [...summary.previews] })),
118
+ }));
119
+ exitCode(1);
120
+ return;
121
+ }
122
+ if (error.code === 'capped') {
123
+ process.stdout.write(renderActivityLimit({ ...output, count: fresh?.ownActivityCount,
124
+ ...(fresh?.hardCap == null ? {} : { hardCap: fresh.hardCap }),
125
+ }));
126
+ exitCode(1);
127
+ return;
128
+ }
129
+ if (error.code === 'held' || error.code === 'throttled') {
130
+ process.stdout.write(renderExpressNoWait({ ...output, reason: error.code, retryCommand,
131
+ delayMs: error.facts?.retryAfterMs ?? 1, holder: error.facts?.holder, holdReason: error.facts?.holdReason,
132
+ }));
133
+ exitCode(1);
134
+ return;
135
+ }
136
+ if (error.code === 'bell_quota') {
137
+ process.stdout.write(withPathOutput(squarePath, [
138
+ '✕ the bell stays quiet for now', ` · you can ring it again in ${formatDuration(error.facts?.retryAfterMs ?? 1)}`,
139
+ `· draft kept: ${draftPath}`, ' · retry when the bell is ready:', `${retryCommand} < ${quoteShell(draftPath)}`,
140
+ ].join('\n'), output));
141
+ exitCode(1);
142
+ return;
143
+ }
144
+ }
145
+ const facts = isSquareError(error) ? error.facts : undefined;
146
+ const reason = facts?.reason;
147
+ const message = reason === 'explicit_reach_required'
148
+ ? '✕ express needs --mention <name>, --no-mention, --bell, or an @name in the body\n · --reply <activity-id> also reaches the author it answers'
149
+ : reason === 'bell_mention_conflict'
150
+ ? '✕ a bell cannot be combined with a mention'
151
+ : reason === 'invalid_reply'
152
+ ? facts?.activityExists === false
153
+ ? `Invalid --reply: ${facts.activityId ?? opts.reply ?? 'that activity'} is not in this square's history`
154
+ : 'Invalid --reply: expected an activity id like act/12'
155
+ : refusalText(error);
156
+ const lines = [message, `· draft kept: ${draftPath}`];
157
+ const recipientFailure = detail?.subject === 'recipient';
158
+ const repair = reason === 'bell_mention_conflict' ? { bell: true, fromBody: true }
159
+ : reason === 'unmatched_mention' ? { bell: false, fromBody: true }
160
+ : reason === 'not_standing' && recipientFailure ? { bell: false, fromBody: false } : undefined;
161
+ if (isSquareError(error)) {
162
+ if ((reason === 'never_joined' && detail?.subject === 'caller') || error.code === 'unknown_participant' || error.code === 'not_joined') {
163
+ lines.push(joinRecoveryCommand(squarePath, name));
164
+ }
165
+ if (error.code === 'already_joined')
166
+ lines.push(...takeoverRecoveryLines(squarePath, name));
167
+ if (repair !== undefined) {
168
+ lines.push(participantsRecoveryCommand(squarePath), repair.fromBody
169
+ ? " · edit the draft first — the body's @name must be removed or wrapped in backticks:"
170
+ : ' · pick someone standing here, or land it bare:', `${mentionRepairCommand(squarePath, name, opts, repair.bell)} < ${quoteShell(draftPath)}`);
171
+ }
172
+ else if (error.code === 'invalid_args' || error.code === 'invalid_name') {
173
+ if (opts.mentions?.length)
174
+ lines.push(participantsRecoveryCommand(squarePath));
175
+ if (opts.reply !== undefined && reason !== 'invalid_reply')
176
+ lines.push(`${commandPrefix(squarePath)} history --limit 10`);
177
+ }
178
+ }
179
+ if (reason === 'explicit_reach_required') {
180
+ lines.push(' · choose someone with --mention <name>, or send without addressing anyone:', `${activityRetryCommand(squarePath, name, { ...opts, noMention: true })} < ${quoteShell(draftPath)}`);
181
+ }
182
+ else if (reason === 'invalid_reply') {
183
+ const hasFlagReach = opts.reach !== undefined || (opts.mentions?.length ?? 0) > 0 || opts.noMention === true;
184
+ const withoutReply = { ...opts, reply: undefined, ...(hasFlagReach ? {} : { noMention: true }) };
185
+ lines.push(`${commandPrefix(squarePath)} history --limit 10`, ' · choose a valid --reply from history, or send without a reply using this repair:', ...(hasFlagReach ? [] : [' · this repair is bare; replace --no-mention with --mention <name> to address someone']), `${activityRetryCommand(squarePath, name, withoutReply)} < ${quoteShell(draftPath)}`);
186
+ }
187
+ else if (repair === undefined) {
188
+ lines.push(' · correct the problem above before retrying; keep your intended recipients and reply:', `${retryCommand} < ${quoteShell(draftPath)}`);
189
+ }
190
+ process.stderr.write(formatRefusal(squarePath, lines, output));
191
+ exitCode(2);
192
+ }
193
+ finally {
194
+ process.off('SIGINT', interrupt);
195
+ }
196
+ }
@@ -3,5 +3,9 @@ interface DoctorResult {
3
3
  output: string;
4
4
  exitCode?: number;
5
5
  }
6
+ interface MigrateResult {
7
+ output: string;
8
+ }
9
+ export declare const migrateCommand: CommandSpec<undefined, MigrateResult>;
6
10
  export declare const doctorCommand: CommandSpec<undefined, DoctorResult>;
7
11
  export {};
@@ -1,7 +1,27 @@
1
- import { diagnoseSquareFile } from '../square-storage.js';
1
+ import { createSquareApplication } from '../square-application.js';
2
2
  import { renderDoctorClean, renderDoctorUnfixable, withPathOutput } from '../presentation.js';
3
- import { inSquareCount } from '../runtime.js';
4
3
  import { requireSquarePath, usage } from './context.js';
4
+ export const migrateCommand = {
5
+ parse(argv, context) {
6
+ if (argv.length > 0)
7
+ usage(context.command);
8
+ return undefined;
9
+ },
10
+ async execute(_intent, context) {
11
+ const squarePath = requireSquarePath(context);
12
+ const { data: outcome, projection } = await createSquareApplication({ cwd: context.cwd, env: context.env, squarePath }).migrate();
13
+ const body = outcome.status === 'migrated' && outcome.counts !== undefined
14
+ ? [
15
+ '✓ migrated to row storage',
16
+ ` · ${outcome.counts.activities} activities · ${outcome.counts.receipts} receipts · ${outcome.counts.leases} leases · ${outcome.counts.routes} routes`,
17
+ ].join('\n')
18
+ : '· already format 2 — nothing changed';
19
+ return { output: withPathOutput(squarePath, body, { participantCount: projection.standingCount }) };
20
+ },
21
+ present(result) {
22
+ process.stdout.write(result.output);
23
+ },
24
+ };
5
25
  export const doctorCommand = {
6
26
  parse(argv, context) {
7
27
  if (argv.length > 0)
@@ -10,16 +30,19 @@ export const doctorCommand = {
10
30
  },
11
31
  async execute(_intent, context) {
12
32
  const squarePath = requireSquarePath(context);
13
- const diagnosis = await diagnoseSquareFile(squarePath);
14
- if (diagnosis.unfixable !== undefined || diagnosis.state === undefined) {
33
+ const { data: diagnosis, projection } = await createSquareApplication({ cwd: context.cwd, env: context.env, squarePath }).diagnose();
34
+ if (diagnosis.migrationRequired !== undefined) {
35
+ return { output: withPathOutput(squarePath, `${diagnosis.migrationRequired}\n`), exitCode: 2 };
36
+ }
37
+ if (diagnosis.unfixable !== undefined || diagnosis.standingCount === undefined) {
15
38
  return {
16
- output: withPathOutput(squarePath, renderDoctorUnfixable(diagnosis.unfixable ?? 'the snapshot could not be decoded')),
39
+ output: withPathOutput(squarePath, renderDoctorUnfixable(diagnosis.unfixable ?? 'the artifact could not be decoded')),
17
40
  exitCode: 2,
18
41
  };
19
42
  }
20
43
  return {
21
44
  output: withPathOutput(squarePath, renderDoctorClean(), {
22
- participantCount: inSquareCount(diagnosis.state),
45
+ participantCount: projection.standingCount,
23
46
  }),
24
47
  };
25
48
  },
@@ -1,18 +1,20 @@
1
- import { type ActivitiesOptions, type WatchOptions } from '../model.js';
2
- import { type StreamStart } from '../stream.js';
1
+ import type { CatchApplicationOptions, DiscoveryApplicationOptions, HistoryApplicationQuery, StreamStart } from '../square-facade.js';
3
2
  import { type CommandSpec } from './context.js';
4
- interface HistoryCommandOptions extends ActivitiesOptions {
3
+ interface HistoryCommandOptions {
4
+ query: HistoryApplicationQuery;
5
5
  noTruncate: boolean;
6
+ format?: string[];
7
+ json: boolean;
6
8
  continuationArgs: string[];
7
9
  }
8
- export declare const listCommand: CommandSpec<string[]>;
10
+ export declare const listCommand: CommandSpec<DiscoveryApplicationOptions, string>;
9
11
  interface StreamIntent {
10
12
  ndjson: boolean;
11
13
  forName?: string;
12
14
  start: StreamStart;
13
15
  }
14
16
  export declare const streamCommand: CommandSpec<StreamIntent>;
15
- export declare const catchCommand: CommandSpec<WatchOptions>;
17
+ export declare const catchCommand: CommandSpec<CatchApplicationOptions>;
16
18
  export declare const historyCommand: CommandSpec<HistoryCommandOptions, string>;
17
19
  interface ParticipantsCommandOptions {
18
20
  limit: number;