@astrosheep/square 0.3.77 → 0.3.79
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/attention-presentation.d.ts +3 -1
- package/dist/attention-presentation.js +45 -13
- package/dist/automatic-session.js +1 -1
- package/dist/boundary-presentation.js +4 -6
- package/dist/claude-mod.js +1 -1
- package/dist/cli/catch-presentation.d.ts +2 -2
- package/dist/cli/catch-presentation.js +3 -3
- package/dist/cli/context.d.ts +11 -1
- package/dist/cli/context.js +14 -3
- package/dist/cli/express-presentation.d.ts +6 -5
- package/dist/cli/express-presentation.js +35 -30
- package/dist/cli/maintenance-commands.js +8 -8
- package/dist/cli/mcp-server-command.js +1 -1
- package/dist/cli/observation-commands.d.ts +4 -4
- package/dist/cli/observation-commands.js +20 -16
- package/dist/cli/program.d.ts +1 -1
- package/dist/cli/program.js +13 -7
- package/dist/cli/square-commands.js +35 -23
- package/dist/decisions.d.ts +9 -4
- package/dist/decisions.js +17 -8
- package/dist/delivery.d.ts +1 -1
- package/dist/help.js +15 -2
- package/dist/host-ledger-migration.js +2 -6
- package/dist/host-ledger-sqlite.js +2 -2
- package/dist/inbox.js +3 -3
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/ledger-maintenance.d.ts +1 -1
- package/dist/ledger-maintenance.js +1 -1
- package/dist/list.d.ts +3 -3
- package/dist/list.js +1 -1
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +12 -7
- package/dist/mention-parse.d.ts +2 -8
- package/dist/mention-parse.js +6 -9
- package/dist/model.d.ts +5 -1
- package/dist/notifications.js +24 -8
- package/dist/participant-host.js +5 -0
- package/dist/plugins/opencode/index.js +1 -1
- package/dist/square-actions.d.ts +22 -9
- package/dist/square-actions.js +27 -19
- package/dist/square-api.d.ts +43 -0
- package/dist/square-api.js +471 -0
- package/dist/square-facade.d.ts +124 -203
- package/dist/square-sqlite.d.ts +2 -8
- package/dist/square-sqlite.js +3 -11
- package/dist/square-tools.d.ts +4 -3
- package/dist/square-tools.js +22 -16
- package/dist/stream.d.ts +2 -2
- package/dist/views.d.ts +16 -12
- package/dist/views.js +13 -27
- package/guides/application-demand-optimization.md +5 -0
- package/guides/application-facade-delivery-evidence.md +6 -1
- package/guides/claude-native.md +3 -1
- package/guides/mcp-connection-lifetime.md +2 -2
- package/guides/mcp.md +1 -1
- package/guides/migration.md +2 -1
- package/guides/operation-cancellation.md +2 -0
- package/guides/primitive-catch-consolidation.md +5 -0
- package/guides/primitive-history-consolidation.md +5 -0
- package/guides/primitive-status-consolidation.md +5 -0
- package/guides/primitive-storage-consolidation.md +5 -0
- package/guides/row-storage-design.md +2 -2
- package/guides/square-api.md +107 -0
- package/package.json +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/skills/square/SKILL.md +3 -3
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/opencode/index.ts +1 -1
- package/plugins/paseo/client/markdown.tsx +16 -6
- package/plugins/pi/square-pi.js +70 -30
- package/skills/square/SKILL.md +3 -3
- package/dist/square-application.d.ts +0 -38
- package/dist/square-application.js +0 -683
- package/guides/application-boundary.md +0 -32
- package/guides/application-facade-design.md +0 -98
package/dist/cli/program.js
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import { helpRequest } from '../help.js';
|
|
2
2
|
import { isSquareError } from '../model.js';
|
|
3
3
|
import { commandPrefix, formatRefusal, refusalText, joinRecoveryCommand, participantIdentity, participantsRecoveryCommand, takeoverRecoveryLines, } from '../presentation.js';
|
|
4
|
-
import {
|
|
4
|
+
import { withSquare, defaultContext, parseGlobalArgs } from './context.js';
|
|
5
5
|
import { executeRegisteredCommand, findCommand } from './registry.js';
|
|
6
6
|
async function handleSquareError(error, context) {
|
|
7
7
|
if (!isSquareError(error))
|
|
8
8
|
throw error;
|
|
9
9
|
const detail = error.operation;
|
|
10
10
|
const squarePath = context?.squarePath;
|
|
11
|
-
const recovery = context === undefined || squarePath === undefined ? undefined
|
|
12
|
-
: await
|
|
13
|
-
const name = detail?.participant ?? recovery?.
|
|
11
|
+
const recovery = error.facts?.reason === 'migration_required' || context === undefined || squarePath === undefined ? undefined
|
|
12
|
+
: await withSquare({ ...context, name: detail?.participant ?? context.name }, (square) => square.recovery(detail?.participant ?? context.name)).catch(() => undefined);
|
|
13
|
+
const name = detail?.participant ?? recovery?.participant ?? context?.name;
|
|
14
14
|
const existing = [refusalText(error)];
|
|
15
|
-
if (
|
|
15
|
+
if (error.facts?.reason === 'migration_required') {
|
|
16
|
+
if (error.facts.ledgerRoot !== undefined)
|
|
17
|
+
existing.push('square ledger migrate', 'square ledger verify');
|
|
18
|
+
else if (squarePath !== undefined)
|
|
19
|
+
existing.push(`${commandPrefix(squarePath)} migrate`);
|
|
20
|
+
}
|
|
21
|
+
else if (squarePath !== undefined && name !== undefined && error.code === 'already_joined') {
|
|
16
22
|
existing.splice(0, 1, `✕ ${participantIdentity(name)} shoos you out of the square`, ` · ${participantIdentity(name)} already stands here — another session holds the name`);
|
|
17
23
|
existing.push(...takeoverRecoveryLines(squarePath, name));
|
|
18
24
|
}
|
|
@@ -34,11 +40,11 @@ async function handleSquareError(error, context) {
|
|
|
34
40
|
process.stderr.write(`${existing.join('\n')}\n`);
|
|
35
41
|
else
|
|
36
42
|
process.stderr.write(formatRefusal(squarePath, existing, {
|
|
37
|
-
participantCount: recovery?.
|
|
43
|
+
participantCount: recovery?.standingCount, held: recovery?.held,
|
|
38
44
|
}));
|
|
39
45
|
process.exit(error.code === 'not_found' ? 1 : 2);
|
|
40
46
|
}
|
|
41
|
-
/** Parse global flags, select an
|
|
47
|
+
/** Parse global flags, select an adapter, and execute through Square/Participant. */
|
|
42
48
|
export async function runCli(rawArgs = process.argv.slice(2)) {
|
|
43
49
|
let context;
|
|
44
50
|
try {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { formatHardCap } from '../model.js';
|
|
2
2
|
import { commandPrefix, participantCommandPrefix, participantIdentity, renderRoomChangeText, renderAmbientEvent, withPathOutput, } from '../presentation.js';
|
|
3
|
-
import { formatActivityId } from '../square-
|
|
3
|
+
import { Square, formatActivityId } from '../square-api.js';
|
|
4
4
|
import { style } from '../tty-style.js';
|
|
5
5
|
import { executeExpression } from './express-presentation.js';
|
|
6
|
-
import {
|
|
6
|
+
import { squareInput, withSquare, fail, parseBoundedLimit, parseHardCap, parsePositiveInteger, readStdin, requireSquarePath, requireValue, resolveBody, usage, } from './context.js';
|
|
7
7
|
const JOIN_DEFAULT_LAST = 10;
|
|
8
8
|
const JOIN_MAX_LAST = 100;
|
|
9
9
|
export const buildCommand = {
|
|
@@ -42,11 +42,18 @@ export const buildCommand = {
|
|
|
42
42
|
const markdown = await readStdin();
|
|
43
43
|
if (markdown.trim() === '')
|
|
44
44
|
fail('Missing Markdown body snippet on stdin.');
|
|
45
|
-
const
|
|
45
|
+
const square = await Square.build({ ...squareInput(context), ...intent.options, markdown });
|
|
46
|
+
let data;
|
|
47
|
+
try {
|
|
48
|
+
data = await square.status();
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
await square.close();
|
|
52
|
+
}
|
|
46
53
|
const cap = data.hardCap === null ? 'unlimited' : formatHardCap(data.hardCap);
|
|
47
54
|
const throttle = data.throttlePerMinute === undefined ? [] : [` · throttle ${data.throttlePerMinute}/min`];
|
|
48
|
-
return withPathOutput(
|
|
49
|
-
participantCount:
|
|
55
|
+
return withPathOutput(square.location, ['· the square stands ready', ` · cap ${cap}`, ...throttle].join('\n'), {
|
|
56
|
+
participantCount: 0, held: false,
|
|
50
57
|
});
|
|
51
58
|
},
|
|
52
59
|
present: (result) => process.stdout.write(result),
|
|
@@ -68,12 +75,14 @@ export const joinCommand = {
|
|
|
68
75
|
return { lastN, kick };
|
|
69
76
|
},
|
|
70
77
|
async execute(intent, context) {
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
const
|
|
78
|
+
const data = await withSquare(context, (square) => square.join(context.name, { takeover: intent.kick, last: intent.lastN, observe: true }));
|
|
79
|
+
const after = data.observation;
|
|
80
|
+
const entry = after.entry;
|
|
81
|
+
const name = data.participant.name;
|
|
82
|
+
const activities = (entry?.recentActivities ?? []).map((event) => renderAmbientEvent(event, name, {
|
|
74
83
|
now: after.now, preview: 200,
|
|
75
|
-
actNumber: event.kind === 'say' ?
|
|
76
|
-
perception:
|
|
84
|
+
actNumber: event.kind === 'say' ? entry.sayNumbers[event.index] : undefined,
|
|
85
|
+
perception: entry.perceptions[event.index],
|
|
77
86
|
})).filter(Boolean).join('\n\n');
|
|
78
87
|
const fallback = after.automaticDeliveryAvailable ? [] : [
|
|
79
88
|
'', `${participantCommandPrefix(after.location, name)} catch --idle 30m`,
|
|
@@ -83,13 +92,14 @@ export const joinCommand = {
|
|
|
83
92
|
: data.kind === 'reconnected' ? '● you are already in the square'
|
|
84
93
|
: `✓ you banished the original ${participantIdentity(name)} — the name is yours`;
|
|
85
94
|
const isRejoin = data.kind !== 'joined';
|
|
86
|
-
const entryScene = isRejoin ? '' :
|
|
87
|
-
? '*the flagstones are where you left them. so is your shadow.*' :
|
|
95
|
+
const entryScene = isRejoin || entry === undefined ? '' : entry.joinedBefore
|
|
96
|
+
? '*the flagstones are where you left them. so is your shadow.*' : entry.scene;
|
|
88
97
|
return withPathOutput(after.location, [
|
|
89
98
|
entryLine, '',
|
|
90
99
|
"· carved into the fountain's edge: every word here lands on a real ear — speak when someone needs it.",
|
|
91
100
|
...(entryScene === '' ? [] : ['', entryScene]),
|
|
92
|
-
...(isRejoin ||
|
|
101
|
+
...(isRejoin || !entry?.joinContext ? [] : ['', style('dim', 'context'), entry.joinContext]),
|
|
102
|
+
...(after.enrichmentUnavailable ? [' · joined; recent activity and context are unavailable right now'] : []),
|
|
93
103
|
...(isRejoin || activities === '' ? [] : ['', style('dim', 'recent activity'), activities]),
|
|
94
104
|
...fallback,
|
|
95
105
|
].join('\n'), { participantCount: after.standingCount, held: after.held });
|
|
@@ -128,10 +138,6 @@ function parseActivity(argv, context) {
|
|
|
128
138
|
bodyArgs.push(argument);
|
|
129
139
|
}
|
|
130
140
|
const reach = bell ? 'bell' : undefined;
|
|
131
|
-
if (bell && (noMention || mentions.length > 0))
|
|
132
|
-
fail('✕ --bell cannot be combined with --mention or --no-mention\nsquare express --help');
|
|
133
|
-
if (noMention && mentions.length > 0)
|
|
134
|
-
fail('✕ --no-mention cannot be combined with --mention\nsquare express --help');
|
|
135
141
|
if (bodyArgs.length !== 1) {
|
|
136
142
|
if (bodyArgs.length === 0 && !process.stdin.isTTY)
|
|
137
143
|
bodyArgs.push('-');
|
|
@@ -144,7 +150,7 @@ export const expressCommand = {
|
|
|
144
150
|
parse: parseActivity,
|
|
145
151
|
async execute(intent, context) {
|
|
146
152
|
const rawInput = await resolveBody(intent.activity);
|
|
147
|
-
await executeExpression(
|
|
153
|
+
await executeExpression((control) => Square.at(squareInput(context), control), requireSquarePath(context), context.name, rawInput, {
|
|
148
154
|
force: intent.force, noWait: intent.noWait, noMention: intent.noMention,
|
|
149
155
|
mentions: intent.mentions, reach: intent.reach, reply: intent.reply,
|
|
150
156
|
});
|
|
@@ -160,7 +166,8 @@ function listenerCommand(verb) {
|
|
|
160
166
|
return {
|
|
161
167
|
parse(argv, context) { return parseListener(argv, context, true); },
|
|
162
168
|
async execute(intent, context) {
|
|
163
|
-
const
|
|
169
|
+
const data = await withSquare(context, async (square) => (await square.participant(context.name))[verb](intent.target, { observe: true }));
|
|
170
|
+
const projection = data.observation;
|
|
164
171
|
const actor = participantIdentity(projection.participant);
|
|
165
172
|
const target = participantIdentity(projection.target);
|
|
166
173
|
const action = data.activity !== null
|
|
@@ -178,7 +185,8 @@ export const ignoreCommand = listenerCommand('ignore');
|
|
|
178
185
|
export const listeningCommand = {
|
|
179
186
|
parse(argv, context) { return parseListener(argv, context, false); },
|
|
180
187
|
async execute(_intent, context) {
|
|
181
|
-
const
|
|
188
|
+
const data = await withSquare(context, async (square) => (await square.participant(context.name)).listening({ observe: true }));
|
|
189
|
+
const projection = data.observation;
|
|
182
190
|
const body = data.listening.length === 0
|
|
183
191
|
? `○ ${participantIdentity(projection.participant)} is not turned toward anyone`
|
|
184
192
|
: ['listening', ...data.listening.map((target) => ` · ${participantIdentity(target)}`)].join('\n');
|
|
@@ -195,7 +203,8 @@ export const doneCommand = {
|
|
|
195
203
|
async execute(intent, context) {
|
|
196
204
|
const body = intent.body === '-' || ((intent.body === undefined || intent.body === '') && !process.stdin.isTTY)
|
|
197
205
|
? await readStdin() : intent.body;
|
|
198
|
-
const
|
|
206
|
+
const data = await withSquare(context, async (square) => (await square.participant(context.name)).done(body, { observe: true }));
|
|
207
|
+
const projection = data.observation;
|
|
199
208
|
return withPathOutput(projection.location, `○ you stepped out — your footsteps fade · ${data.activity.id}`, {
|
|
200
209
|
participantCount: projection.standingCount, held: projection.held,
|
|
201
210
|
});
|
|
@@ -209,7 +218,9 @@ export const holdCommand = {
|
|
|
209
218
|
return { body: argv[0] };
|
|
210
219
|
},
|
|
211
220
|
async execute(intent, context) {
|
|
212
|
-
const
|
|
221
|
+
const body = (await resolveBody(intent.body ?? '')).replace(/\r\n/g, '\n').trim();
|
|
222
|
+
const result = await withSquare(context, async (square) => (await square.participant(context.name)).hold(body, { observe: true }));
|
|
223
|
+
const projection = result.observation;
|
|
213
224
|
return withPathOutput(projection.location, `· ${renderRoomChangeText(projection.activity)} · ${formatActivityId(projection.activity.index)} · just now`, { participantCount: projection.standingCount, held: projection.held });
|
|
214
225
|
},
|
|
215
226
|
present: (result) => process.stdout.write(result),
|
|
@@ -221,7 +232,8 @@ export const resumeCommand = {
|
|
|
221
232
|
return {};
|
|
222
233
|
},
|
|
223
234
|
async execute(_intent, context) {
|
|
224
|
-
const
|
|
235
|
+
const result = await withSquare(context, async (square) => (await square.participant(context.name)).resume({ observe: true }));
|
|
236
|
+
const projection = result.observation;
|
|
225
237
|
return withPathOutput(projection.location, `✓ ${renderRoomChangeText(projection.activity)} · ${formatActivityId(projection.activity.index)} · just now`, { participantCount: projection.standingCount, held: projection.held });
|
|
226
238
|
},
|
|
227
239
|
present: (result) => process.stdout.write(result),
|
package/dist/decisions.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ type TimedAct<K extends Act['kind'] = Act['kind']> = Extract<Act, {
|
|
|
5
5
|
}> & {
|
|
6
6
|
at: number;
|
|
7
7
|
};
|
|
8
|
-
import type { ParticipantSummary as ParticipantStatus, UnreadActivitySummary } from './square-facade.js';
|
|
8
|
+
import type { ExpressOptions, ExpressResult, ParticipantSummary as ParticipantStatus, UnreadActivitySummary } from './square-facade.js';
|
|
9
9
|
export type { ParticipantSummary as ParticipantStatus, ParticipantPresence as CorePresenceState, UnreadActivitySummary, UnreadActivityPreview } from './square-facade.js';
|
|
10
10
|
export declare function resolveKnownName(facts: SquareRead, name: string): string;
|
|
11
11
|
export interface DecideJoinResult {
|
|
@@ -20,11 +20,10 @@ export interface DecideImplicitJoinResult {
|
|
|
20
20
|
}
|
|
21
21
|
export declare function decideJoin(facts: SquareRead, name: string, now: number): DecideJoinResult;
|
|
22
22
|
export declare function decideImplicitJoin(facts: SquareRead, name: string, now: number): DecideImplicitJoinResult;
|
|
23
|
-
export type ActDecision = {
|
|
23
|
+
export type ActDecision = ({
|
|
24
24
|
type: 'sent';
|
|
25
25
|
act: TimedAct;
|
|
26
|
-
|
|
27
|
-
} | {
|
|
26
|
+
} & Pick<ExpressResult, 'replyAuthorNotHere' | 'unmatchedMentions'>) | {
|
|
28
27
|
type: 'blocked';
|
|
29
28
|
activitySummaries: UnreadActivitySummary[];
|
|
30
29
|
} | {
|
|
@@ -43,6 +42,12 @@ export type ActDecision = {
|
|
|
43
42
|
};
|
|
44
43
|
/** Count the directed suffix, but hydrate only its last three previews per spelling. */
|
|
45
44
|
export declare function unreadActivitySummaries(facts: SquareRead, name: string, now: number): UnreadActivitySummary[];
|
|
45
|
+
export declare function expressionReach(input: {
|
|
46
|
+
body: string;
|
|
47
|
+
} & Pick<ExpressOptions, 'reach' | 'mentions' | 'noMention'>): {
|
|
48
|
+
bodyMentions: string[];
|
|
49
|
+
hasReachWithoutReply: boolean;
|
|
50
|
+
};
|
|
46
51
|
export declare function decideAct(facts: SquareRead, input: {
|
|
47
52
|
name: string;
|
|
48
53
|
body: string;
|
package/dist/decisions.js
CHANGED
|
@@ -2,7 +2,7 @@ import { SquareError, isSquareError, sameName, nameKey, validateName, } from './
|
|
|
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';
|
|
5
|
-
import { scanMentionCandidates,
|
|
5
|
+
import { scanMentionCandidates, resolveMention } from './mention-parse.js';
|
|
6
6
|
export function resolveKnownName(facts, name) {
|
|
7
7
|
validateName(name);
|
|
8
8
|
const known = facts.participant(name)?.name;
|
|
@@ -84,6 +84,11 @@ export function unreadActivitySummaries(facts, name, now) {
|
|
|
84
84
|
previews: summary.previews.map((preview) => ({ number: preview.number, act: byIndex.get(preview.index), perception: 'full' })),
|
|
85
85
|
})).sort((a, b) => a.latestActivityAgeMs - b.latestActivityAgeMs || a.name.localeCompare(b.name));
|
|
86
86
|
}
|
|
87
|
+
export function expressionReach(input) {
|
|
88
|
+
const bodyMentions = input.reach === 'bell' || input.noMention === true ? [] : scanMentionCandidates(input.body);
|
|
89
|
+
const hasReachWithoutReply = input.reach === 'bell' || (input.mentions?.length ?? 0) > 0 || input.noMention === true || bodyMentions.length > 0;
|
|
90
|
+
return { bodyMentions, hasReachWithoutReply };
|
|
91
|
+
}
|
|
87
92
|
export function decideAct(facts, input) {
|
|
88
93
|
const { now, force, body, reach, reply } = input;
|
|
89
94
|
const current = resolveStanding(facts, input.name, true);
|
|
@@ -92,6 +97,8 @@ export function decideAct(facts, input) {
|
|
|
92
97
|
throw activityRefusal('invalid_args', 'express body cannot be empty');
|
|
93
98
|
const roster = facts.roster();
|
|
94
99
|
const joinedNames = roster.filter((participant) => participant.joined).map((participant) => participant.name);
|
|
100
|
+
if (reach === 'bell' && input.mentions?.length)
|
|
101
|
+
throw activityRefusal('invalid_args', 'A bell cannot be combined with a mention', { reason: 'bell_mention_conflict', targets: [...input.mentions] });
|
|
95
102
|
const mentionNames = [];
|
|
96
103
|
const addMention = (resolved) => {
|
|
97
104
|
if (mentionNames.some((existing) => sameName(existing, resolved)))
|
|
@@ -107,21 +114,22 @@ export function decideAct(facts, input) {
|
|
|
107
114
|
throw activityRefusal('invalid_args', `${participantIdentity(requested)} is not standing in this square`, { reason: 'not_standing', targets: [requested] });
|
|
108
115
|
addMention(resolved);
|
|
109
116
|
}
|
|
110
|
-
const bodyMentions
|
|
111
|
-
|
|
112
|
-
|
|
117
|
+
const { bodyMentions, hasReachWithoutReply } = expressionReach(input);
|
|
118
|
+
const unmatchedMentions = [];
|
|
119
|
+
for (const token of bodyMentions) {
|
|
120
|
+
const resolved = resolveMention(token, joinedNames);
|
|
113
121
|
if (resolved !== undefined)
|
|
114
122
|
addMention(resolved);
|
|
123
|
+
else
|
|
124
|
+
unmatchedMentions.push(token);
|
|
115
125
|
}
|
|
116
|
-
if (
|
|
117
|
-
throw activityRefusal('invalid_args', 'A bell cannot be combined with a mention', { reason: 'bell_mention_conflict', targets: mentionNames });
|
|
118
|
-
if (input.requireExplicitReach && reach !== 'bell' && mentionNames.length === 0 && bodyMentions.length === 0 && reply === undefined && input.noMention !== true) {
|
|
126
|
+
if (input.requireExplicitReach && !hasReachWithoutReply && reply === undefined) {
|
|
119
127
|
throw activityRefusal('invalid_args', 'Express needs a recipient, reply, bell, or explicit bare reach', { reason: 'explicit_reach_required' });
|
|
120
128
|
}
|
|
121
129
|
const header = facts.header();
|
|
122
130
|
if (reply !== undefined && (!Number.isSafeInteger(reply) || reply < 0 || reply >= header.nextActIndex)) {
|
|
123
131
|
const label = Number.isSafeInteger(reply) && reply >= 0 ? formatActivityId(reply) : String(reply);
|
|
124
|
-
throw activityRefusal('invalid_args', `Unknown reply activity: ${label}`, { reason: 'invalid_reply', activityId: label, activityExists: false });
|
|
132
|
+
throw activityRefusal('invalid_args', `Unknown reply activity: ${label}`, { reason: 'invalid_reply', activityId: label, activityExists: false, hasReachWithoutReply });
|
|
125
133
|
}
|
|
126
134
|
const replyAuthor = reply === undefined ? undefined : facts.activity(reply)?.actor;
|
|
127
135
|
const replyAuthorNotHere = replyAuthor === undefined || sameName(replyAuthor, name) || facts.participant(replyAuthor)?.joined ? undefined : replyAuthor;
|
|
@@ -152,6 +160,7 @@ export function decideAct(facts, input) {
|
|
|
152
160
|
}
|
|
153
161
|
return { type: 'sent', act: { kind: 'say', actor: name, at: now, body, mentions: mentionNames,
|
|
154
162
|
...(reach === undefined ? {} : { reach }), ...(reply === undefined ? {} : { reply }) },
|
|
163
|
+
...(unmatchedMentions.length === 0 ? {} : { unmatchedMentions }),
|
|
155
164
|
...(replyAuthorNotHere === undefined ? {} : { replyAuthorNotHere }) };
|
|
156
165
|
}
|
|
157
166
|
export function validateDoneBody(body) {
|
package/dist/delivery.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ export declare function pendingPages(artifact: SquareArtifactPort, scan: Pending
|
|
|
47
47
|
at: number;
|
|
48
48
|
actor: string;
|
|
49
49
|
recipient: string;
|
|
50
|
-
route: "bell" | "
|
|
50
|
+
route: "bell" | "reply" | "mention" | "attention";
|
|
51
51
|
}[], void, unknown>;
|
|
52
52
|
/** A pass retains only its captured joins; routes and individual receipts are fresh. */
|
|
53
53
|
export declare function pendingFacts(facts: SquareRead, scan: PendingScan, heads: readonly DirectedHead[]): {
|
package/dist/help.js
CHANGED
|
@@ -17,9 +17,22 @@ const COMMANDS = [
|
|
|
17
17
|
details: ['Options:', ' --last <N> Show the last N public activities (default 10, maximum 100).', ' --kick End the standing participant and reclaim the name for this session.'],
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
|
-
names: ['express'], usage: '--as <name> express [-f|--force] [--no-wait]
|
|
20
|
+
names: ['express'], usage: '--as <name> express [-f|--force] [--no-wait] [--mention <name>... | --bell] [--no-mention] [--reply <activity-id>] <activity | ->', usesSquare: true, group: 'participant',
|
|
21
21
|
summary: 'Speak, gesture, or do both.',
|
|
22
|
-
details: [
|
|
22
|
+
details: [
|
|
23
|
+
'Reach:',
|
|
24
|
+
' --mention <name> Address one standing participant; repeat to address more.',
|
|
25
|
+
' @name Also address a standing participant named in the body; `backticks` keep @text literal.',
|
|
26
|
+
" --bell Call everyone; body @names stay literal. Cannot combine with --mention.",
|
|
27
|
+
' --no-mention Keep body @names literal; explicit --mention and --reply still apply.',
|
|
28
|
+
' --reply <activity-id> Also reach the author of an earlier activity (for example act/12).',
|
|
29
|
+
' Choose reach with a flag above or an @name in the body. Unmatched @names stay literal, with a notice.',
|
|
30
|
+
' With no mention, bell, or reply, the activity lands bare; only listeners receive it.',
|
|
31
|
+
'',
|
|
32
|
+
'Options:',
|
|
33
|
+
' -f, --force Express without first catching unread activity or satisfying attention etiquette.',
|
|
34
|
+
' --no-wait If held or throttled, save a draft and return instead of waiting. A held wait gives up on its own after 10 minutes.',
|
|
35
|
+
],
|
|
23
36
|
},
|
|
24
37
|
{ names: ['listen'], usage: '--as <name> listen <participant>', usesSquare: true, group: 'participant', summary: 'Turn an ear toward one participant\'s future bare says.' },
|
|
25
38
|
{ names: ['ignore'], usage: '--as <name> ignore <participant>', usesSquare: true, group: 'participant', summary: 'Ignore one participant\'s future mentions and bare says.' },
|
|
@@ -5,7 +5,7 @@ import { DatabaseSync } from 'node:sqlite';
|
|
|
5
5
|
import { canonicalPathSync } from './canonical-path.js';
|
|
6
6
|
import { withFileLock } from './file-lock.js';
|
|
7
7
|
import { hostLedgerRoot } from './host-ledger-root.js';
|
|
8
|
-
import { LEDGER_FILENAME, initializeLedger, insertLedgerRecord, ledgerRecordColumns, setLedgerRevisions, upgradeLedger, validateLedger, verifyLedger, inspectLedgerIdentity, } from './host-ledger-sqlite.js';
|
|
8
|
+
import { LEDGER_FILENAME, initializeLedger, insertLedgerRecord, ledgerRecordColumns, setLedgerRevisions, upgradeLedger, validateLedger, verifyLedger, inspectLedgerIdentity, migrationRequired, } from './host-ledger-sqlite.js';
|
|
9
9
|
import { inSqliteTransaction } from './sqlite-transaction.js';
|
|
10
10
|
function rootFor(options) {
|
|
11
11
|
return canonicalPathSync(options.rootPath ?? hostLedgerRoot(options.env));
|
|
@@ -25,10 +25,6 @@ async function exists(file) {
|
|
|
25
25
|
throw error;
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
-
function migrationGuidance(root, file) {
|
|
29
|
-
const name = file ?? path.join(root, LEDGER_FILENAME);
|
|
30
|
-
return `Host ledger at ${root} has a supported older format (${name}).\nStop agent sessions and MCP services using Square with this ledger; keep them stopped until migration and verification succeed; then start them with the updated Square version.\nsquare ledger migrate\nsquare ledger verify`;
|
|
31
|
-
}
|
|
32
28
|
function verificationFailure(root, file, format) {
|
|
33
29
|
const name = file ?? path.join(root, LEDGER_FILENAME);
|
|
34
30
|
const detail = format === 'unsupported'
|
|
@@ -62,7 +58,7 @@ async function verifyDatabase(file, root, signal) {
|
|
|
62
58
|
db = undefined;
|
|
63
59
|
}
|
|
64
60
|
if (format === 'old-sqlite')
|
|
65
|
-
throw
|
|
61
|
+
throw migrationRequired(root);
|
|
66
62
|
throw verificationFailure(root, file, format);
|
|
67
63
|
}
|
|
68
64
|
finally {
|
|
@@ -3,7 +3,7 @@ import { randomUUID } from 'node:crypto';
|
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { pathToFileURL } from 'node:url';
|
|
6
|
-
import { nameKey } from './model.js';
|
|
6
|
+
import { SquareError, nameKey } from './model.js';
|
|
7
7
|
import { inSqliteTransaction, retrySqliteBusy } from './sqlite-transaction.js';
|
|
8
8
|
export const LEDGER_FILENAME = 'host-ledger.sqlite';
|
|
9
9
|
export const LEDGER_APPLICATION_ID = 0x5351484c;
|
|
@@ -175,7 +175,7 @@ catch (error) {
|
|
|
175
175
|
throw error;
|
|
176
176
|
} }
|
|
177
177
|
export function migrationRequired(root) {
|
|
178
|
-
return new
|
|
178
|
+
return new SquareError('invalid_args', `Host ledger at ${root} needs offline migration.\nStop sessions and MCP services using it, back it up, then migrate and verify from the same environment before restarting.`, { reason: 'migration_required', ledgerRoot: root });
|
|
179
179
|
}
|
|
180
180
|
/** Compact runtime framing. Full schema, projections and integrity belong to explicit verify. */
|
|
181
181
|
function validateLedgerHeader(db, version = LEDGER_VERSION) {
|
package/dist/inbox.js
CHANGED
|
@@ -5,7 +5,7 @@ import { canonicalPath } from './canonical-path.js';
|
|
|
5
5
|
import { projectBoundaryEligibility, projectSessionBindings } from './square-projections.js';
|
|
6
6
|
import { freshWatchLease, WATCH_STALE_MS } from './runtime.js';
|
|
7
7
|
import { pendingHead, pendingNotifications, leaseOwnsNotification } from './delivery.js';
|
|
8
|
-
import { attentionBodyIsClipped, renderAttentionPreview } from './attention-presentation.js';
|
|
8
|
+
import { attentionBodyIsClipped, renderAttentionPreview, renderSquareReminder } from './attention-presentation.js';
|
|
9
9
|
function bindingCoordinate(binding) {
|
|
10
10
|
return JSON.stringify([binding.location, nameKey(binding.participant), binding.sessionId, binding.channel, binding.epoch, binding.cancelledThrough ?? -1]);
|
|
11
11
|
}
|
|
@@ -37,8 +37,8 @@ export function nativePendingPreview(facts, input) {
|
|
|
37
37
|
const item = facts.activity(input.actIndex);
|
|
38
38
|
if (item?.kind !== 'say')
|
|
39
39
|
return undefined;
|
|
40
|
-
return { payload: renderAttentionPreview({ squarePath: input.location, recipient: input.participant, actIndex: item.index,
|
|
41
|
-
|
|
40
|
+
return { payload: renderSquareReminder([renderAttentionPreview({ squarePath: input.location, recipient: input.participant, actIndex: item.index,
|
|
41
|
+
actor: item.actor, route: head.route, body: item.body })]), clipped: attentionBodyIsClipped(item.body) };
|
|
42
42
|
}
|
|
43
43
|
async function readInboxMembership(binding, signal) {
|
|
44
44
|
const artifact = createArtifactPort(binding.location, undefined, signal);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
export { Square } from './square-
|
|
2
|
-
export { createSquareApplication } from './square-application.js';
|
|
1
|
+
export { Square, squareLocation, squareClock, createSquareResourceOwner, discover, inbox, diagnoseSquare, migrateSquare } from './square-api.js';
|
|
3
2
|
export { SquareError } from './model.js';
|
|
4
3
|
export { squareAssignedParticipantName } from './registry.js';
|
|
5
4
|
export { createDefaultWakeTransport } from './notifications.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
export { Square } from './square-
|
|
2
|
-
export { createSquareApplication } from './square-application.js';
|
|
1
|
+
export { Square, squareLocation, squareClock, createSquareResourceOwner, discover, inbox, diagnoseSquare, migrateSquare } from './square-api.js';
|
|
3
2
|
export { SquareError } from './model.js';
|
|
4
3
|
export { squareAssignedParticipantName } from './registry.js';
|
|
5
4
|
export { createDefaultWakeTransport } from './notifications.js';
|
|
@@ -22,7 +22,7 @@ export interface LedgerMaintenanceReport {
|
|
|
22
22
|
/** Canonical locations whose artifact could not be read; their rows were retained. */
|
|
23
23
|
readonly unavailableArtifacts: readonly string[];
|
|
24
24
|
}
|
|
25
|
-
/**
|
|
25
|
+
/** Maintenance entry: root selection uses the caller's captured environment. */
|
|
26
26
|
export declare function gcHostLedger(options?: LedgerMaintenanceOptions & {
|
|
27
27
|
readonly env?: NodeJS.ProcessEnv;
|
|
28
28
|
}): Promise<LedgerMaintenanceReport>;
|
|
@@ -62,7 +62,7 @@ async function provenClosable(location, group) {
|
|
|
62
62
|
await artifact.close();
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
-
/**
|
|
65
|
+
/** Maintenance entry: root selection uses the caller's captured environment. */
|
|
66
66
|
export function gcHostLedger(options = {}) {
|
|
67
67
|
return maintainHostLedger(hostLedgerForEnv(options.env ?? process.env), options);
|
|
68
68
|
}
|
package/dist/list.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DiscoveryResult, DiscoveryOptions, OpenOptions, OperationControl } from './square-facade.js';
|
|
2
2
|
export declare const DEFAULT_LIST_DEPTH = 4;
|
|
3
3
|
export declare const MAX_LIST_DEPTH = 16;
|
|
4
4
|
export declare const DEFAULT_LIST_LIMIT = 20;
|
|
5
5
|
export declare const MAX_LIST_LIMIT = 100;
|
|
6
|
-
/** Bounded deterministic discovery
|
|
7
|
-
export declare function discoverSquares(root: string, options?:
|
|
6
|
+
/** Bounded deterministic discovery for the public resource API. */
|
|
7
|
+
export declare function discoverSquares(root: string, options?: DiscoveryOptions, control?: OperationControl, openOptions?: OpenOptions): Promise<DiscoveryResult>;
|
package/dist/list.js
CHANGED
|
@@ -32,7 +32,7 @@ async function readSquareListItem(filePath, root, options, control) {
|
|
|
32
32
|
return null;
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
-
/** Bounded deterministic discovery
|
|
35
|
+
/** Bounded deterministic discovery for the public resource API. */
|
|
36
36
|
export async function discoverSquares(root, options = {}, control, openOptions = {}) {
|
|
37
37
|
const depth = options.depth ?? DEFAULT_LIST_DEPTH;
|
|
38
38
|
const limit = options.limit ?? DEFAULT_LIST_LIMIT;
|
package/dist/mcp.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { SquareAtInput } from './square-facade.js';
|
|
3
3
|
/** The server owns its artifact connection lifetime, so callers cannot lend one. */
|
|
4
|
-
export interface SquareMcpOptions extends Omit<
|
|
4
|
+
export interface SquareMcpOptions extends Omit<SquareAtInput, 'resources'> {
|
|
5
5
|
}
|
|
6
6
|
export type SquareMcpServer = McpServer;
|
|
7
7
|
export declare function createSquareMcpServer(options: SquareMcpOptions): SquareMcpServer;
|
package/dist/mcp.js
CHANGED
|
@@ -4,9 +4,9 @@ import { SQUARE_TOOLS, SQUARE_TOOL_INSTRUCTIONS, squareToolFailure } from './squ
|
|
|
4
4
|
import { operationEnv } from './automatic-session.js';
|
|
5
5
|
import { readClaudeMcpCall } from './claude-mcp-context.js';
|
|
6
6
|
import { SQUARE_IDENTITY } from './identity.js';
|
|
7
|
-
import {
|
|
7
|
+
import { Square, createSquareResourceOwner } from './square-api.js';
|
|
8
8
|
export function createSquareMcpServer(options) {
|
|
9
|
-
const baseEnv = { ...options.env };
|
|
9
|
+
const baseEnv = { ...(options.env ?? process.env) };
|
|
10
10
|
const resources = createSquareResourceOwner();
|
|
11
11
|
// Calls to the configured square borrow one server-owned connection.
|
|
12
12
|
const inFlight = new Set();
|
|
@@ -33,15 +33,20 @@ export function createSquareMcpServer(options) {
|
|
|
33
33
|
: typeof opencodeSessionId === 'string' && opencodeSessionId.trim()
|
|
34
34
|
? operationEnv('opencode', opencodeSessionId, baseEnv)
|
|
35
35
|
: baseEnv;
|
|
36
|
-
const
|
|
36
|
+
const square = await Square.at({
|
|
37
37
|
...options, env, cwd: claude?.cwd ?? options.cwd,
|
|
38
|
-
|
|
38
|
+
path: location ?? options.path,
|
|
39
39
|
participant: participant ?? options.participant,
|
|
40
40
|
// The server owner is bound to its configured square; overrides close after each call.
|
|
41
41
|
resources: location === undefined && claude === undefined ? resources : undefined,
|
|
42
|
-
}, {
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
}, { signal: extra.signal });
|
|
43
|
+
let structuredContent;
|
|
44
|
+
try {
|
|
45
|
+
structuredContent = await tool.run(square, args, { signal: extra.signal });
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
await square.close();
|
|
49
|
+
}
|
|
45
50
|
return { content: [{ type: 'text', text: JSON.stringify(structuredContent) }], structuredContent };
|
|
46
51
|
}
|
|
47
52
|
catch (error) {
|
package/dist/mention-parse.d.ts
CHANGED
|
@@ -1,8 +1,2 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
readonly token: string;
|
|
4
|
-
/** UTF-16 index of the `@` in the body. */
|
|
5
|
-
readonly index: number;
|
|
6
|
-
}
|
|
7
|
-
export declare function scanMentionCandidates(body: string): MentionCandidate[];
|
|
8
|
-
export declare function resolveNameAt(body: string, candidate: MentionCandidate, names: readonly string[]): string | undefined;
|
|
1
|
+
export declare function scanMentionCandidates(body: string): string[];
|
|
2
|
+
export declare function resolveMention(text: string, names: readonly string[]): string | undefined;
|
package/dist/mention-parse.js
CHANGED
|
@@ -10,8 +10,8 @@ import { NAME_GRAPHEME_SOURCE, sameName } from './model.js';
|
|
|
10
10
|
* (`user@host`), URLs (`x.com/@user`), and `snake_@case` stay literal. Only ASCII
|
|
11
11
|
* word characters, `_`, `-`, `/`, and `@` block a boundary: CJK adjacency like
|
|
12
12
|
* `告诉@rei` stays mentionable because CJK text has no spaces to lean on.
|
|
13
|
-
* -
|
|
14
|
-
*
|
|
13
|
+
* - Slash-separated names are allowed. Resolution may stop at a CJK suffix, but never
|
|
14
|
+
* at an ASCII handle continuation: `@rei这个` can reach `rei`; `@rei2` cannot.
|
|
15
15
|
*/
|
|
16
16
|
const MENTION_TOKEN = new RegExp(`@((?:${NAME_GRAPHEME_SOURCE})+(?:/(?:${NAME_GRAPHEME_SOURCE})+)*)`, 'vg');
|
|
17
17
|
const BOUNDARY_BLOCKER = /[A-Za-z0-9_\-/@]/;
|
|
@@ -62,29 +62,26 @@ export function scanMentionCandidates(body) {
|
|
|
62
62
|
const start = match.index;
|
|
63
63
|
if (spans.some(([from, to]) => start >= from && start < to))
|
|
64
64
|
continue;
|
|
65
|
-
const
|
|
66
|
-
const previous = unit === undefined ? undefined
|
|
67
|
-
: unit >= 0xdc00 && unit <= 0xdfff && start >= 2 ? String.fromCodePoint(body.codePointAt(start - 2)) : String.fromCodePoint(unit);
|
|
65
|
+
const previous = body[start - 1];
|
|
68
66
|
if (previous !== undefined && BOUNDARY_BLOCKER.test(previous))
|
|
69
67
|
continue;
|
|
70
68
|
const token = match[1];
|
|
71
69
|
const key = token.toLocaleLowerCase();
|
|
72
70
|
if (!seen.has(key)) {
|
|
73
71
|
seen.add(key);
|
|
74
|
-
found.push(
|
|
72
|
+
found.push(token);
|
|
75
73
|
}
|
|
76
74
|
}
|
|
77
75
|
return found;
|
|
78
76
|
}
|
|
79
77
|
/**
|
|
80
|
-
* Resolve the longest standing name
|
|
78
|
+
* Resolve the longest standing name at the start of a candidate token. A partial
|
|
81
79
|
* match is only accepted when the character right after it is not handle-continuation
|
|
82
80
|
* (ASCII word char, `_`, `-`, `/`): `@rei2` never degrades into mentioning `rei`, while
|
|
83
81
|
* CJK-fluent text like `告诉@rei这个` still mentions `rei`.
|
|
84
82
|
*/
|
|
85
83
|
const TRAILING_BLOCKER = /[A-Za-z0-9_\-/]/;
|
|
86
|
-
export function
|
|
87
|
-
const text = body.slice(candidate.index + 1, candidate.index + 1 + candidate.token.length);
|
|
84
|
+
export function resolveMention(text, names) {
|
|
88
85
|
let best;
|
|
89
86
|
for (const name of names) {
|
|
90
87
|
if (name.length > text.length)
|
package/dist/model.d.ts
CHANGED
|
@@ -24,10 +24,14 @@ export interface SquareErrorFacts {
|
|
|
24
24
|
reason?: 'never_joined' | 'not_standing' | 'bell_mention_conflict' | 'not_addressed' | 'migration_required' | 'explicit_reach_required' | 'invalid_reply' | 'invalid_done_body';
|
|
25
25
|
/** The activity id a `catch --id` refusal named. */
|
|
26
26
|
activityId?: string;
|
|
27
|
+
/** The host ledger requiring offline migration, rather than the selected artifact. */
|
|
28
|
+
ledgerRoot?: string;
|
|
27
29
|
/** Requested recipient spellings, without parsing presentation copy. */
|
|
28
30
|
targets?: readonly string[];
|
|
29
31
|
/** Whether the artifact holds that activity at all; an unknown id needs an ordinary page. */
|
|
30
32
|
activityExists?: boolean;
|
|
33
|
+
/** Express has a recipient, bell, body candidate, or explicit bare reach without its reply. */
|
|
34
|
+
hasReachWithoutReply?: boolean;
|
|
31
35
|
pending?: number;
|
|
32
36
|
holder?: string;
|
|
33
37
|
holdReason?: string;
|
|
@@ -36,7 +40,7 @@ export interface SquareErrorFacts {
|
|
|
36
40
|
declare class SquareErrorBase<Code extends string> extends Error {
|
|
37
41
|
code: Code;
|
|
38
42
|
facts?: SquareErrorFacts | undefined;
|
|
39
|
-
operation?: import('./square-facade.js').
|
|
43
|
+
operation?: import('./square-facade.js').OperationFailureDetail;
|
|
40
44
|
constructor(code: Code, message: string, facts?: SquareErrorFacts | undefined);
|
|
41
45
|
}
|
|
42
46
|
export declare class SquareError extends SquareErrorBase<SquareErrorCode> {
|