@astrosheep/square 0.3.85 → 0.3.87
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifact.d.ts +6 -1
- package/dist/artifact.js +79 -10
- package/dist/boundary-presentation.d.ts +5 -4
- package/dist/boundary-presentation.js +21 -25
- package/dist/catch-decisions.js +2 -1
- package/dist/claude-delivery-status.d.ts +8 -2
- package/dist/claude-delivery-status.js +26 -10
- package/dist/claude-delivery.d.ts +8 -4
- package/dist/claude-delivery.js +114 -104
- package/dist/claude-mcp-context.js +2 -2
- package/dist/claude-mod.js +119 -105
- package/dist/cli/context.d.ts +3 -1
- package/dist/cli/context.js +25 -1
- package/dist/cli/express-presentation.d.ts +3 -1
- package/dist/cli/express-presentation.js +15 -12
- package/dist/cli/mcp-server-command.js +2 -2
- package/dist/cli/observation-commands.js +2 -1
- package/dist/cli/registry.js +0 -2
- package/dist/cli/square-commands.d.ts +4 -2
- package/dist/cli/square-commands.js +21 -6
- package/dist/codex-hook.d.ts +3 -3
- package/dist/codex-hook.js +4 -4
- package/dist/codex-queue.d.ts +5 -6
- package/dist/codex-queue.js +9 -5
- package/dist/decisions.d.ts +3 -2
- package/dist/decisions.js +18 -6
- package/dist/delivery-health.d.ts +32 -2
- package/dist/delivery-health.js +108 -45
- package/dist/delivery-operations.d.ts +1 -2
- package/dist/delivery-operations.js +155 -196
- package/dist/delivery.d.ts +14 -23
- package/dist/delivery.js +48 -18
- package/dist/file-lock.d.ts +8 -1
- package/dist/file-lock.js +2 -0
- package/dist/help.js +12 -14
- package/dist/hints.js +2 -3
- package/dist/inbox.d.ts +35 -9
- package/dist/inbox.js +228 -144
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -3
- package/dist/list.js +9 -4
- package/dist/mcp.js +12 -1
- package/dist/model.d.ts +39 -5
- package/dist/model.js +84 -0
- package/dist/notifications.d.ts +8 -6
- package/dist/notifications.js +56 -143
- package/dist/operation-presentation.js +12 -14
- package/dist/participant-identity.d.ts +0 -3
- package/dist/participant-identity.js +0 -31
- package/dist/paseo-delivery.d.ts +5 -6
- package/dist/paseo-delivery.js +13 -8
- package/dist/paseo.d.ts +1 -1
- package/dist/paseo.js +1 -1
- package/dist/plugins/opencode/delivery.d.ts +8 -3
- package/dist/plugins/opencode/delivery.js +187 -132
- package/dist/plugins/opencode/index.js +12 -38
- package/dist/ports.d.ts +112 -40
- package/dist/presentation-operations.d.ts +1 -1
- package/dist/presentation-operations.js +51 -35
- package/dist/presentation.d.ts +0 -1
- package/dist/presentation.js +18 -13
- package/dist/seats.d.ts +7 -0
- package/dist/seats.js +27 -0
- package/dist/session-lifecycle.d.ts +2 -2
- package/dist/session-lifecycle.js +10 -12
- package/dist/sqlite-square.d.ts +53 -0
- package/dist/sqlite-square.js +602 -0
- package/dist/square-api.d.ts +12 -41
- package/dist/square-api.js +26 -474
- package/dist/square-backend.d.ts +13 -0
- package/dist/square-core.d.ts +3 -2
- package/dist/square-domain.d.ts +12 -5
- package/dist/square-domain.js +163 -7
- package/dist/square-facade.d.ts +16 -10
- package/dist/square-index.d.ts +24 -0
- package/dist/square-index.js +131 -0
- package/dist/square-legacy-framing.d.ts +9 -0
- package/dist/square-legacy-framing.js +56 -0
- package/dist/square-location.d.ts +15 -0
- package/dist/square-location.js +76 -0
- package/dist/square-migration-host-ledger.d.ts +26 -0
- package/dist/square-migration-host-ledger.js +168 -0
- package/dist/square-migration.d.ts +5 -5
- package/dist/square-migration.js +373 -71
- package/dist/square-sqlite.d.ts +9 -9
- package/dist/square-sqlite.js +329 -79
- package/dist/square-stdio.d.ts +22 -0
- package/dist/square-stdio.js +222 -0
- package/dist/square-tools.js +30 -3
- package/dist/stdio-server.d.ts +3 -0
- package/dist/stdio-server.js +192 -0
- package/dist/stream.d.ts +3 -2
- package/dist/stream.js +9 -10
- package/dist/views.d.ts +9 -14
- package/dist/views.js +50 -70
- package/dist/wakes.d.ts +6 -5
- package/dist/wakes.js +20 -17
- package/guides/application-facade-delivery-evidence.md +1 -1
- package/guides/backends.md +180 -0
- package/guides/claude-native.md +9 -9
- package/guides/mcp.md +1 -1
- package/guides/migration.md +107 -67
- package/guides/row-storage-design.md +1 -1
- package/guides/square-api.md +69 -30
- package/package.json +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/hooks/register.js +1 -1
- package/plugins/claude/skills/square/SKILL.md +7 -7
- package/plugins/claude/types.d.ts +1 -1
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/opencode/delivery.ts +170 -118
- package/plugins/opencode/index.ts +12 -30
- package/plugins/pi/square-pi.js +58 -20
- package/skills/square/SKILL.md +7 -7
- package/dist/cli/ledger-command.d.ts +0 -12
- package/dist/cli/ledger-command.js +0 -72
- package/dist/host-ledger-file-adapter.d.ts +0 -47
- package/dist/host-ledger-file-adapter.js +0 -384
- package/dist/host-ledger-migration.d.ts +0 -27
- package/dist/host-ledger-migration.js +0 -264
- package/dist/host-ledger-root.d.ts +0 -2
- package/dist/host-ledger-root.js +0 -9
- package/dist/host-ledger-sqlite.d.ts +0 -65
- package/dist/host-ledger-sqlite.js +0 -368
- package/dist/host-ledger.d.ts +0 -195
- package/dist/ledger-maintenance.d.ts +0 -30
- package/dist/ledger-maintenance.js +0 -110
- package/dist/open-square.d.ts +0 -36
- package/dist/open-square.js +0 -81
- package/dist/participant-host.d.ts +0 -22
- package/dist/participant-host.js +0 -92
- package/dist/registry.d.ts +0 -55
- package/dist/registry.js +0 -123
- package/dist/routes.d.ts +0 -39
- package/dist/routes.js +0 -99
- package/dist/square-actions.d.ts +0 -56
- package/dist/square-actions.js +0 -353
- package/dist/square-projections.d.ts +0 -31
- package/dist/square-projections.js +0 -64
- package/dist/wake-attempts.d.ts +0 -10
- package/dist/wake-attempts.js +0 -39
- package/dist/wake-eligibility.d.ts +0 -53
- package/dist/wake-eligibility.js +0 -123
- package/dist/wake-evidence.d.ts +0 -33
- package/dist/wake-evidence.js +0 -32
- package/guides/host-ledger.md +0 -58
- package/guides/indexed-host-ledger-performance.md +0 -39
- /package/dist/{host-ledger.js → square-backend.js} +0 -0
package/dist/cli/context.js
CHANGED
|
@@ -2,6 +2,7 @@ import os from 'node:os';
|
|
|
2
2
|
import { commandUsageHint } from '../help.js';
|
|
3
3
|
import { parseParticipantList, validateName } from '../model.js';
|
|
4
4
|
import { Square, squareClock } from '../square-api.js';
|
|
5
|
+
import { harnessSessionSources } from '../participant-identity.js';
|
|
5
6
|
export async function readStdin() {
|
|
6
7
|
try {
|
|
7
8
|
let content = '';
|
|
@@ -139,7 +140,30 @@ export function defaultContext(command, squarePath, name) {
|
|
|
139
140
|
return { command, cwd: process.cwd(), env: { ...process.env }, squarePath, name, homeDir: os.homedir() };
|
|
140
141
|
}
|
|
141
142
|
export function squareInput(context) {
|
|
142
|
-
|
|
143
|
+
const { body, wakeAddress } = commandIdentity(context.env);
|
|
144
|
+
return { cwd: context.cwd, env: context.env, path: context.squarePath, participant: context.name,
|
|
145
|
+
...(body === undefined ? {} : { body }), ...(wakeAddress === undefined ? {} : { wakeAddress }) };
|
|
146
|
+
}
|
|
147
|
+
function commandIdentity(env) {
|
|
148
|
+
const coordinates = harnessSessionSources.flatMap(({ variable, channel, provider }) => {
|
|
149
|
+
const session = env[variable]?.trim();
|
|
150
|
+
return session === undefined || session === '' ? [] : [{ channel, session, native: provider !== undefined }];
|
|
151
|
+
});
|
|
152
|
+
const native = coordinates.filter((coordinate) => coordinate.native);
|
|
153
|
+
if (native.length > 1)
|
|
154
|
+
throw new Error('Multiple harness execution coordinates are present; select one explicit body.');
|
|
155
|
+
const paseo = coordinates.find((coordinate) => coordinate.channel === 'paseo');
|
|
156
|
+
// A child marker without a reliable agent id must never inherit the parent or Paseo identity.
|
|
157
|
+
if (env.CLAUDE_CODE_CHILD_SESSION === '1')
|
|
158
|
+
return {};
|
|
159
|
+
const selected = native[0] ?? paseo;
|
|
160
|
+
if (selected === undefined)
|
|
161
|
+
return {};
|
|
162
|
+
const body = { channel: selected.channel, session: selected.session };
|
|
163
|
+
const wakeAddress = selected.channel === 'codex'
|
|
164
|
+
? { kind: 'codex-queue', address: { threadId: selected.session } }
|
|
165
|
+
: paseo === undefined ? undefined : { kind: 'paseo', address: { agentId: paseo.session } };
|
|
166
|
+
return { body, ...(wakeAddress === undefined ? {} : { wakeAddress }) };
|
|
143
167
|
}
|
|
144
168
|
/** Adapter lifetime only; each callback uses the actual public resource API. */
|
|
145
169
|
export async function withSquare(context, run, control) {
|
|
@@ -3,9 +3,11 @@ import type { OperationControl, ExpressOptions } from '../square-facade.js';
|
|
|
3
3
|
export { renderExpression } from '../operation-presentation.js';
|
|
4
4
|
export interface ActivityOptions extends Omit<ExpressOptions, 'reply'> {
|
|
5
5
|
readonly reply?: string;
|
|
6
|
+
readonly metadataJson?: string;
|
|
7
|
+
readonly bodyProvided?: boolean;
|
|
6
8
|
}
|
|
7
9
|
export declare function saveActivityDraft(squarePath: string, name: string, body: string, now?: number): string;
|
|
8
10
|
/** Retry choices are transport syntax, never a second admission decision. */
|
|
9
11
|
export declare function activityRetryCommand(squarePath: string, name: string, opts: ActivityOptions, force?: boolean): string;
|
|
10
12
|
/** Body adaptation, terminal IO and drafts only; Participant owns every attempt and wait. */
|
|
11
|
-
export declare function executeExpression(open: (control?: OperationControl) => Promise<Square>, squarePath: string, requestedName: string | undefined, rawInput: string, opts: ActivityOptions): Promise<void>;
|
|
13
|
+
export declare function executeExpression(open: (control?: OperationControl) => Promise<Square>, squarePath: string, requestedName: string | undefined, rawInput: string | undefined, opts: ActivityOptions): Promise<void>;
|
|
@@ -8,7 +8,7 @@ import { renderExpression } from '../operation-presentation.js';
|
|
|
8
8
|
import { formatDuration } from '../time.js';
|
|
9
9
|
export { renderExpression } from '../operation-presentation.js';
|
|
10
10
|
export function saveActivityDraft(squarePath, name, body, now = Date.now()) {
|
|
11
|
-
const draftDir = path.join(path.dirname(squarePath), 'drafts');
|
|
11
|
+
const draftDir = path.join(/^[A-Za-z][A-Za-z0-9+.-]*:/.test(squarePath) ? process.cwd() : path.dirname(squarePath), 'drafts');
|
|
12
12
|
fs.mkdirSync(draftDir, { recursive: true });
|
|
13
13
|
const hash = crypto.createHash('sha256').update(body).digest('hex').slice(0, 8);
|
|
14
14
|
const stamp = new Date(now).toISOString().replace(/\.\d{3}Z$/, 'Z').replace(/[:.]/g, '-');
|
|
@@ -37,7 +37,9 @@ export function activityRetryCommand(squarePath, name, opts, force = opts.force
|
|
|
37
37
|
const reach = (opts.reach === 'bell' ? ' --bell' : '')
|
|
38
38
|
+ (opts.mentions ?? []).map((target) => ` --mention ${quoteShell(target)}`).join('')
|
|
39
39
|
+ (opts.noMention === true ? ' --no-mention' : '');
|
|
40
|
-
|
|
40
|
+
const metadata = opts.metadataJson === undefined ? '' : ` --metadata ${quoteShell(opts.metadataJson)}`;
|
|
41
|
+
const body = opts.bodyProvided === false ? '' : ' -';
|
|
42
|
+
return `${participantCommandPrefix(squarePath, name)} express${force ? ' --force' : ''}${opts.noWait ? ' --no-wait' : ''}${reach}${metadata}${replyArgument(opts.reply)}${body}`;
|
|
41
43
|
}
|
|
42
44
|
/** Body adaptation, terminal IO and drafts only; Participant owns every attempt and wait. */
|
|
43
45
|
export async function executeExpression(open, squarePath, requestedName, rawInput, opts) {
|
|
@@ -54,8 +56,9 @@ export async function executeExpression(open, squarePath, requestedName, rawInpu
|
|
|
54
56
|
const participant = await square.participant(requestedName);
|
|
55
57
|
name = participant.name;
|
|
56
58
|
attempted = true;
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
+
const { metadataJson: _metadataJson, bodyProvided: _bodyProvided, ...expressOptions } = opts;
|
|
60
|
+
const outcome = await participant.express(rawInput === undefined ? undefined : rawInput.replace(/\r\n/g, '\n').trim(), {
|
|
61
|
+
...expressOptions, reply: opts.reply, observe: true,
|
|
59
62
|
}, {
|
|
60
63
|
signal: controller.signal,
|
|
61
64
|
onProgress(progress) {
|
|
@@ -75,7 +78,7 @@ export async function executeExpression(open, squarePath, requestedName, rawInpu
|
|
|
75
78
|
const detail = error?.operation;
|
|
76
79
|
name = detail?.participant ?? name ?? 'participant';
|
|
77
80
|
// This MUST precede recovery: even an unreadable artifact cannot eat the raw body.
|
|
78
|
-
const draftPath = saveActivityDraft(squarePath, name, rawInput);
|
|
81
|
+
const draftPath = saveActivityDraft(squarePath, name, rawInput ?? '');
|
|
79
82
|
if (square === undefined)
|
|
80
83
|
square = await open().catch(() => undefined);
|
|
81
84
|
const fresh = await square?.recovery(name).catch(() => undefined);
|
|
@@ -85,6 +88,7 @@ export async function executeExpression(open, squarePath, requestedName, rawInpu
|
|
|
85
88
|
&& detail?.expressEffect !== 'landed' && detail?.expressEffect !== 'unknown'
|
|
86
89
|
&& (!attempted || detail?.expressEffect === 'unsent' || (isSquareError(error) && error.facts?.activityUnsent === true));
|
|
87
90
|
const retryCommand = activityRetryCommand(squarePath, name, opts);
|
|
91
|
+
const retryInput = opts.bodyProvided === false ? retryCommand : `${retryCommand} < ${quoteShell(draftPath)}`;
|
|
88
92
|
const exitCode = (code) => { process.exitCode = controller.signal.aborted ? 130 : code; };
|
|
89
93
|
if (!confirmedUnsent) {
|
|
90
94
|
process.stderr.write(renderActivityUncertain({ ...output, landedId: landedId ?? detail?.landedId,
|
|
@@ -94,8 +98,7 @@ export async function executeExpression(open, squarePath, requestedName, rawInpu
|
|
|
94
98
|
}
|
|
95
99
|
if (isSquareError(error)) {
|
|
96
100
|
if (error.code === 'behind' && fresh?.activitySummaries !== undefined) {
|
|
97
|
-
process.stdout.write(renderActivityBlocked({ ...output, retryCommand,
|
|
98
|
-
forceCommand: activityRetryCommand(squarePath, name, opts, true),
|
|
101
|
+
process.stdout.write(renderActivityBlocked({ ...output, retryCommand: retryInput,
|
|
99
102
|
activitySummaries: fresh.activitySummaries.map((summary) => ({ ...summary, previews: [...summary.previews] })),
|
|
100
103
|
}));
|
|
101
104
|
exitCode(1);
|
|
@@ -109,7 +112,7 @@ export async function executeExpression(open, squarePath, requestedName, rawInpu
|
|
|
109
112
|
return;
|
|
110
113
|
}
|
|
111
114
|
if (error.code === 'held' || error.code === 'throttled') {
|
|
112
|
-
process.stdout.write(renderExpressNoWait({ ...output, reason: error.code, retryCommand,
|
|
115
|
+
process.stdout.write(renderExpressNoWait({ ...output, reason: error.code, retryCommand: retryInput,
|
|
113
116
|
delayMs: error.facts?.retryAfterMs ?? 1, holder: error.facts?.holder, holdReason: error.facts?.holdReason,
|
|
114
117
|
}));
|
|
115
118
|
exitCode(1);
|
|
@@ -118,7 +121,7 @@ export async function executeExpression(open, squarePath, requestedName, rawInpu
|
|
|
118
121
|
if (error.code === 'bell_quota') {
|
|
119
122
|
process.stdout.write(withPathOutput(squarePath, [
|
|
120
123
|
'✕ the bell stays quiet for now', ` · you can ring it again in ${formatDuration(error.facts?.retryAfterMs ?? 1)}`,
|
|
121
|
-
`· draft kept: ${draftPath}`, ' · retry when the bell is ready:',
|
|
124
|
+
`· draft kept: ${draftPath}`, ' · retry when the bell is ready:', retryInput,
|
|
122
125
|
].join('\n'), output));
|
|
123
126
|
exitCode(1);
|
|
124
127
|
return;
|
|
@@ -144,7 +147,7 @@ export async function executeExpression(open, squarePath, requestedName, rawInpu
|
|
|
144
147
|
if (repair) {
|
|
145
148
|
lines.push(participantsRecoveryCommand(squarePath), reason === 'bell_mention_conflict'
|
|
146
149
|
? ' · ring the bell without explicit mentions:'
|
|
147
|
-
: ' · retry without explicit recipients:', `${activityRetryCommand(squarePath, name, { ...opts, mentions: [] })} < ${quoteShell(draftPath)}`);
|
|
150
|
+
: ' · retry without explicit recipients:', `${activityRetryCommand(squarePath, name, { ...opts, mentions: [] })}${opts.bodyProvided === false ? '' : ` < ${quoteShell(draftPath)}`}`);
|
|
148
151
|
}
|
|
149
152
|
else if (error.code === 'invalid_args' || error.code === 'invalid_name') {
|
|
150
153
|
if (opts.mentions?.length)
|
|
@@ -155,10 +158,10 @@ export async function executeExpression(open, squarePath, requestedName, rawInpu
|
|
|
155
158
|
}
|
|
156
159
|
if (reason === 'invalid_reply') {
|
|
157
160
|
const withoutReply = { ...opts, reply: undefined };
|
|
158
|
-
lines.push(`${commandPrefix(squarePath)} history --limit 10`, ' · choose a valid --reply from history, or send without a reply using this repair:', `${activityRetryCommand(squarePath, name, withoutReply)} < ${quoteShell(draftPath)}`);
|
|
161
|
+
lines.push(`${commandPrefix(squarePath)} history --limit 10`, ' · choose a valid --reply from history, or send without a reply using this repair:', `${activityRetryCommand(squarePath, name, withoutReply)}${withoutReply.bodyProvided === false ? '' : ` < ${quoteShell(draftPath)}`}`);
|
|
159
162
|
}
|
|
160
163
|
else if (!repair) {
|
|
161
|
-
lines.push(' · correct the problem above before retrying; keep your intended recipients and reply:', `${
|
|
164
|
+
lines.push(' · correct the problem above before retrying; keep your intended recipients and reply:', `${retryInput}`);
|
|
162
165
|
}
|
|
163
166
|
process.stderr.write(formatRefusal(squarePath, lines, output));
|
|
164
167
|
exitCode(2);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fail } from './context.js';
|
|
1
|
+
import { fail, squareInput } from './context.js';
|
|
2
2
|
export const mcpServerCommand = {
|
|
3
3
|
parse(argv) {
|
|
4
4
|
if (argv.length > 0)
|
|
@@ -7,7 +7,7 @@ export const mcpServerCommand = {
|
|
|
7
7
|
},
|
|
8
8
|
async execute(_intent, context) {
|
|
9
9
|
const { runSquareMcpStdio } = await import('../mcp.js');
|
|
10
|
-
await runSquareMcpStdio(
|
|
10
|
+
await runSquareMcpStdio(squareInput(context));
|
|
11
11
|
},
|
|
12
12
|
present() { },
|
|
13
13
|
};
|
|
@@ -122,7 +122,7 @@ export const streamCommand = {
|
|
|
122
122
|
async execute(intent, context) {
|
|
123
123
|
const squarePath = requireSquarePath(context);
|
|
124
124
|
if (!intent.ndjson) {
|
|
125
|
-
process.stderr.write(`✕ interactive stream was removed\nsquare --location ${quoteShell(path.resolve(context.cwd, squarePath))} stream --ndjson\n`);
|
|
125
|
+
process.stderr.write(`✕ interactive stream was removed\nsquare --location ${quoteShell(/^[A-Za-z][A-Za-z0-9+.-]*:/.test(squarePath) ? squarePath : path.resolve(context.cwd, squarePath))} stream --ndjson\n`);
|
|
126
126
|
process.exitCode = 2;
|
|
127
127
|
return;
|
|
128
128
|
}
|
|
@@ -408,6 +408,7 @@ function jsonLine(sayNumbers, item) {
|
|
|
408
408
|
body: 'body' in act && typeof act.body === 'string' ? act.body : '',
|
|
409
409
|
number: act.kind === 'say' ? sayNumbers[act.index] : null,
|
|
410
410
|
mentions: act.kind === 'say' ? [...(act.mentions ?? [])] : [],
|
|
411
|
+
metadata: act.kind === 'say' ? act.metadata ?? null : null,
|
|
411
412
|
reach: act.kind === 'say' ? act.reach ?? null : null,
|
|
412
413
|
reply: act.kind === 'say' && act.reply !== undefined ? formatActivityId(act.reply) : null,
|
|
413
414
|
});
|
package/dist/cli/registry.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { buildCommand, doneCommand, expressCommand, holdCommand, ignoreCommand, joinCommand, listenCommand, listeningCommand, resumeCommand, unlistenCommand } from './square-commands.js';
|
|
2
2
|
import { doctorCommand, migrateCommand } from './maintenance-commands.js';
|
|
3
|
-
import { ledgerCommand } from './ledger-command.js';
|
|
4
3
|
import { harnessCommand, installCommand, uninstallCommand } from './harness-command.js';
|
|
5
4
|
import { helpCommand, versionCommand } from './meta-commands.js';
|
|
6
5
|
import { mcpServerCommand } from './mcp-server-command.js';
|
|
@@ -28,7 +27,6 @@ export const commandRegistry = [
|
|
|
28
27
|
{ names: ['harness'], spec: harnessCommand },
|
|
29
28
|
{ names: ['doctor'], spec: doctorCommand },
|
|
30
29
|
{ names: ['migrate'], spec: migrateCommand },
|
|
31
|
-
{ names: ['ledger'], spec: ledgerCommand },
|
|
32
30
|
{ names: ['history'], spec: historyCommand },
|
|
33
31
|
{ names: ['status'], spec: statusCommand },
|
|
34
32
|
{ names: ['participants'], spec: participantsCommand },
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type BuildOptions, type HardCap } from '../model.js';
|
|
2
|
-
import type { Reach } from '../square-facade.js';
|
|
2
|
+
import type { Reach, ActivityMetadata } from '../square-facade.js';
|
|
3
3
|
import { type CommandSpec } from './context.js';
|
|
4
4
|
interface BuildIntent {
|
|
5
5
|
options: BuildOptions & {
|
|
@@ -11,13 +11,15 @@ interface JoinIntent {
|
|
|
11
11
|
kick: boolean;
|
|
12
12
|
}
|
|
13
13
|
interface ActivityIntent {
|
|
14
|
-
activity
|
|
14
|
+
activity?: string;
|
|
15
15
|
force: boolean;
|
|
16
16
|
noWait: boolean;
|
|
17
17
|
noMention: boolean;
|
|
18
18
|
mentions: string[];
|
|
19
19
|
reach?: Reach;
|
|
20
20
|
reply?: string;
|
|
21
|
+
metadata?: ActivityMetadata;
|
|
22
|
+
metadataJson?: string;
|
|
21
23
|
}
|
|
22
24
|
interface BodyIntent {
|
|
23
25
|
body?: string;
|
|
@@ -87,6 +87,8 @@ function parseActivity(argv, context) {
|
|
|
87
87
|
let noMention = false;
|
|
88
88
|
const mentions = [];
|
|
89
89
|
let reply;
|
|
90
|
+
let metadata;
|
|
91
|
+
let metadataJson;
|
|
90
92
|
const bodyArgs = [];
|
|
91
93
|
for (let index = 0; index < argv.length; index++) {
|
|
92
94
|
const argument = argv[index];
|
|
@@ -108,25 +110,38 @@ function parseActivity(argv, context) {
|
|
|
108
110
|
reply = requireValue(argv, index, argument);
|
|
109
111
|
index += 1;
|
|
110
112
|
}
|
|
113
|
+
else if (argument === '--metadata') {
|
|
114
|
+
metadataJson = requireValue(argv, index, argument);
|
|
115
|
+
try {
|
|
116
|
+
metadata = JSON.parse(metadataJson);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
fail('✕ invalid --metadata: expected JSON');
|
|
120
|
+
}
|
|
121
|
+
index += 1;
|
|
122
|
+
}
|
|
111
123
|
else
|
|
112
124
|
bodyArgs.push(argument);
|
|
113
125
|
}
|
|
114
126
|
const reach = bell ? 'bell' : undefined;
|
|
115
|
-
if (bodyArgs.length
|
|
116
|
-
|
|
127
|
+
if (bodyArgs.length > 1)
|
|
128
|
+
fail("express accepts one body argument (a quoted string or '-')");
|
|
129
|
+
if (bodyArgs.length === 0 && metadataJson === undefined) {
|
|
130
|
+
if (!process.stdin.isTTY)
|
|
117
131
|
bodyArgs.push('-');
|
|
118
132
|
else
|
|
119
|
-
fail("express requires a body argument
|
|
133
|
+
fail("express requires a body argument or --metadata <json>");
|
|
120
134
|
}
|
|
121
|
-
return { activity: bodyArgs[0], force, noWait, noMention, mentions, reach, reply };
|
|
135
|
+
return { activity: bodyArgs[0], force, noWait, noMention, mentions, reach, reply, metadata, metadataJson };
|
|
122
136
|
}
|
|
123
137
|
export const expressCommand = {
|
|
124
138
|
parse: parseActivity,
|
|
125
139
|
async execute(intent, context) {
|
|
126
|
-
const rawInput = await resolveBody(intent.activity);
|
|
140
|
+
const rawInput = intent.activity === undefined ? undefined : await resolveBody(intent.activity);
|
|
127
141
|
await executeExpression((control) => Square.at(squareInput(context), control), requireSquarePath(context), context.name, rawInput, {
|
|
128
142
|
force: intent.force, noWait: intent.noWait, noMention: intent.noMention,
|
|
129
|
-
mentions: intent.mentions, reach: intent.reach, reply: intent.reply,
|
|
143
|
+
mentions: intent.mentions, reach: intent.reach, reply: intent.reply, metadata: intent.metadata,
|
|
144
|
+
metadataJson: intent.metadataJson, bodyProvided: intent.activity !== undefined,
|
|
130
145
|
});
|
|
131
146
|
},
|
|
132
147
|
present: () => { },
|
package/dist/codex-hook.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { Body, PendingMembership } from './model.js';
|
|
2
|
+
import type { WakeTransports } from './notifications.js';
|
|
3
3
|
export interface CodexHookInput {
|
|
4
4
|
session_id?: unknown;
|
|
5
5
|
hook_event_name?: unknown;
|
|
@@ -10,6 +10,6 @@ export type HookTrace = (line: string) => void;
|
|
|
10
10
|
/** Overall hook budget: Codex kills the hook at its configured 5s timeout, so the hook must
|
|
11
11
|
* bound every wait/retry itself and exit with a readable stage trace before that. */
|
|
12
12
|
export declare const CODEX_HOOK_BUDGET_MS = 4500;
|
|
13
|
-
export declare function codexHookResponse(input: CodexHookInput, lookup?: (
|
|
13
|
+
export declare function codexHookResponse(input: CodexHookInput, lookup?: (identity: Body, env?: NodeJS.ProcessEnv, signal?: AbortSignal) => Promise<PendingMembership[]> | PendingMembership[], env?: NodeJS.ProcessEnv, deliveryTransports?: WakeTransports, trace?: HookTrace): Promise<object | undefined>;
|
|
14
14
|
export declare function runCodexHook(inputText: string, env?: NodeJS.ProcessEnv): Promise<string>;
|
|
15
15
|
export declare function runCodexHookAsync(inputText: string, env?: NodeJS.ProcessEnv): Promise<string>;
|
package/dist/codex-hook.js
CHANGED
|
@@ -15,7 +15,7 @@ function hookBudgetMs(env) {
|
|
|
15
15
|
const configured = Number(env.SQUARE_CODEX_HOOK_BUDGET_MS ?? CODEX_HOOK_BUDGET_MS);
|
|
16
16
|
return Number.isFinite(configured) && configured > 0 ? configured : CODEX_HOOK_BUDGET_MS;
|
|
17
17
|
}
|
|
18
|
-
export async function codexHookResponse(input, lookup = sessionInbox, env = process.env,
|
|
18
|
+
export async function codexHookResponse(input, lookup = sessionInbox, env = process.env, deliveryTransports, trace = defaultTrace) {
|
|
19
19
|
if (typeof input.session_id !== 'string' || input.session_id === '')
|
|
20
20
|
return undefined;
|
|
21
21
|
if (typeof input.hook_event_name !== 'string')
|
|
@@ -38,20 +38,20 @@ export async function codexHookResponse(input, lookup = sessionInbox, env = proc
|
|
|
38
38
|
await recordCodexBoundary(input.session_id, hookEventName === 'Stop' ? 'Stop' : 'non-stop', env, signal);
|
|
39
39
|
stage('boundary-record');
|
|
40
40
|
if (hookEventName === 'Stop') {
|
|
41
|
-
await sweepPrivilegedPending(cwd, env,
|
|
41
|
+
await sweepPrivilegedPending(cwd, env, deliveryTransports, sweepDeadline, signal).catch(() => undefined);
|
|
42
42
|
stage('sweep', signal.aborted ? 'aborted=true' : '');
|
|
43
43
|
return undefined;
|
|
44
44
|
}
|
|
45
45
|
let response;
|
|
46
46
|
try {
|
|
47
|
-
response = await presentPendingAtBoundary(input.session_id, (context) => ({ hookSpecificOutput: { hookEventName: 'PostToolUse', additionalContext: context } }), lookup, env, signal);
|
|
47
|
+
response = await presentPendingAtBoundary({ channel: 'codex', session: input.session_id }, (context) => ({ hookSpecificOutput: { hookEventName: 'PostToolUse', additionalContext: context } }), lookup, env, signal);
|
|
48
48
|
stage('presentation', signal.aborted ? 'aborted=true' : '');
|
|
49
49
|
}
|
|
50
50
|
catch (error) {
|
|
51
51
|
stage('presentation', `error=${error instanceof Error ? error.name : String(error)}`);
|
|
52
52
|
response = undefined;
|
|
53
53
|
}
|
|
54
|
-
await sweepPrivilegedPending(cwd, env,
|
|
54
|
+
await sweepPrivilegedPending(cwd, env, deliveryTransports, sweepDeadline, signal).catch(() => undefined);
|
|
55
55
|
stage('sweep', signal.aborted ? 'aborted=true' : '');
|
|
56
56
|
return response;
|
|
57
57
|
}
|
package/dist/codex-queue.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { WakeRequest, WakeOutcome, WakeTransportPort } from './ports.js';
|
|
2
2
|
import { type WakeRoute } from './model.js';
|
|
3
3
|
export interface CodexQueueRequest {
|
|
4
4
|
threadId: string;
|
|
@@ -15,14 +15,13 @@ export declare function sendCodexQueue({ threadId, message }: CodexQueueRequest,
|
|
|
15
15
|
env?: NodeJS.ProcessEnv;
|
|
16
16
|
timeoutMs?: number;
|
|
17
17
|
}): void;
|
|
18
|
-
export interface
|
|
18
|
+
export interface CodexQueueTransportOptions {
|
|
19
19
|
env?: NodeJS.ProcessEnv;
|
|
20
20
|
sendQueue?: typeof sendCodexQueue;
|
|
21
21
|
}
|
|
22
|
-
export declare class
|
|
22
|
+
export declare class CodexQueueTransport implements WakeTransportPort {
|
|
23
23
|
private readonly opts;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
dispatch(address: Readonly<Record<string, string>>, payload: string, beforeSend: () => Promise<boolean>, timeoutMs?: number): Promise<WakeAdapterResult>;
|
|
24
|
+
constructor(opts?: CodexQueueTransportOptions);
|
|
25
|
+
attempt(request: WakeRequest, timeoutMs: number, beforeSend: () => Promise<boolean>): Promise<WakeOutcome>;
|
|
27
26
|
}
|
|
28
27
|
export type { WakeRoute };
|
package/dist/codex-queue.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
2
|
import { codexQueueEligible } from './codex-boundary-state.js';
|
|
3
|
+
import { wakePreview } from './delivery.js';
|
|
3
4
|
export class CodexQueueSendError extends Error {
|
|
4
5
|
kind;
|
|
5
6
|
constructor(message, kind) {
|
|
@@ -30,16 +31,15 @@ export function sendCodexQueue({ threadId, message }, opts = {}) {
|
|
|
30
31
|
const messageText = output || `codex queue exited with ${result.status ?? 'no status'}`;
|
|
31
32
|
throw new CodexQueueSendError(messageText, classifyFailure(messageText));
|
|
32
33
|
}
|
|
33
|
-
export class
|
|
34
|
+
export class CodexQueueTransport {
|
|
34
35
|
opts;
|
|
35
|
-
kind = 'codex-queue';
|
|
36
36
|
constructor(opts = {}) {
|
|
37
37
|
this.opts = opts;
|
|
38
38
|
}
|
|
39
|
-
async
|
|
39
|
+
async attempt(request, timeoutMs, beforeSend) {
|
|
40
40
|
const deadline = Date.now() + timeoutMs;
|
|
41
41
|
const remainingMs = () => Math.max(0, deadline - Date.now());
|
|
42
|
-
const threadId = address.threadId?.trim();
|
|
42
|
+
const threadId = request.route.address.threadId?.trim();
|
|
43
43
|
if (!threadId) {
|
|
44
44
|
return { outcome: 'failed', unavailable: true, signature: 'invalid_address', message: 'Codex route has no thread id.', routeStale: true };
|
|
45
45
|
}
|
|
@@ -53,8 +53,12 @@ export class CodexQueueAdapter {
|
|
|
53
53
|
retainRoute: true,
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
|
+
const payload = request.artifact === undefined ? undefined
|
|
57
|
+
: (await request.artifact.read((facts) => wakePreview(facts, request))).value;
|
|
58
|
+
if (payload === undefined)
|
|
59
|
+
return { outcome: 'not-capable', diagnostic: 'No pending Square activity to preview.' };
|
|
56
60
|
if (!(await beforeSend()))
|
|
57
|
-
return { outcome: '
|
|
61
|
+
return { outcome: 'not-capable' };
|
|
58
62
|
if (!await codexQueueEligible(threadId, env)) {
|
|
59
63
|
return {
|
|
60
64
|
outcome: 'failed',
|
package/dist/decisions.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Act, type Reach } from './model.js';
|
|
1
|
+
import { type Act, type Reach, type ActivityMetadata } from './model.js';
|
|
2
2
|
import type { SquareRead } from './ports.js';
|
|
3
3
|
type TimedAct<K extends Act['kind'] = Act['kind']> = Extract<Act, {
|
|
4
4
|
kind: K;
|
|
@@ -44,9 +44,10 @@ export type ActDecision = ({
|
|
|
44
44
|
export declare function unreadActivitySummaries(facts: SquareRead, name: string, now: number): UnreadActivitySummary[];
|
|
45
45
|
export declare function decideAct(facts: SquareRead, input: {
|
|
46
46
|
name: string;
|
|
47
|
-
body
|
|
47
|
+
body?: string;
|
|
48
48
|
force: boolean;
|
|
49
49
|
now: number;
|
|
50
|
+
metadata?: ActivityMetadata;
|
|
50
51
|
mentions?: readonly string[];
|
|
51
52
|
reach?: Reach;
|
|
52
53
|
reply?: number;
|
package/dist/decisions.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SquareError, isSquareError, sameName, nameKey, validateName, } from './model.js';
|
|
1
|
+
import { SquareError, isSquareError, sameName, nameKey, validateName, validateActivityMetadata, activityMetadataHasContent, } from './model.js';
|
|
2
2
|
import { participantIdentity } from './participant-identity.js';
|
|
3
3
|
import { UNREAD_BLOCK_GRACE_MS, freshWatchLease, readCursor, FACT_PAGE_SIZE, THROTTLE_WINDOW_MS } from './runtime.js';
|
|
4
4
|
import { formatActivityId, MAX_IDENTITY_SET_SIZE } from './square-core.js';
|
|
@@ -85,11 +85,23 @@ export function unreadActivitySummaries(facts, name, now) {
|
|
|
85
85
|
})).sort((a, b) => a.latestActivityAgeMs - b.latestActivityAgeMs || a.name.localeCompare(b.name));
|
|
86
86
|
}
|
|
87
87
|
export function decideAct(facts, input) {
|
|
88
|
-
const { now, force,
|
|
88
|
+
const { now, force, reach, reply } = input;
|
|
89
|
+
let metadata;
|
|
90
|
+
try {
|
|
91
|
+
metadata = validateActivityMetadata(input.metadata);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
if (isSquareError(error))
|
|
95
|
+
error.facts = { ...error.facts, activityUnsent: true };
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
if (input.body !== undefined && typeof input.body !== 'string')
|
|
99
|
+
throw activityRefusal('invalid_args', 'express body must be a string');
|
|
100
|
+
const body = input.body?.trim() === '' ? undefined : input.body;
|
|
101
|
+
if (body === undefined && !activityMetadataHasContent(metadata))
|
|
102
|
+
throw activityRefusal('invalid_args', 'express requires a non-empty body or metadata');
|
|
89
103
|
const current = resolveStanding(facts, input.name, true);
|
|
90
104
|
const name = current.name;
|
|
91
|
-
if (body.trim() === '')
|
|
92
|
-
throw activityRefusal('invalid_args', 'express body cannot be empty');
|
|
93
105
|
const roster = facts.roster();
|
|
94
106
|
const joinedNames = roster.filter((participant) => participant.joined).map((participant) => participant.name);
|
|
95
107
|
if (reach === 'bell' && input.mentions?.length)
|
|
@@ -109,7 +121,7 @@ export function decideAct(facts, input) {
|
|
|
109
121
|
throw activityRefusal('invalid_args', `${participantIdentity(requested)} is not standing in this square`, { reason: 'not_standing', targets: [requested] });
|
|
110
122
|
addMention(resolved);
|
|
111
123
|
}
|
|
112
|
-
const bodyMentions = reach === 'bell' || input.noMention === true ? [] : scanMentionCandidates(body);
|
|
124
|
+
const bodyMentions = reach === 'bell' || input.noMention === true ? [] : scanMentionCandidates(body ?? '');
|
|
113
125
|
const unmatchedMentions = [];
|
|
114
126
|
for (const token of bodyMentions) {
|
|
115
127
|
const resolved = resolveMention(token, joinedNames);
|
|
@@ -150,7 +162,7 @@ export function decideAct(facts, input) {
|
|
|
150
162
|
if (activitySummaries.length > 0 && activitySummaries[0].latestActivityAgeMs > UNREAD_BLOCK_GRACE_MS)
|
|
151
163
|
return { type: 'blocked', activitySummaries };
|
|
152
164
|
}
|
|
153
|
-
return { type: 'sent', act: { kind: 'say', actor: name, at: now, body, mentions: mentionNames,
|
|
165
|
+
return { type: 'sent', act: { kind: 'say', actor: name, at: now, ...(body === undefined ? {} : { body }), ...(metadata === undefined ? {} : { metadata }), mentions: mentionNames,
|
|
154
166
|
...(reach === undefined ? {} : { reach }), ...(reply === undefined ? {} : { reply }) },
|
|
155
167
|
...(unmatchedMentions.length === 0 ? {} : { unmatchedMentions }),
|
|
156
168
|
...(replyAuthorNotHere === undefined ? {} : { replyAuthorNotHere }) };
|
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
import { type DirectedNotificationRoute } from './delivery.js';
|
|
2
|
-
import
|
|
2
|
+
import type { DeliveryEvidence } from './ports.js';
|
|
3
|
+
export interface WakeAttempt {
|
|
4
|
+
readonly nativeObservations?: readonly NonNullable<DeliveryEvidence['nativeObservations']>[number][];
|
|
5
|
+
readonly at: number;
|
|
6
|
+
readonly attention: {
|
|
7
|
+
readonly squarePath: string;
|
|
8
|
+
readonly recipient: string;
|
|
9
|
+
readonly actIndex: number;
|
|
10
|
+
};
|
|
11
|
+
readonly routeKind: import('./model.js').WakeRouteKind;
|
|
12
|
+
readonly outcome: 'accepted' | 'unknown' | 'failed';
|
|
13
|
+
readonly signature?: string;
|
|
14
|
+
readonly attemptN: number;
|
|
15
|
+
readonly session?: string;
|
|
16
|
+
readonly message?: string;
|
|
17
|
+
readonly diagnostic?: unknown;
|
|
18
|
+
}
|
|
19
|
+
export interface WakeReleaseDiagnostic {
|
|
20
|
+
readonly at: number;
|
|
21
|
+
readonly attention: {
|
|
22
|
+
readonly squarePath: string;
|
|
23
|
+
readonly recipient: string;
|
|
24
|
+
readonly actIndex: number;
|
|
25
|
+
};
|
|
26
|
+
readonly routeKind?: import('./model.js').WakeRouteKind;
|
|
27
|
+
readonly attemptN?: number;
|
|
28
|
+
readonly session?: string;
|
|
29
|
+
readonly signature?: string;
|
|
30
|
+
readonly message?: string;
|
|
31
|
+
readonly diagnostic?: unknown;
|
|
32
|
+
}
|
|
3
33
|
export type DeliveryHealthKind = 'awaiting' | 'wake-accepted' | 'wake-unknown' | 'presented-not-delivered' | 'unreachable';
|
|
4
34
|
export interface DeliveryHealthItem {
|
|
5
35
|
squarePath: string;
|
|
@@ -12,7 +42,7 @@ export interface DeliveryHealthItem {
|
|
|
12
42
|
kind: DeliveryHealthKind;
|
|
13
43
|
attempt?: WakeAttempt;
|
|
14
44
|
}
|
|
15
|
-
/**
|
|
45
|
+
/** Classify current pending attention from the artifact's seats and delivery evidence. */
|
|
16
46
|
export declare function classifyDeliveryHealth(squarePath: string, opts: {
|
|
17
47
|
graceMs: number;
|
|
18
48
|
now?: number;
|