@astrosheep/square 0.4.7 → 0.4.8

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 (83) hide show
  1. package/dist/card-format.d.ts +2 -0
  2. package/dist/card-format.js +2 -0
  3. package/dist/claude-mcp-context.d.ts +0 -1
  4. package/dist/claude-mcp-context.js +0 -10
  5. package/dist/claude-mod.d.ts +2 -0
  6. package/dist/claude-mod.js +211 -0
  7. package/dist/cli/context.d.ts +1 -1
  8. package/dist/cli/context.js +1 -12
  9. package/dist/cli/express-presentation.js +6 -33
  10. package/dist/cli/json.js +1 -1
  11. package/dist/cli/observation-commands.d.ts +2 -3
  12. package/dist/cli/observation-commands.js +21 -59
  13. package/dist/cli/registry.js +3 -5
  14. package/dist/cli/square-commands.d.ts +1 -2
  15. package/dist/cli/square-commands.js +7 -17
  16. package/dist/decisions.js +5 -4
  17. package/dist/harness-claude.js +3 -3
  18. package/dist/help.js +6 -8
  19. package/dist/mcp.js +1 -1
  20. package/dist/neutral.d.ts +15 -1
  21. package/dist/notifications.js +1 -1
  22. package/dist/operation-presentation.d.ts +17 -15
  23. package/dist/operation-presentation.js +58 -32
  24. package/dist/packages/agent-delivery/src/index.d.ts +4 -9
  25. package/dist/packages/agent-delivery/src/index.js +0 -9
  26. package/dist/packages/agent-delivery/src/local-endpoint.d.ts +2 -12
  27. package/dist/packages/agent-delivery/src/local-endpoint.js +4 -48
  28. package/dist/packages/agent-delivery/src/pi-client.js +0 -2
  29. package/dist/packages/agent-delivery/src/types.d.ts +3 -37
  30. package/dist/plugins/paseo/shared/card-format.d.ts +6 -0
  31. package/dist/plugins/paseo/shared/card-format.js +38 -0
  32. package/dist/plugins/paseo/shared/card-presentation.d.ts +3 -1
  33. package/dist/plugins/paseo/shared/card-presentation.js +3 -1
  34. package/dist/presentation.d.ts +2 -20
  35. package/dist/presentation.js +11 -46
  36. package/dist/sqlite-square.js +5 -6
  37. package/dist/square-facade.d.ts +3 -12
  38. package/dist/square-posting.js +1 -1
  39. package/dist/square-tools.d.ts +0 -11
  40. package/dist/square-tools.js +33 -119
  41. package/dist/square-values.d.ts +1 -1
  42. package/dist/square-values.js +1 -1
  43. package/dist/time.d.ts +1 -0
  44. package/dist/time.js +10 -0
  45. package/dist/views.d.ts +1 -1
  46. package/dist/views.js +2 -2
  47. package/dist/wake-transports.d.ts +1 -1
  48. package/dist/wake-transports.js +1 -1
  49. package/guides/catch-cold-path-optimization.md +1 -1
  50. package/guides/claude-native.md +37 -7
  51. package/guides/delivery-primitives.md +2 -1
  52. package/guides/mcp.md +3 -3
  53. package/guides/pi-native-delivery.md +10 -2
  54. package/guides/pi-tools.md +1 -3
  55. package/guides/row-storage-design.md +1 -1
  56. package/package.json +1 -1
  57. package/plugins/claude/.claude-plugin/plugin.json +3 -2
  58. package/plugins/claude/README.md +9 -0
  59. package/plugins/claude/hooks/hooks.json +1 -9
  60. package/plugins/claude/hooks/register.js +178 -0
  61. package/plugins/claude/skills/square/SKILL.md +2 -2
  62. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  63. package/plugins/paseo/OVERVIEW.md +1 -1
  64. package/plugins/paseo/README.md +20 -23
  65. package/plugins/paseo/client/markdown.tsx +88 -95
  66. package/plugins/paseo/client/palette.ts +37 -12
  67. package/plugins/paseo/client/square-message.tsx +52 -76
  68. package/plugins/paseo/client/transform.ts +4 -6
  69. package/plugins/paseo/shared/card-format.ts +37 -0
  70. package/plugins/paseo/shared/card-presentation.ts +6 -2
  71. package/plugins/paseo/shared/message.ts +1 -6
  72. package/plugins/pi/square-pi.js +69 -50
  73. package/skills/square/SKILL.md +2 -2
  74. package/dist/claude-boundary-state.d.ts +0 -20
  75. package/dist/claude-boundary-state.js +0 -84
  76. package/dist/claude-hook.d.ts +0 -2
  77. package/dist/claude-hook.js +0 -232
  78. package/dist/packages/agent-delivery/src/claude-auth.d.ts +0 -13
  79. package/dist/packages/agent-delivery/src/claude-auth.js +0 -75
  80. package/dist/packages/agent-delivery/src/claude-native.d.ts +0 -30
  81. package/dist/packages/agent-delivery/src/claude-native.js +0 -75
  82. package/dist/packages/agent-delivery/src/claude.d.ts +0 -5
  83. package/dist/packages/agent-delivery/src/claude.js +0 -66
@@ -0,0 +1,2 @@
1
+ /** Receiver formatting shared by the Paseo and Pi cards. */
2
+ export { cardReferences, reachLabel, timeLabel } from './plugins/paseo/shared/card-format.js';
@@ -0,0 +1,2 @@
1
+ /** Receiver formatting shared by the Paseo and Pi cards. */
2
+ export { cardReferences, reachLabel, timeLabel } from './plugins/paseo/shared/card-format.js';
@@ -10,6 +10,5 @@ export declare function beginClaudeMcpCall(input: ClaudeMcpCall, env?: NodeJS.Pr
10
10
  token: string;
11
11
  }>;
12
12
  export declare function endClaudeMcpCall(toolUseId: string, token: string, env?: NodeJS.ProcessEnv): Promise<void>;
13
- export declare function endClaudeMcpCallForSession(toolUseId: string, sessionId: string, env?: NodeJS.ProcessEnv, agentId?: string, signal?: AbortSignal): Promise<void>;
14
13
  export declare function readClaudeMcpCall(toolUseId: unknown, env?: NodeJS.ProcessEnv): Promise<ClaudeMcpCall>;
15
14
  export {};
@@ -71,16 +71,6 @@ export async function endClaudeMcpCall(toolUseId, token, env = process.env) {
71
71
  await fs.unlink(file);
72
72
  });
73
73
  }
74
- export async function endClaudeMcpCallForSession(toolUseId, sessionId, env = process.env, agentId, signal) {
75
- if (!text.safeParse(toolUseId).success || !text.safeParse(sessionId).success)
76
- throw unavailable();
77
- await mutate(env, async (root) => {
78
- const file = callFile(root, toolUseId);
79
- const stored = await readRecord(file);
80
- if (stored?.toolUseId === toolUseId && stored.sessionId === sessionId && stored.agentId === agentId)
81
- await fs.unlink(file);
82
- }, signal);
83
- }
84
74
  export async function readClaudeMcpCall(toolUseId, env = process.env) {
85
75
  if (!text.safeParse(toolUseId).success)
86
76
  throw unavailable();
@@ -0,0 +1,2 @@
1
+ /** One mod-owned helper. Only this process touches Node/SQLite; no producer socket or inbox. */
2
+ export declare function runClaudeMod(input: string, env?: NodeJS.ProcessEnv, signal?: AbortSignal, output?: (line: string) => void | Promise<void>): Promise<string>;
@@ -0,0 +1,211 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { createServer } from 'node:http';
3
+ import { once } from 'node:events';
4
+ import { z } from 'zod/v4';
5
+ import { DeliveryLedger, deliveryLedgerLocation } from './delivery-ledger.js';
6
+ import { retainDeliveryLedger } from './delivery-lifetime.js';
7
+ import { harnessOwner } from './owner.js';
8
+ import { attachDeliverySession, detachDeliverySession } from './session-lifecycle.js';
9
+ import { beginClaudeMcpCall, endClaudeMcpCall } from './claude-mcp-context.js';
10
+ import { renderDeliveryReminder } from './delivery-frame.js';
11
+ const routeKind = 'claude-mod';
12
+ const nativeId = z.string().min(1).max(4096).regex(/^[^\x00-\x1f\x7f]+$/u);
13
+ const coordinate = z.object({ agentId: nativeId.optional() });
14
+ const request = z.discriminatedUnion('op', [
15
+ coordinate.extend({ op: z.literal('state'), mode: z.enum(['busy', 'idle', 'stopped', 'ended']) }),
16
+ z.object({ op: z.literal('guard'), token: z.string().uuid() }),
17
+ z.object({ op: z.literal('settle'), token: z.string().uuid(), kind: z.enum(['append', 'submit', 'failed', 'unknown', 'released']),
18
+ text: z.string().optional(), uuid: nativeId.optional() }),
19
+ coordinate.extend({ op: z.literal('mcp-begin'), toolUseId: nativeId, cwd: z.string().min(1) }),
20
+ z.object({ op: z.literal('mcp-end'), toolUseId: nativeId, token: nativeId }),
21
+ z.object({ op: z.literal('close') }),
22
+ ]);
23
+ /** One mod-owned helper. Only this process touches Node/SQLite; no producer socket or inbox. */
24
+ export async function runClaudeMod(input, env = process.env, signal, output = line => new Promise((resolve, reject) => {
25
+ process.stdout.write(line, error => error ? reject(error) : resolve());
26
+ })) {
27
+ const { sessionId } = z.object({ sessionId: nativeId }).parse(JSON.parse(input));
28
+ const stop = new AbortController();
29
+ const abort = () => stop.abort();
30
+ const lifetimeSignal = signal ? AbortSignal.any([signal, stop.signal]) : stop.signal;
31
+ const ledger = await DeliveryLedger.open({ location: deliveryLedgerLocation(env), signal: AbortSignal.any([lifetimeSignal, AbortSignal.timeout(5000)]) });
32
+ process.once('SIGTERM', abort);
33
+ process.once('SIGINT', abort);
34
+ const lifetime = retainDeliveryLedger(ledger);
35
+ const receivers = new Map();
36
+ const offers = new Map();
37
+ const secret = `/${randomBytes(32).toString('hex')}`;
38
+ let revision = 0;
39
+ const ownerFor = (agentId) => harnessOwner({ channel: 'claude-code', session: sessionId, ...(agentId === undefined ? {} : { agent: agentId }) });
40
+ async function state(agentId, mode, signal = lifetimeSignal) {
41
+ const key = agentId ?? '';
42
+ let receiver = receivers.get(key);
43
+ if ((!receiver || receiver.mode === 'ended') && (mode === 'busy' || mode === 'idle')) {
44
+ const owner = ownerFor(agentId);
45
+ const attached = await attachDeliverySession(owner, env, [{ kind: routeKind, address: { sessionId, ...(agentId === undefined ? {} : { agentId }) } }], signal);
46
+ if (!attached)
47
+ return false;
48
+ receiver = { agentId, owner, generation: attached.generation, mode, attempted: new Set() };
49
+ receivers.set(key, receiver);
50
+ }
51
+ if (!receiver)
52
+ return false;
53
+ if (mode === 'busy' && receiver.mode === 'stopped')
54
+ receiver.attempted.clear();
55
+ receiver.mode = mode;
56
+ revision++;
57
+ if (mode === 'stopped')
58
+ await ledger.cancel({ owner: receiver.owner, generation: receiver.generation }, signal);
59
+ if (mode === 'ended')
60
+ await detachDeliverySession(receiver.owner, receiver.generation, env, signal);
61
+ if (mode === 'stopped' || mode === 'ended')
62
+ for (const offer of offers.values()) {
63
+ if (offer.receiver === receiver)
64
+ offer.finish(offer.invoked ? 'unknown' : 'not-capable');
65
+ }
66
+ return true;
67
+ }
68
+ async function rpc(data) {
69
+ switch (data.op) {
70
+ case 'state': return { ok: await state(data.agentId, data.mode) };
71
+ case 'guard': {
72
+ const offer = offers.get(data.token);
73
+ if (!offer || !['busy', 'idle'].includes(offer.receiver.mode) || !await offer.guard())
74
+ return { ok: false };
75
+ // Crossing this fence may invoke a native effect. Cancellation thereafter is uncertain.
76
+ offer.invoked = true;
77
+ return { ok: true };
78
+ }
79
+ case 'settle': {
80
+ const offer = offers.get(data.token);
81
+ if (!offer)
82
+ return { ok: false };
83
+ const exact = data.text === offer.text;
84
+ const outcome = data.kind === 'append' ? exact && data.uuid ? 'presented' : 'unknown'
85
+ : data.kind === 'submit' ? exact ? 'presented' : 'accepted'
86
+ : data.kind === 'released' ? 'not-capable' : data.kind;
87
+ offer.finish(outcome);
88
+ return { ok: true };
89
+ }
90
+ case 'mcp-begin': {
91
+ const receiver = receivers.get(data.agentId ?? '');
92
+ if (!receiver || receiver.mode !== 'busy') {
93
+ // Some native child starts are nested under another mod's event. The
94
+ // actual tool event is still authoritative, never its model arguments.
95
+ if (receiver?.mode === 'stopped' || !await state(data.agentId, 'busy'))
96
+ throw new Error('Square MCP call identity unavailable');
97
+ }
98
+ return beginClaudeMcpCall({ toolUseId: data.toolUseId, sessionId, cwd: data.cwd, agentId: data.agentId }, env, lifetimeSignal);
99
+ }
100
+ case 'mcp-end':
101
+ await endClaudeMcpCall(data.toolUseId, data.token, env);
102
+ return { ok: true };
103
+ case 'close':
104
+ // The mod may kill its process stream as soon as this RPC returns.
105
+ // Commit detach before acknowledging, not only in process teardown.
106
+ for (const receiver of receivers.values())
107
+ await state(receiver.agentId, 'ended');
108
+ stop.abort();
109
+ return { ok: true };
110
+ }
111
+ }
112
+ const server = createServer(async (req, res) => {
113
+ if (req.method !== 'POST' || req.url !== secret) {
114
+ res.writeHead(404).end();
115
+ return;
116
+ }
117
+ try {
118
+ let body = '';
119
+ for await (const chunk of req) {
120
+ body += chunk.toString();
121
+ if (body.length > 1_048_576)
122
+ throw new Error('Request too large');
123
+ }
124
+ const result = await rpc(request.parse(JSON.parse(body)));
125
+ res.writeHead(200, { 'content-type': 'application/json' }).end(JSON.stringify(result));
126
+ }
127
+ catch {
128
+ res.writeHead(400, { 'content-type': 'application/json' }).end(JSON.stringify({ error: 'Square Claude mod request failed' }));
129
+ }
130
+ });
131
+ let observer;
132
+ try {
133
+ server.listen(0, '127.0.0.1');
134
+ await once(server, 'listening', { signal: lifetimeSignal });
135
+ const address = server.address();
136
+ if (!address || typeof address === 'string')
137
+ throw new Error('Claude helper address unavailable');
138
+ if (!await state(undefined, 'idle', AbortSignal.any([lifetimeSignal, AbortSignal.timeout(5000)])))
139
+ throw new Error('Claude helper attachment unavailable');
140
+ observer = await ledger.observe(ownerFor());
141
+ await output(`${JSON.stringify({ type: 'ready', url: `http://127.0.0.1:${address.port}${secret}` })}\n`);
142
+ while (!lifetimeSignal.aborted) {
143
+ let version = await observer.read(lifetimeSignal);
144
+ const previousRevision = revision;
145
+ for (const receiver of receivers.values()) {
146
+ if (!['busy', 'idle'].includes(receiver.mode))
147
+ continue;
148
+ const current = await ledger.session(receiver.owner, lifetimeSignal);
149
+ if (current?.state !== 'attached' || current.generation !== receiver.generation) {
150
+ if (receiver.agentId === undefined)
151
+ return '';
152
+ receiver.mode = 'ended';
153
+ continue;
154
+ }
155
+ const held = new Set((await ledger.handoffs(receiver.owner, { generation: receiver.generation }, lifetimeSignal))
156
+ .filter(attempt => !['failed', 'released'].includes(attempt.outcome)).map(attempt => attempt.seq));
157
+ for (const card of await ledger.pending(receiver.owner, { generation: receiver.generation }, lifetimeSignal)) {
158
+ if (lifetimeSignal.aborted || !['busy', 'idle'].includes(receiver.mode))
159
+ break;
160
+ if (held.has(card.seq) || receiver.attempted.has(card.key))
161
+ continue;
162
+ // An idle child cannot be woken with the main session's prompt.submit.
163
+ if (receiver.agentId !== undefined && receiver.mode !== 'busy')
164
+ continue;
165
+ receiver.attempted.add(card.key);
166
+ const transport = {
167
+ kind: routeKind,
168
+ async send(deliveryCard, _address, context) {
169
+ const text = renderDeliveryReminder([`[square-delivery:${context.token}]\n${deliveryCard.text}\n[/square-delivery:${context.token}]`], { tagged: false });
170
+ if (!await context.prepare({ token: context.token, messageId: context.token, payloadHash: createHash('sha256').update(text).digest('hex') })
171
+ || !await context.beforeSend() || !['busy', 'idle'].includes(receiver.mode))
172
+ return 'not-capable';
173
+ const completion = Promise.withResolvers();
174
+ offers.set(context.token, { receiver, text, invoked: false, guard: context.beforeSend, finish: completion.resolve });
175
+ try {
176
+ await output(`${JSON.stringify({ type: 'delivery', token: context.token, agentId: receiver.agentId, text, display: deliveryCard.text })}\n`);
177
+ return await completion.promise;
178
+ }
179
+ finally {
180
+ offers.delete(context.token);
181
+ }
182
+ },
183
+ };
184
+ lifetime.track(await ledger.dispatchHandoff(receiver.owner, card.key, transport, { generation: receiver.generation, timeoutMs: 5000, signal: lifetimeSignal }));
185
+ // Settlement changes are our own; drain them rather than busy-retrying.
186
+ version = await observer.read(lifetimeSignal);
187
+ }
188
+ }
189
+ if (!lifetimeSignal.aborted && previousRevision === revision)
190
+ await observer.changed(version, 1000, lifetimeSignal);
191
+ }
192
+ }
193
+ catch (error) {
194
+ if (!lifetimeSignal.aborted)
195
+ throw error;
196
+ }
197
+ finally {
198
+ stop.abort();
199
+ for (const offer of offers.values())
200
+ offer.finish(offer.invoked ? 'unknown' : 'not-capable');
201
+ server.closeAllConnections();
202
+ await new Promise(resolve => server.close(() => resolve()));
203
+ observer?.close();
204
+ for (const receiver of receivers.values())
205
+ await detachDeliverySession(receiver.owner, receiver.generation, env, AbortSignal.timeout(1500)).catch(() => undefined);
206
+ await lifetime.close();
207
+ process.off('SIGTERM', abort);
208
+ process.off('SIGINT', abort);
209
+ }
210
+ return '';
211
+ }
@@ -22,7 +22,7 @@ export declare function resolveBody(arg: string): Promise<string>;
22
22
  export declare function fail(message: string, exitCode?: number): never;
23
23
  export declare function usage(command: string): never;
24
24
  export declare function requireValue(args: string[], index: number, flag: string): string;
25
- export declare function parseDurationMs(value: string, flag: string): number;
25
+ export { parseDurationMs } from '../time.js';
26
26
  export declare function parsePositiveInteger(value: string, flag: string): number;
27
27
  export declare function parseBoundedLimit(value: string | undefined, flag: string, max: number, retry: string): number;
28
28
  export declare function parseNonNegativeInteger(value: string, flag: string): number;
@@ -44,17 +44,7 @@ export function requireValue(args, index, flag) {
44
44
  fail(`✕ missing value for ${flag}`);
45
45
  return value;
46
46
  }
47
- export function parseDurationMs(value, flag) {
48
- const match = value.match(/^([1-9]\d*)(ms|s|m|h)$/);
49
- if (!match)
50
- fail(`✕ invalid ${flag}: expected a positive duration with unit ms, s, m, or h (for example 500ms, 30s, 3m, 1h)`);
51
- const amount = Number.parseInt(match[1], 10);
52
- const multiplier = { ms: 1, s: 1000, m: 60000, h: 3600000 };
53
- const milliseconds = amount * multiplier[match[2]];
54
- if (!Number.isSafeInteger(milliseconds))
55
- fail(`✕ invalid ${flag}: duration is too large`);
56
- return milliseconds;
57
- }
47
+ export { parseDurationMs } from '../time.js';
58
48
  export function parsePositiveInteger(value, flag) {
59
49
  if (!/^[1-9]\d*$/.test(value))
60
50
  fail(`✕ invalid ${flag}: expected a positive integer`);
@@ -107,7 +97,6 @@ const LOCATION_REQUIRED_COMMANDS = new Set([
107
97
  'listen',
108
98
  'ignore',
109
99
  'unlisten',
110
- 'listening',
111
100
  'done',
112
101
  'history',
113
102
  'status',
@@ -3,9 +3,8 @@ import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { isSquareError } from '../model.js';
5
5
  import { selectOwnSeat, formatActivityId, parseActivityId } from '../square-api.js';
6
- import { formatRefusal, renderActivityBlocked, renderActivityLimit, renderActivityUncertain, renderExpressNoWait, renderExpressWaiting, participantCommandPrefix, commandPrefix, quoteShell, withPathOutput, } from '../presentation.js';
7
- import { renderExpression, renderFailureLines } from '../operation-presentation.js';
8
- import { formatDuration } from '../time.js';
6
+ import { formatRefusal, renderActivityUncertain, renderExpressWaiting, participantCommandPrefix, commandPrefix, quoteShell, withPathOutput, } from '../presentation.js';
7
+ import { isExpressGate, renderExpression, renderFailureLines } from '../operation-presentation.js';
9
8
  import { jsonResult } from './json.js';
10
9
  export { renderExpression } from '../operation-presentation.js';
11
10
  export function saveActivityDraft(squarePath, name, body, now = Date.now()) {
@@ -101,36 +100,10 @@ export async function executeExpression(open, squarePath, requestedName, rawInpu
101
100
  exitCode(1);
102
101
  return;
103
102
  }
104
- if (isSquareError(error)) {
105
- if (error.code === 'behind' && fresh?.activitySummaries !== undefined) {
106
- process.stdout.write(renderActivityBlocked({ ...output, retryCommand: retryInput,
107
- activitySummaries: fresh.activitySummaries,
108
- }));
109
- exitCode(1);
110
- return;
111
- }
112
- if (error.code === 'capped') {
113
- process.stdout.write(renderActivityLimit({ ...output, count: fresh?.ownActivityCount,
114
- ...(fresh?.hardCap == null ? {} : { hardCap: fresh.hardCap }),
115
- }));
116
- exitCode(1);
117
- return;
118
- }
119
- if (error.code === 'throttled') {
120
- process.stdout.write(renderExpressNoWait({ ...output, reason: error.code, retryCommand: retryInput,
121
- delayMs: error.facts?.retryAfterMs ?? 1,
122
- }));
123
- exitCode(1);
124
- return;
125
- }
126
- if (error.code === 'bell_quota') {
127
- process.stdout.write(withPathOutput(squarePath, [
128
- '✕ the bell stays quiet for now', ` · you can ring it again in ${formatDuration(error.facts?.retryAfterMs ?? 1)}`,
129
- `· draft kept: ${draftPath}`, ' · retry when the bell is ready:', retryInput,
130
- ].join('\n'), output));
131
- exitCode(1);
132
- return;
133
- }
103
+ if (isSquareError(error) && isExpressGate(error.code)) {
104
+ process.stdout.write(withPathOutput(squarePath, renderFailureLines({ code: error.code, message: error.message, facts: error.facts }, { squarePath, name, owner: opts.recoveryOwner, draftPath, retry: retryInput }).join('\n'), output));
105
+ exitCode(1);
106
+ return;
134
107
  }
135
108
  const facts = isSquareError(error) ? error.facts : undefined;
136
109
  const reason = facts?.reason;
package/dist/cli/json.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export const JSON_COMMANDS = new Set([
2
- 'join', 'express', 'perceive', 'history', 'listen', 'ignore', 'unlisten', 'listening',
2
+ 'join', 'express', 'perceive', 'history', 'listen', 'ignore', 'unlisten',
3
3
  'done', 'status', 'inbox',
4
4
  ]);
5
5
  /** Serialize the SDK receipt, not its optional presentation or live participant handle. */
@@ -2,7 +2,7 @@ import type { PerceiveOptions, DiscoveryOptions, HistoryQuery, StreamStart } fro
2
2
  import { type CommandSpec } from './context.js';
3
3
  interface HistoryCommandOptions {
4
4
  query: HistoryQuery;
5
- noTruncate: boolean;
5
+ full: boolean;
6
6
  format?: string[];
7
7
  continuationArgs: string[];
8
8
  }
@@ -21,8 +21,7 @@ interface InboxIntent {
21
21
  limit: number;
22
22
  }
23
23
  export declare const inboxCommand: CommandSpec<InboxIntent, string>;
24
- export declare const claudeHookCommand: CommandSpec<undefined, string>;
25
- export declare const claudeWatchCommand: CommandSpec<undefined, string>;
24
+ export declare const claudeModCommand: CommandSpec<undefined, string>;
26
25
  export declare const codexHookCommand: CommandSpec<undefined, string>;
27
26
  export declare const codexWatchCommand: CommandSpec<undefined, string>;
28
27
  export declare const codexInterruptCommand: CommandSpec<undefined, string>;
@@ -1,6 +1,6 @@
1
1
  import path from 'node:path';
2
2
  import { once } from 'node:events';
3
- import { runClaudeHook, runClaudeWatch } from '../claude-hook.js';
3
+ import { runClaudeMod } from '../claude-mod.js';
4
4
  import { runCodexHook, runCodexInterrupt } from '../codex-hook.js';
5
5
  import { runCodexWatch } from '../codex-watch.js';
6
6
  import { isSquareError } from '../model.js';
@@ -135,7 +135,6 @@ export const perceiveCommand = {
135
135
  let limit;
136
136
  let id;
137
137
  let takeover = false;
138
- let now = false;
139
138
  const participants = [];
140
139
  for (let index = 0; index < argv.length; index++) {
141
140
  if (argv[index] === '--id') {
@@ -164,8 +163,6 @@ export const perceiveCommand = {
164
163
  }
165
164
  else if (argv[index] === '--takeover')
166
165
  takeover = true;
167
- else if (argv[index] === '--now')
168
- now = true;
169
166
  else if (argv[index] !== '--json')
170
167
  fail(`✕ perceive does not know ${argv[index]}\nsquare perceive --help`);
171
168
  }
@@ -175,8 +172,6 @@ export const perceiveCommand = {
175
172
  }
176
173
  return { id };
177
174
  }
178
- if (idle !== undefined && now)
179
- fail('perceive accepts --now or --idle, not both.');
180
175
  if (takeover && idle === undefined)
181
176
  fail('--takeover can only be used with --idle.');
182
177
  return {
@@ -244,7 +239,7 @@ function historyContinuationArgs(argv) {
244
239
  continue;
245
240
  }
246
241
  result.push(flag);
247
- if (flag === '--from' || flag === '--since' || flag === '--at' || flag === '-B' || flag === '-A' || flag === '-C' || flag === '--mention' || flag === '--grep' || flag === '--fixed' || flag === '--order' || flag === '--format') {
242
+ if (flag === '--from' || flag === '--since' || flag === '--at' || flag === '-C' || flag === '--mention' || flag === '--search' || flag === '--order' || flag === '--format') {
248
243
  const value = argv[index + 1];
249
244
  if (value !== undefined) {
250
245
  result.push(value);
@@ -261,12 +256,11 @@ function parseHistory(argv, context) {
261
256
  let after;
262
257
  let before;
263
258
  const at = [];
264
- let beforeContext;
265
- let afterContext;
259
+ let historyContext;
266
260
  let mention;
267
- let noTruncate = false;
268
- let grep;
269
- let fixed;
261
+ let full = false;
262
+ let search;
263
+ let regex = false;
270
264
  let order;
271
265
  let format;
272
266
  const participants = [];
@@ -299,32 +293,20 @@ function parseHistory(argv, context) {
299
293
  at.push(...values.map((value) => formatActivityId(parseActRef(value, flag))));
300
294
  index += 1;
301
295
  }
302
- else if (flag === '-B') {
303
- beforeContext = parseNonNegativeInteger(requireValue(argv, index, flag), flag);
304
- index += 1;
305
- }
306
- else if (flag === '-A') {
307
- afterContext = parseNonNegativeInteger(requireValue(argv, index, flag), flag);
308
- index += 1;
309
- }
310
296
  else if (flag === '-C') {
311
- const context = parseNonNegativeInteger(requireValue(argv, index, flag), flag);
312
- beforeContext = context;
313
- afterContext = context;
297
+ historyContext = parseNonNegativeInteger(requireValue(argv, index, flag), flag);
314
298
  index += 1;
315
299
  }
316
- else if (flag === '--no-truncate')
317
- noTruncate = true;
300
+ else if (flag === '--full')
301
+ full = true;
318
302
  else if (flag === '--mention') {
319
303
  mention = requireValue(argv, index, flag);
320
304
  index += 1;
321
305
  }
322
- else if (flag === '--grep') {
323
- grep = requireValue(argv, index, flag);
324
- index += 1;
325
- }
326
- else if (flag === '--fixed') {
327
- fixed = requireValue(argv, index, flag);
306
+ else if (flag === '--regex')
307
+ regex = true;
308
+ else if (flag === '--search') {
309
+ search = requireValue(argv, index, flag);
328
310
  index += 1;
329
311
  }
330
312
  else if (flag === '--order') {
@@ -341,19 +323,17 @@ function parseHistory(argv, context) {
341
323
  else if (flag !== '--json')
342
324
  fail(`✕ history does not know ${flag}\nsquare history --help`);
343
325
  }
344
- if (grep !== undefined && fixed !== undefined)
345
- fail('--grep and --fixed cannot be combined.');
346
- if (grep === '' || fixed === '')
347
- fail('--grep and --fixed require non-empty text.');
326
+ if (search === '')
327
+ fail('--search requires non-empty text.');
348
328
  if (before !== undefined && after !== undefined)
349
329
  fail('--before and --after cannot be combined.');
350
330
  return {
351
331
  query: { limit, from: participants.length === 0 ? undefined : participants, since,
352
332
  cursor: after !== undefined ? { id: after, direction: 'after' } : before === undefined ? undefined : { id: before, direction: 'before' },
353
333
  at: at.length === 0 ? undefined : at,
354
- contextBefore: beforeContext, contextAfter: afterContext, mention, order,
355
- search: grep !== undefined ? { text: grep, mode: 'regex' } : fixed === undefined ? undefined : { text: fixed, mode: 'literal' } },
356
- noTruncate, format,
334
+ context: historyContext, mention, order,
335
+ search: search === undefined ? undefined : { text: search, mode: regex ? 'regex' : 'literal' } },
336
+ full, format,
357
337
  continuationArgs: historyContinuationArgs(argv),
358
338
  };
359
339
  }
@@ -396,7 +376,7 @@ export const historyCommand = {
396
376
  }
397
377
  return renderHistoryResult(squarePath, outcome, {
398
378
  query: options.query,
399
- noTruncate: options.noTruncate,
379
+ full: options.full,
400
380
  continuationArgs: options.continuationArgs,
401
381
  });
402
382
  },
@@ -470,28 +450,10 @@ function hookCommand(runHook) {
470
450
  present: (result) => process.stdout.write(result),
471
451
  };
472
452
  }
473
- export const claudeHookCommand = {
474
- parse(argv, context) { if (argv.length > 0)
475
- usage(context.command); return undefined; },
476
- execute: async () => {
477
- try {
478
- return await runClaudeHook(await readStdin(), process.env, text => new Promise((resolve, reject) => {
479
- process.stdout.write(text, error => { if (error)
480
- reject(error);
481
- else
482
- resolve(); });
483
- }));
484
- }
485
- catch {
486
- return '';
487
- }
488
- },
489
- present: () => { },
490
- };
491
- export const claudeWatchCommand = {
453
+ export const claudeModCommand = {
492
454
  parse(argv, context) { if (argv.length > 0)
493
455
  usage(context.command); return undefined; },
494
- execute: async () => runClaudeWatch(await readStdin(), process.env),
456
+ execute: async () => runClaudeMod(await readStdin(), process.env),
495
457
  present: () => { },
496
458
  };
497
459
  export const codexHookCommand = {
@@ -1,10 +1,10 @@
1
- import { buildCommand, doneCommand, expressCommand, ignoreCommand, joinCommand, listenCommand, listeningCommand, unlistenCommand } from './square-commands.js';
1
+ import { buildCommand, doneCommand, expressCommand, ignoreCommand, joinCommand, listenCommand, unlistenCommand } from './square-commands.js';
2
2
  import { doctorCommand } from './maintenance-commands.js';
3
3
  import { harnessCommand, installCommand, uninstallCommand } from './harness-command.js';
4
4
  import { helpCommand, versionCommand } from './meta-commands.js';
5
5
  import { mcpServerCommand } from './mcp-server-command.js';
6
6
  import { JSON_COMMANDS } from './json.js';
7
- import { perceiveCommand, claudeHookCommand, claudeWatchCommand, codexHookCommand, codexWatchCommand, codexInterruptCommand, historyCommand, inboxCommand, listCommand, statusCommand, streamCommand, } from './observation-commands.js';
7
+ import { perceiveCommand, claudeModCommand, codexHookCommand, codexWatchCommand, codexInterruptCommand, historyCommand, inboxCommand, listCommand, statusCommand, streamCommand, } from './observation-commands.js';
8
8
  /** Every public command is an executable adapter, including aliases and utility commands. */
9
9
  export const commandRegistry = [
10
10
  { names: ['build'], spec: buildCommand },
@@ -12,8 +12,7 @@ export const commandRegistry = [
12
12
  { names: ['join'], spec: joinCommand },
13
13
  { names: ['stream'], spec: streamCommand },
14
14
  { names: ['inbox'], spec: inboxCommand },
15
- { names: ['claude-hook'], spec: claudeHookCommand },
16
- { names: ['claude-watch'], spec: claudeWatchCommand },
15
+ { names: ['claude-mod'], spec: claudeModCommand },
17
16
  { names: ['codex-hook'], spec: codexHookCommand },
18
17
  { names: ['codex-watch'], spec: codexWatchCommand },
19
18
  { names: ['codex-interrupt'], spec: codexInterruptCommand },
@@ -22,7 +21,6 @@ export const commandRegistry = [
22
21
  { names: ['listen'], spec: listenCommand },
23
22
  { names: ['ignore'], spec: ignoreCommand },
24
23
  { names: ['unlisten'], spec: unlistenCommand },
25
- { names: ['listening'], spec: listeningCommand },
26
24
  { names: ['done'], spec: doneCommand },
27
25
  { names: ['install'], spec: installCommand },
28
26
  { names: ['uninstall'], spec: uninstallCommand },
@@ -25,7 +25,7 @@ interface BodyIntent {
25
25
  body?: string;
26
26
  }
27
27
  interface ListenerIntent {
28
- target?: string;
28
+ target: string;
29
29
  }
30
30
  export declare const buildCommand: CommandSpec<BuildIntent, string>;
31
31
  export declare const joinCommand: CommandSpec<JoinIntent, string>;
@@ -33,6 +33,5 @@ export declare const expressCommand: CommandSpec<ActivityIntent>;
33
33
  export declare const listenCommand: CommandSpec<ListenerIntent, string>;
34
34
  export declare const ignoreCommand: CommandSpec<ListenerIntent, string>;
35
35
  export declare const unlistenCommand: CommandSpec<ListenerIntent, string>;
36
- export declare const listeningCommand: CommandSpec<ListenerIntent, string>;
37
36
  export declare const doneCommand: CommandSpec<BodyIntent, string>;
38
37
  export {};
@@ -1,6 +1,6 @@
1
1
  import { formatHardCap } from '../model.js';
2
2
  import { commandPrefix, participantCommandPrefix, withPathOutput, } from '../presentation.js';
3
- import { renderJoinResult, renderLifecycleResult, renderListenerChange, renderListeningResult, } from '../operation-presentation.js';
3
+ import { renderJoinResult, renderLifecycleResult, renderListenerChange, } from '../operation-presentation.js';
4
4
  import { Square, requireOwner, requireJoinName, selectOwnSeat } from '../square-api.js';
5
5
  import { executeExpression } from './express-presentation.js';
6
6
  import { jsonResult } from './json.js';
@@ -153,15 +153,14 @@ export const expressCommand = {
153
153
  },
154
154
  present: () => { },
155
155
  };
156
- function parseListener(argv, context, targetRequired) {
157
- const args = argv.filter((arg) => arg !== '--json');
158
- if (targetRequired ? args.length !== 1 : args.length !== 0)
159
- usage(context.command);
160
- return targetRequired ? { target: args[0] } : {};
161
- }
162
156
  function listenerCommand(verb) {
163
157
  return {
164
- parse(argv, context) { return parseListener(argv, context, true); },
158
+ parse(argv, context) {
159
+ const args = argv.filter((arg) => arg !== '--json');
160
+ if (args.length !== 1)
161
+ usage(context.command);
162
+ return { target: args[0] };
163
+ },
165
164
  async execute(intent, context) {
166
165
  const squarePath = requireSquarePath(context);
167
166
  const data = await withSquare(context, async (square) => (await selectOwnSeat(square, commandIdentity(context.env, context.owner).owner, context.name))[verb](intent.target, context.json ? {} : { observe: true }));
@@ -173,15 +172,6 @@ function listenerCommand(verb) {
173
172
  export const listenCommand = listenerCommand('listen');
174
173
  export const ignoreCommand = listenerCommand('ignore');
175
174
  export const unlistenCommand = listenerCommand('unlisten');
176
- export const listeningCommand = {
177
- parse(argv, context) { return parseListener(argv, context, false); },
178
- async execute(_intent, context) {
179
- const squarePath = requireSquarePath(context);
180
- const data = await withSquare(context, async (square) => (await selectOwnSeat(square, commandIdentity(context.env, context.owner).owner, context.name)).listening(context.json ? {} : { observe: true }));
181
- return context.json ? jsonResult(data) : renderListeningResult(squarePath, data);
182
- },
183
- present: (result) => process.stdout.write(result),
184
- };
185
175
  export const doneCommand = {
186
176
  parse(argv, context) {
187
177
  const args = argv.filter((arg) => arg !== '--json');
package/dist/decisions.js CHANGED
@@ -65,10 +65,11 @@ export function unreadActivitySummaries(facts, name, now) {
65
65
  }
66
66
  const selected = facts.activities([...summaries.values()].map((item) => item.lastIndex));
67
67
  const byIndex = new Map(selected.map((item) => [item.index, item]));
68
- return [...summaries].map(([name, summary]) => ({
69
- name, count: summary.count, latestActivityAgeMs: Math.max(0, now - summary.latestAt),
70
- preview: byIndex.get(summary.lastIndex),
71
- })).sort((a, b) => a.latestActivityAgeMs - b.latestActivityAgeMs || a.name.localeCompare(b.name));
68
+ return [...summaries].map(([name, summary]) => {
69
+ const latest = byIndex.get(summary.lastIndex);
70
+ return { name, count: summary.count, latestActivityAgeMs: Math.max(0, now - summary.latestAt),
71
+ preview: { ...(latest.body === undefined ? {} : { body: latest.body }), structured: latest.metadata !== undefined } };
72
+ }).sort((a, b) => a.latestActivityAgeMs - b.latestActivityAgeMs || a.name.localeCompare(b.name));
72
73
  }
73
74
  export function decideAct(facts, input) {
74
75
  const { now, force, reach, reply } = input;