@astrosheep/square 0.3.78 → 0.3.80
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 +2 -0
- package/dist/attention-presentation.js +39 -4
- package/dist/automatic-session.js +1 -1
- package/dist/boundary-presentation.js +4 -7
- 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 +42 -43
- package/dist/cli/maintenance-commands.js +6 -6
- 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 +5 -5
- package/dist/cli/square-commands.js +35 -23
- package/dist/decisions.d.ts +3 -5
- package/dist/decisions.js +10 -9
- package/dist/delivery.d.ts +1 -1
- package/dist/help.js +15 -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 +2 -2
- 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 +26 -19
- package/dist/square-api.d.ts +43 -0
- package/dist/square-api.js +471 -0
- package/dist/square-facade.d.ts +123 -203
- package/dist/square-tools.d.ts +4 -3
- package/dist/square-tools.js +21 -15
- 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 +7 -2
- package/guides/mcp-connection-lifetime.md +2 -2
- package/guides/mcp.md +1 -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 +1 -1
- 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 +4 -4
- 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 +7 -6
- package/skills/square/SKILL.md +4 -4
- 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,7 +1,7 @@
|
|
|
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))
|
|
@@ -9,8 +9,8 @@ async function handleSquareError(error, context) {
|
|
|
9
9
|
const detail = error.operation;
|
|
10
10
|
const squarePath = context?.squarePath;
|
|
11
11
|
const recovery = error.facts?.reason === 'migration_required' || context === undefined || squarePath === undefined ? undefined
|
|
12
|
-
: await
|
|
13
|
-
const name = detail?.participant ?? recovery?.
|
|
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
15
|
if (error.facts?.reason === 'migration_required') {
|
|
16
16
|
if (error.facts.ledgerRoot !== undefined)
|
|
@@ -40,11 +40,11 @@ async function handleSquareError(error, context) {
|
|
|
40
40
|
process.stderr.write(`${existing.join('\n')}\n`);
|
|
41
41
|
else
|
|
42
42
|
process.stderr.write(formatRefusal(squarePath, existing, {
|
|
43
|
-
participantCount: recovery?.
|
|
43
|
+
participantCount: recovery?.standingCount, held: recovery?.held,
|
|
44
44
|
}));
|
|
45
45
|
process.exit(error.code === 'not_found' ? 1 : 2);
|
|
46
46
|
}
|
|
47
|
-
/** Parse global flags, select an
|
|
47
|
+
/** Parse global flags, select an adapter, and execute through Square/Participant. */
|
|
48
48
|
export async function runCli(rawArgs = process.argv.slice(2)) {
|
|
49
49
|
let context;
|
|
50
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 { 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
|
} | {
|
|
@@ -52,7 +51,6 @@ export declare function decideAct(facts: SquareRead, input: {
|
|
|
52
51
|
reach?: Reach;
|
|
53
52
|
reply?: number;
|
|
54
53
|
noMention?: boolean;
|
|
55
|
-
requireExplicitReach?: boolean;
|
|
56
54
|
}): ActDecision;
|
|
57
55
|
export declare function validateDoneBody(body: unknown): void;
|
|
58
56
|
export declare function coreDone(facts: SquareRead, name: string, body: string, now: number): TimedAct<'done'>;
|
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;
|
|
@@ -92,6 +92,8 @@ export function decideAct(facts, input) {
|
|
|
92
92
|
throw activityRefusal('invalid_args', 'express body cannot be empty');
|
|
93
93
|
const roster = facts.roster();
|
|
94
94
|
const joinedNames = roster.filter((participant) => participant.joined).map((participant) => participant.name);
|
|
95
|
+
if (reach === 'bell' && input.mentions?.length)
|
|
96
|
+
throw activityRefusal('invalid_args', 'A bell cannot be combined with a mention', { reason: 'bell_mention_conflict', targets: [...input.mentions] });
|
|
95
97
|
const mentionNames = [];
|
|
96
98
|
const addMention = (resolved) => {
|
|
97
99
|
if (mentionNames.some((existing) => sameName(existing, resolved)))
|
|
@@ -107,16 +109,14 @@ export function decideAct(facts, input) {
|
|
|
107
109
|
throw activityRefusal('invalid_args', `${participantIdentity(requested)} is not standing in this square`, { reason: 'not_standing', targets: [requested] });
|
|
108
110
|
addMention(resolved);
|
|
109
111
|
}
|
|
110
|
-
const bodyMentions = input.noMention === true ? [] : scanMentionCandidates(body);
|
|
111
|
-
|
|
112
|
-
|
|
112
|
+
const bodyMentions = reach === 'bell' || input.noMention === true ? [] : scanMentionCandidates(body);
|
|
113
|
+
const unmatchedMentions = [];
|
|
114
|
+
for (const token of bodyMentions) {
|
|
115
|
+
const resolved = resolveMention(token, joinedNames);
|
|
113
116
|
if (resolved !== undefined)
|
|
114
117
|
addMention(resolved);
|
|
115
|
-
|
|
116
|
-
|
|
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) {
|
|
119
|
-
throw activityRefusal('invalid_args', 'Express needs a recipient, reply, bell, or explicit bare reach', { reason: 'explicit_reach_required' });
|
|
118
|
+
else
|
|
119
|
+
unmatchedMentions.push(token);
|
|
120
120
|
}
|
|
121
121
|
const header = facts.header();
|
|
122
122
|
if (reply !== undefined && (!Number.isSafeInteger(reply) || reply < 0 || reply >= header.nextActIndex)) {
|
|
@@ -152,6 +152,7 @@ export function decideAct(facts, input) {
|
|
|
152
152
|
}
|
|
153
153
|
return { type: 'sent', act: { kind: 'say', actor: name, at: now, body, mentions: mentionNames,
|
|
154
154
|
...(reach === undefined ? {} : { reach }), ...(reply === undefined ? {} : { reply }) },
|
|
155
|
+
...(unmatchedMentions.length === 0 ? {} : { unmatchedMentions }),
|
|
155
156
|
...(replyAuthorNotHere === undefined ? {} : { replyAuthorNotHere }) };
|
|
156
157
|
}
|
|
157
158
|
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
|
+
' Address with --mention, --bell, --reply, or a standing @name in the body. Otherwise the activity lands bare; unmatched @names stay literal, with a notice.',
|
|
30
|
+
' Bare activities are delivered only to participants listening to the sender.',
|
|
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.' },
|
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
|
@@ -21,7 +21,7 @@ export interface SquareErrorFacts {
|
|
|
21
21
|
/** Express admission rejected before any activity was committed. */
|
|
22
22
|
activityUnsent?: true;
|
|
23
23
|
/** Refusal reason, including absent participants, conflicting reach, and inaccessible activities. */
|
|
24
|
-
reason?: 'never_joined' | 'not_standing' | 'bell_mention_conflict' | 'not_addressed' | 'migration_required' | '
|
|
24
|
+
reason?: 'never_joined' | 'not_standing' | 'bell_mention_conflict' | 'not_addressed' | 'migration_required' | 'invalid_reply' | 'invalid_done_body';
|
|
25
25
|
/** The activity id a `catch --id` refusal named. */
|
|
26
26
|
activityId?: string;
|
|
27
27
|
/** The host ledger requiring offline migration, rather than the selected artifact. */
|
|
@@ -38,7 +38,7 @@ export interface SquareErrorFacts {
|
|
|
38
38
|
declare class SquareErrorBase<Code extends string> extends Error {
|
|
39
39
|
code: Code;
|
|
40
40
|
facts?: SquareErrorFacts | undefined;
|
|
41
|
-
operation?: import('./square-facade.js').
|
|
41
|
+
operation?: import('./square-facade.js').OperationFailureDetail;
|
|
42
42
|
constructor(code: Code, message: string, facts?: SquareErrorFacts | undefined);
|
|
43
43
|
}
|
|
44
44
|
export declare class SquareError extends SquareErrorBase<SquareErrorCode> {
|
package/dist/notifications.js
CHANGED
|
@@ -2,7 +2,7 @@ import { hostLedgerRoot } from './host-ledger-root.js';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import { SquareError } from './model.js';
|
|
5
|
-
import {
|
|
5
|
+
import { renderAttentionPreview, renderSquareReminder } from './attention-presentation.js';
|
|
6
6
|
import { openSquare } from './open-square.js';
|
|
7
7
|
import { closeOpenSquare } from './open-square.js';
|
|
8
8
|
import { deliverPending, sweepPending } from './delivery-operations.js';
|
|
@@ -96,13 +96,29 @@ export function createWakeTransport(adapters, hostLedger, clock, nativePorts = {
|
|
|
96
96
|
if (adapter === undefined)
|
|
97
97
|
return { outcome: 'not-capable', diagnostic: `no adapter for ${request.route.kind}` };
|
|
98
98
|
try {
|
|
99
|
-
const
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
99
|
+
const location = await canonicalPath(request.location);
|
|
100
|
+
const square = await openSquare(location, { hostLedger }).catch(() => undefined);
|
|
101
|
+
if (square === undefined)
|
|
102
|
+
return { outcome: 'not-capable', diagnostic: `Square is unavailable at ${location}` };
|
|
103
|
+
let payload;
|
|
104
|
+
try {
|
|
105
|
+
const index = parseActivityId(request.activity);
|
|
106
|
+
payload = (await square.artifact.read((facts) => {
|
|
107
|
+
const head = index === undefined ? undefined : pendingHead(facts, request.participant, index);
|
|
108
|
+
const item = index === undefined ? undefined : facts.activity(index);
|
|
109
|
+
if (!head || item?.kind !== 'say')
|
|
110
|
+
return undefined;
|
|
111
|
+
return renderSquareReminder([renderAttentionPreview({
|
|
112
|
+
squarePath: location, recipient: request.participant, actIndex: item.index,
|
|
113
|
+
actor: item.actor, route: head.route, body: item.body,
|
|
114
|
+
})]);
|
|
115
|
+
})).value;
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
await closeOpenSquare(square);
|
|
119
|
+
}
|
|
120
|
+
if (payload === undefined)
|
|
121
|
+
return { outcome: 'not-capable', diagnostic: 'No pending Square activity to preview.' };
|
|
106
122
|
const result = await adapter.dispatch(request.route.address, payload, finalGate, timeoutMs, request);
|
|
107
123
|
if (result.outcome !== 'gate-rejected')
|
|
108
124
|
return result;
|
package/dist/participant-host.js
CHANGED
|
@@ -10,6 +10,8 @@ export async function identityRouteDraft(context, participant) {
|
|
|
10
10
|
if (context.location === undefined || context.location === 'memory')
|
|
11
11
|
return undefined;
|
|
12
12
|
const identity = processIdentity(context.env ?? process.env);
|
|
13
|
+
if (identity.channel === 'unknown')
|
|
14
|
+
return undefined;
|
|
13
15
|
const provider = identity.channel === 'claude-code' ? 'claude' : identity.channel === 'opencode' ? 'opencode' : identity.channel === 'pi' ? 'pi' : identity.channel === 'paseo' ? 'paseo' : 'codex';
|
|
14
16
|
const boundary = { location: context.location, participant, sessionId: identity.session, provider };
|
|
15
17
|
const env = context.env ?? process.env;
|
|
@@ -51,6 +53,9 @@ export async function ensureLocalPresence(context, participant, epoch) {
|
|
|
51
53
|
if (context.hostLedger === undefined || context.location === undefined || context.location === 'memory')
|
|
52
54
|
return;
|
|
53
55
|
const identity = processIdentity(context.env ?? process.env);
|
|
56
|
+
// Sessionless library operations do not establish host presence or ownership.
|
|
57
|
+
if (identity.channel === 'unknown')
|
|
58
|
+
return;
|
|
54
59
|
const now = Date.now();
|
|
55
60
|
let existing;
|
|
56
61
|
try {
|
|
@@ -3,7 +3,7 @@ import { hostLedgerForEnv } from '../../registry.js';
|
|
|
3
3
|
import { canonicalPath } from '../../canonical-path.js';
|
|
4
4
|
import { openSquare } from '../../open-square.js';
|
|
5
5
|
import { closeOpenSquare } from '../../open-square.js';
|
|
6
|
-
import { Square } from '../../square-
|
|
6
|
+
import { Square } from '../../square-api.js';
|
|
7
7
|
import { connectNative } from '../../packages/agent-delivery/src/opencode-native.js';
|
|
8
8
|
import { observeOpenCodeContext, publishOpenCodeRoutes, receiveOpenCodePending } from './delivery.js';
|
|
9
9
|
/** Actual OpenCode 2.0.20 server plugin. Harness identity belongs to ctx, never process.env. */
|