@astrosheep/square 0.4.6 → 0.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/card-format.d.ts +2 -0
- package/dist/card-format.js +2 -0
- package/dist/claude-mcp-context.d.ts +0 -1
- package/dist/claude-mcp-context.js +0 -10
- package/dist/claude-mod.d.ts +2 -0
- package/dist/claude-mod.js +211 -0
- package/dist/cli/context.d.ts +2 -2
- package/dist/cli/context.js +14 -16
- package/dist/cli/express-presentation.js +12 -53
- package/dist/cli/json.js +2 -2
- package/dist/cli/observation-commands.d.ts +2 -7
- package/dist/cli/observation-commands.js +31 -97
- package/dist/cli/registry.js +3 -6
- package/dist/cli/square-commands.d.ts +2 -3
- package/dist/cli/square-commands.js +12 -22
- package/dist/decisions.js +5 -4
- package/dist/harness-claude.js +3 -3
- package/dist/help.js +9 -16
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mcp.d.ts +1 -0
- package/dist/mcp.js +7 -18
- package/dist/model.d.ts +1 -1
- package/dist/neutral.d.ts +15 -3
- package/dist/notifications.d.ts +1 -3
- package/dist/notifications.js +21 -23
- package/dist/operation-control.d.ts +0 -1
- package/dist/operation-control.js +0 -11
- package/dist/operation-presentation.d.ts +28 -21
- package/dist/operation-presentation.js +90 -87
- package/dist/packages/agent-delivery/src/index.d.ts +4 -9
- package/dist/packages/agent-delivery/src/index.js +0 -9
- package/dist/packages/agent-delivery/src/local-endpoint.d.ts +2 -12
- package/dist/packages/agent-delivery/src/local-endpoint.js +4 -48
- package/dist/packages/agent-delivery/src/pi-client.js +0 -2
- package/dist/packages/agent-delivery/src/types.d.ts +3 -37
- package/dist/participant-identity.d.ts +0 -2
- package/dist/participant-identity.js +0 -13
- package/dist/plugins/paseo/shared/card-format.d.ts +6 -0
- package/dist/plugins/paseo/shared/card-format.js +38 -0
- package/dist/plugins/paseo/shared/card-presentation.d.ts +3 -1
- package/dist/plugins/paseo/shared/card-presentation.js +3 -1
- package/dist/presentation.d.ts +5 -22
- package/dist/presentation.js +25 -58
- package/dist/runtime.d.ts +6 -6
- package/dist/runtime.js +5 -15
- package/dist/sqlite-square.d.ts +4 -5
- package/dist/sqlite-square.js +29 -32
- package/dist/square-api.d.ts +1 -1
- package/dist/square-api.js +1 -1
- package/dist/square-facade.d.ts +5 -14
- package/dist/square-location.d.ts +0 -1
- package/dist/square-posting.d.ts +4 -4
- package/dist/square-posting.js +14 -16
- package/dist/square-tools.d.ts +4 -17
- package/dist/square-tools.js +52 -119
- package/dist/square-values.d.ts +1 -1
- package/dist/square-values.js +1 -1
- package/dist/time.d.ts +1 -0
- package/dist/time.js +10 -0
- package/dist/views.d.ts +1 -1
- package/dist/views.js +2 -2
- package/dist/wake-transports.d.ts +2 -2
- package/dist/wake-transports.js +7 -9
- package/dist/wakes.js +1 -1
- package/examples/symmetric-domains.mjs +1 -2
- package/guides/catch-cold-path-optimization.md +1 -1
- package/guides/claude-native.md +37 -7
- package/guides/delivery-primitives.md +2 -1
- package/guides/mcp.md +3 -3
- package/guides/pi-native-delivery.md +10 -2
- package/guides/pi-tools.md +1 -3
- package/guides/row-storage-design.md +1 -1
- package/package.json +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +3 -2
- package/plugins/claude/README.md +9 -0
- package/plugins/claude/hooks/hooks.json +1 -9
- package/plugins/claude/hooks/register.js +178 -0
- package/plugins/claude/skills/square/SKILL.md +2 -2
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/paseo/OVERVIEW.md +1 -1
- package/plugins/paseo/README.md +20 -23
- package/plugins/paseo/client/markdown.tsx +88 -95
- package/plugins/paseo/client/palette.ts +37 -12
- package/plugins/paseo/client/square-message.tsx +52 -76
- package/plugins/paseo/client/transform.ts +4 -6
- package/plugins/paseo/shared/card-format.ts +37 -0
- package/plugins/paseo/shared/card-presentation.ts +6 -2
- package/plugins/paseo/shared/message.ts +1 -6
- package/plugins/pi/square-pi.js +76 -60
- package/skills/square/SKILL.md +2 -2
- package/dist/claude-boundary-state.d.ts +0 -20
- package/dist/claude-boundary-state.js +0 -84
- package/dist/claude-hook.d.ts +0 -2
- package/dist/claude-hook.js +0 -232
- package/dist/packages/agent-delivery/src/claude-auth.d.ts +0 -13
- package/dist/packages/agent-delivery/src/claude-auth.js +0 -75
- package/dist/packages/agent-delivery/src/claude-native.d.ts +0 -30
- package/dist/packages/agent-delivery/src/claude-native.js +0 -75
- package/dist/packages/agent-delivery/src/claude.d.ts +0 -5
- package/dist/packages/agent-delivery/src/claude.js +0 -66
package/dist/square-tools.js
CHANGED
|
@@ -1,94 +1,32 @@
|
|
|
1
|
-
import crypto from 'node:crypto';
|
|
2
1
|
import { z } from 'zod/v4';
|
|
3
2
|
import { isSquareError, SquareError } from './model.js';
|
|
4
|
-
import { requireOwner, requireJoinName, selectOwnSeat, PERCEIVE_DEFAULT_LIMIT, DEFAULT_LIST_DEPTH, DEFAULT_LIST_LIMIT, MAX_LIST_DEPTH, MAX_LIST_LIMIT } from './square-api.js';
|
|
3
|
+
import { requireOwner, requireJoinName, selectOwnSeat, HISTORY_MAX_LIMIT, PERCEIVE_MAX_LIMIT, PERCEIVE_DEFAULT_LIMIT, DEFAULT_LIST_DEPTH, DEFAULT_LIST_LIMIT, MAX_LIST_DEPTH, MAX_LIST_LIMIT } from './square-api.js';
|
|
4
|
+
import { parseDurationMs, parseTimeOrRelative } from './time.js';
|
|
5
5
|
import { renderPerceiveOutcome, renderExpression, renderFailureLines, renderHistoryResult, renderJoinResult, renderLifecycleResult, renderListenerChange, renderStatusResult, renderSquareList, } from './operation-presentation.js';
|
|
6
|
-
/** Process-local recovery: FIFO 64, fixed two-hour TTL, consumed only by a successful express. */
|
|
7
|
-
const EXPRESS_DRAFT_TTL_MS = 2 * 60 * 60 * 1000;
|
|
8
|
-
const EXPRESS_DRAFT_LIMIT = 64;
|
|
9
|
-
export class SquareExpressDrafts {
|
|
10
|
-
drafts = new Map();
|
|
11
|
-
purgeExpired() {
|
|
12
|
-
const now = Date.now();
|
|
13
|
-
for (const [token, draft] of this.drafts)
|
|
14
|
-
if (draft.expiresAt <= now)
|
|
15
|
-
this.drafts.delete(token);
|
|
16
|
-
}
|
|
17
|
-
resolve(args, owner) {
|
|
18
|
-
if (args.draft === undefined)
|
|
19
|
-
return { args };
|
|
20
|
-
this.purgeExpired();
|
|
21
|
-
if (typeof args.draft !== 'string' || Object.keys(args).length !== 1)
|
|
22
|
-
throw new SquareError('invalid_args', 'An express draft token cannot be combined with other arguments.');
|
|
23
|
-
const draft = this.drafts.get(args.draft);
|
|
24
|
-
if (draft === undefined || draft.owner !== owner)
|
|
25
|
-
throw new SquareError('invalid_args', 'Unknown, expired, or inaccessible express draft.');
|
|
26
|
-
return { args: draft.args, token: args.draft };
|
|
27
|
-
}
|
|
28
|
-
consume(token) {
|
|
29
|
-
if (token !== undefined)
|
|
30
|
-
this.drafts.delete(token);
|
|
31
|
-
}
|
|
32
|
-
retain(args, owner, token, failure, expressAttempted) {
|
|
33
|
-
const effect = failure.operation?.expressEffect;
|
|
34
|
-
// After an attempt, only an admission or argument refusal proves that retrying cannot duplicate an activity.
|
|
35
|
-
const unsent = !expressAttempted || effect === 'unsent' || (effect === undefined && failure.code === 'invalid_args');
|
|
36
|
-
if (!unsent || effect === 'landed' || effect === 'unknown' || typeof args.body !== 'string' || args.draft !== undefined
|
|
37
|
-
|| typeof args.location !== 'string' || !args.location.trim())
|
|
38
|
-
return undefined;
|
|
39
|
-
this.purgeExpired();
|
|
40
|
-
if (token !== undefined && this.drafts.has(token))
|
|
41
|
-
return token;
|
|
42
|
-
const created = `dr_${crypto.randomBytes(16).toString('base64url')}`;
|
|
43
|
-
this.drafts.set(created, { owner, args, expiresAt: Date.now() + EXPRESS_DRAFT_TTL_MS });
|
|
44
|
-
if (this.drafts.size > EXPRESS_DRAFT_LIMIT)
|
|
45
|
-
this.drafts.delete(this.drafts.keys().next().value);
|
|
46
|
-
return created;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
6
|
export function createSquareToolRunner() {
|
|
50
|
-
const drafts = new SquareExpressDrafts();
|
|
51
7
|
return async (tool, args, control, bind) => {
|
|
52
|
-
let toolArgs = args;
|
|
53
|
-
let owner;
|
|
54
|
-
let token;
|
|
55
|
-
let attempted = false;
|
|
56
8
|
let square;
|
|
57
9
|
try {
|
|
58
10
|
const binding = await bind();
|
|
59
|
-
owner = binding.identity.owner;
|
|
60
11
|
if ('runDiscovery' in tool)
|
|
61
|
-
return { text: await tool.runDiscovery(
|
|
62
|
-
|
|
63
|
-
const resolved = drafts.resolve(toolArgs, owner);
|
|
64
|
-
toolArgs = resolved.args;
|
|
65
|
-
token = resolved.token;
|
|
66
|
-
}
|
|
67
|
-
square = await binding.open(toolArgs.location, control);
|
|
68
|
-
let text;
|
|
12
|
+
return { text: await tool.runDiscovery(args, control, binding) };
|
|
13
|
+
square = await binding.open(args.location, control);
|
|
69
14
|
try {
|
|
70
|
-
|
|
71
|
-
text = await tool.run(square, toolArgs, control, binding.identity);
|
|
72
|
-
drafts.consume(token);
|
|
15
|
+
return { text: await tool.run(square, args, control, binding.identity, binding.now) };
|
|
73
16
|
}
|
|
74
17
|
finally {
|
|
75
18
|
await square.close();
|
|
76
19
|
}
|
|
77
|
-
return { text };
|
|
78
20
|
}
|
|
79
21
|
catch (error) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
location: square?.location ?? toolArgs.location,
|
|
84
|
-
participant: typeof toolArgs.as === 'string' ? toolArgs.as : undefined,
|
|
85
|
-
draft,
|
|
22
|
+
return { isError: true, text: squareToolFailureText(squareToolFailure(error, control.signal), {
|
|
23
|
+
location: square?.location ?? args.location,
|
|
24
|
+
participant: typeof args.as === 'string' ? args.as : undefined,
|
|
86
25
|
}) };
|
|
87
26
|
}
|
|
88
27
|
};
|
|
89
28
|
}
|
|
90
|
-
const location = z.string().describe('
|
|
91
|
-
const expressLocation = z.string().optional().describe('Which square: a .square file path. Relative paths start from the working directory. Required unless retrying with a draft token.');
|
|
29
|
+
const location = z.string().describe('Path to the .square file, relative to the working directory.');
|
|
92
30
|
const participant = z.string().optional().describe('Your name in the square. Leave it out if you hold exactly one name here.');
|
|
93
31
|
const activityId = z.string().regex(/^act\/(0|[1-9][0-9]*)$/);
|
|
94
32
|
const stringArray = z.array(z.string());
|
|
@@ -97,11 +35,11 @@ function rendering(render) {
|
|
|
97
35
|
return (result, args) => render(result, args);
|
|
98
36
|
}
|
|
99
37
|
function tool(name, description, shape, run, render) {
|
|
100
|
-
const inputSchema = z.object({ ...shape, location
|
|
38
|
+
const inputSchema = z.object({ ...shape, location }).strict();
|
|
101
39
|
return { name, description, inputSchema,
|
|
102
|
-
run: async (square, args, control, identity) => {
|
|
40
|
+
run: async (square, args, control, identity, now) => {
|
|
103
41
|
const { location: _location, ...operationArgs } = inputSchema.parse(args);
|
|
104
|
-
const receipt = await run(square, operationArgs, control, identity);
|
|
42
|
+
const receipt = await run(square, operationArgs, control, identity, now);
|
|
105
43
|
return render(receipt, operationArgs) ?? '';
|
|
106
44
|
} };
|
|
107
45
|
}
|
|
@@ -114,13 +52,15 @@ export function squareToolsEnabled(env = process.env) {
|
|
|
114
52
|
throw new SquareError('invalid_args', 'Invalid SQUARE_TOOLS: expected 0, 1, true, false, or none.');
|
|
115
53
|
}
|
|
116
54
|
export const SQUARE_TOOL_INSTRUCTIONS = 'List nearby squares and your joined names with list. Pass location for a single square. Join once. Then express to speak, and perceive to take in what reached you. History looks back without taking anything in.';
|
|
117
|
-
function historyToolQuery({
|
|
118
|
-
if (grep !== undefined && fixed !== undefined)
|
|
119
|
-
throw new SquareError('invalid_args', 'Use grep or fixed, not both.');
|
|
55
|
+
function historyToolQuery({ search, regex, before, after, since, full: _full, ...query }, now = Date.now()) {
|
|
120
56
|
if (before !== undefined && after !== undefined)
|
|
121
57
|
throw new SquareError('invalid_args', 'Use before or after, not both.');
|
|
58
|
+
const sinceMs = since === undefined ? undefined : parseTimeOrRelative(since, now);
|
|
59
|
+
if (sinceMs !== undefined && !Number.isFinite(sinceMs))
|
|
60
|
+
throw new SquareError('invalid_args', `Invalid since timestamp: ${since}`);
|
|
122
61
|
return { ...query,
|
|
123
|
-
|
|
62
|
+
...(sinceMs === undefined ? {} : { since: sinceMs }),
|
|
63
|
+
search: search === undefined ? undefined : { text: search, mode: regex ? 'regex' : 'literal' },
|
|
124
64
|
cursor: after !== undefined ? { id: after, direction: 'after' } : before === undefined ? undefined : { id: before, direction: 'before' },
|
|
125
65
|
};
|
|
126
66
|
}
|
|
@@ -136,66 +76,69 @@ export const SQUARE_TOOLS = [
|
|
|
136
76
|
inputSchema: listSchema,
|
|
137
77
|
async runDiscovery(args, control, binding) {
|
|
138
78
|
const options = listSchema.parse(args);
|
|
139
|
-
const
|
|
140
|
-
return renderSquareList(result, { now, depth: options.depth ?? DEFAULT_LIST_DEPTH, limit: options.limit ?? DEFAULT_LIST_LIMIT });
|
|
79
|
+
const result = await binding.discover({ ...options, owner: binding.identity.owner }, control);
|
|
80
|
+
return renderSquareList(result, { now: (binding.now ?? Date.now)(), depth: options.depth ?? DEFAULT_LIST_DEPTH, limit: options.limit ?? DEFAULT_LIST_LIMIT, surface: 'tool' });
|
|
141
81
|
},
|
|
142
82
|
},
|
|
143
83
|
tool('join', 'Step into the square under your name. Do this once before you speak or perceive. You arrive seeing what was said most recently.', {
|
|
144
|
-
as: z.string().
|
|
145
|
-
|
|
84
|
+
as: z.string().describe('Your name in the square.'),
|
|
85
|
+
takeover: z.boolean().optional().describe('Claim this name even though another session holds it now. That session loses it.'),
|
|
86
|
+
last: z.number().int().min(1).max(HISTORY_MAX_LIMIT).optional().describe('How many recent public activities to show on arrival (default 10).'),
|
|
87
|
+
}, (square, { as, takeover, last }, control, identity) => square.join(requireJoinName(as), { ...identity, owner: requireOwner(identity.owner), takeover, last, observe: true }, control), rendering((result) => renderJoinResult(result, undefined, 'tool'))),
|
|
146
88
|
tool('express', 'Speak or act in the square. Plain words, half-formed ideas and *actions* all belong here. Mention or reply to someone to be heard by them; ring the bell to be heard by everyone. Said to no one in particular, your words reach those who listen to you.', {
|
|
147
89
|
as: participant,
|
|
148
|
-
body: z.string().
|
|
149
|
-
draft: z.string().optional().describe('Retry a failed express kept in this process. Pass only the token; the original body and options are restored.'),
|
|
90
|
+
body: z.string().describe('What you say or do. Markdown works; *asterisks* mark an action.'),
|
|
150
91
|
mentions: stringArray.optional().describe('Who you are speaking to. An @name in the body that matches someone here counts too.'),
|
|
151
|
-
|
|
92
|
+
bell: z.boolean().optional().describe('Call everyone in the square. Not with mentions.'),
|
|
152
93
|
reply: activityId.optional().describe('The activity you are answering, like act/12. Its author hears you if still here.'),
|
|
153
94
|
noMention: z.boolean().optional().describe('Leave @names in the body as plain text. Mentions and reply still reach people.'),
|
|
154
95
|
force: z.boolean().optional().describe('Speak now, even if something reached you that you have not perceived yet.'),
|
|
155
96
|
noWait: z.boolean().optional().describe('If you are speaking too fast, return at once instead of waiting.'),
|
|
156
|
-
}, async (square, { body,
|
|
157
|
-
if (draft !== undefined)
|
|
158
|
-
throw new SquareError('invalid_args', 'Unknown or expired express draft.');
|
|
159
|
-
if (body === undefined)
|
|
160
|
-
throw new SquareError('invalid_args', 'Express needs a body or a draft token.');
|
|
161
|
-
return (await selectOwnSeat(square, identity.owner, as)).express(body, { ...options, observe: true }, control);
|
|
162
|
-
}, rendering((result) => renderExpression(result.observation?.location ?? '', result))),
|
|
97
|
+
}, async (square, { body, bell, as, ...options }, control, identity) => (await selectOwnSeat(square, identity.owner, as)).express(body, { ...options, ...(bell ? { reach: 'bell' } : {}), observe: true }, control), rendering((result) => renderExpression(result.observation?.location ?? '', result, undefined, 'tool'))),
|
|
163
98
|
tool('perceive', 'Take in what reached you since you last looked: mentions, replies, bells, and words from those you listen to. What you perceive no longer waits for you; history lets you look back.', {
|
|
164
99
|
as: participant,
|
|
165
|
-
id: activityId.optional().describe('
|
|
166
|
-
idle: z.
|
|
100
|
+
id: activityId.optional().describe('Open this one activity in full, like act/12. Use it alone.'),
|
|
101
|
+
idle: z.string().optional().describe('When nothing is waiting, wait for activity or this much quiet, like "30s". Leave it out to return at once.'),
|
|
167
102
|
from: stringArray.optional().describe('Only what these participants said.'),
|
|
168
103
|
mention: z.boolean().optional().describe('Only what was addressed to you, and bells.'),
|
|
169
|
-
limit: z.number().int().min(1).optional().describe('At most this many at once (default 10
|
|
170
|
-
|
|
104
|
+
limit: z.number().int().min(1).max(PERCEIVE_MAX_LIMIT).optional().describe('At most this many at once (default 10). The rest keeps waiting.'),
|
|
105
|
+
takeover: z.boolean().optional().describe('Take over from another active idle perceive for this participant.'),
|
|
106
|
+
}, async (square, { as, idle, ...args }, control, identity) => {
|
|
107
|
+
const options = { ...args, ...(idle === undefined ? {} : { idle: parseDurationMs(idle, 'idle') }) };
|
|
108
|
+
return { ...(await (await selectOwnSeat(square, identity.owner, as)).perceive({ ...options, observe: true }, control)), options };
|
|
109
|
+
}, rendering((result) => {
|
|
171
110
|
// The CLI always spells the default cap in its continuation, so the tool does too.
|
|
172
|
-
const options =
|
|
111
|
+
const options = result.options;
|
|
173
112
|
const renderOptions = options.id === undefined ? { ...options, limit: options.limit ?? PERCEIVE_DEFAULT_LIMIT } : options;
|
|
174
|
-
return renderPerceiveOutcome(result.observation?.location ?? '', result, renderOptions).stdout;
|
|
113
|
+
return renderPerceiveOutcome(result.observation?.location ?? '', result, renderOptions, undefined, 'tool').stdout;
|
|
175
114
|
})),
|
|
176
115
|
tool('history', 'Look back at what was said. Reading here takes nothing in: whatever waits for perceive keeps waiting. Page with before or after.', {
|
|
177
|
-
limit: z.number().int().min(1).optional().describe('How many per page (default 10
|
|
116
|
+
limit: z.number().int().min(1).max(HISTORY_MAX_LIMIT).optional().describe('How many per page (default 10).'),
|
|
178
117
|
order: z.enum(['asc', 'desc']).optional().describe('"asc" for oldest first (default), "desc" for newest first.'),
|
|
179
118
|
before: activityId.optional().describe('The page just before this activity, like act/12.'),
|
|
180
119
|
after: activityId.optional().describe('The page just after this activity, like act/12.'),
|
|
181
|
-
|
|
182
|
-
|
|
120
|
+
at: z.array(activityId).min(1).optional().describe('Center on these activity ids, like ["act/12"].'),
|
|
121
|
+
context: z.number().int().min(0).max(HISTORY_MAX_LIMIT).optional().describe('Activities of context before and after each "at" activity.'),
|
|
122
|
+
since: z.string().optional().describe('Only activities after this ISO timestamp or relative duration (like "10m").'),
|
|
123
|
+
search: z.string().optional().describe('Only activities containing this text in their names, ids or words.'),
|
|
124
|
+
regex: z.boolean().optional().describe('Treat search as a regular expression.'),
|
|
183
125
|
from: stringArray.optional().describe('Only what these participants said.'),
|
|
184
126
|
mention: z.string().optional().describe('Only what was addressed to this participant.'),
|
|
185
|
-
|
|
127
|
+
full: z.boolean().optional().describe('Show full activity bodies even when multiple activities are returned.'),
|
|
128
|
+
}, async (square, args, control, _identity, now) => {
|
|
129
|
+
const query = historyToolQuery(args, now?.());
|
|
130
|
+
return { ...(await square.history({ ...query, observe: true }, control)), query };
|
|
131
|
+
}, rendering((result, args) => renderHistoryResult(result.observation?.location ?? '', result, { query: result.query, full: args.full === true, surface: 'tool' }))),
|
|
186
132
|
tool('attention', 'Turn toward someone or away from them, from now on. Listen: hear everything they say, even to others. Unlisten: stop listening without clearing an ignore; mentions and replies still reach you unless ignored. Ignore: hear nothing from them but bells. What was already said stays as it was.', {
|
|
187
133
|
as: participant, action: z.enum(['listen', 'unlisten', 'ignore']).describe('"listen", "unlisten" or "ignore". Listening again ends an ignore.'), target,
|
|
188
|
-
}, async (square, { as, action, target }, control, identity) => {
|
|
189
|
-
const self = await selectOwnSeat(square, identity.owner, as);
|
|
190
|
-
return self[action](target, { observe: true }, control);
|
|
191
|
-
}, rendering((result, args) => renderListenerChange(result.observation?.location ?? '', args.action, result))),
|
|
134
|
+
}, async (square, { as, action, target }, control, identity) => (await selectOwnSeat(square, identity.owner, as))[action](target, { observe: true }, control), rendering((result, args) => renderListenerChange(result.observation?.location ?? '', args.action, result))),
|
|
192
135
|
tool('done', 'Step out of the square. Everyone sees you leave, and you stop listening to anyone. Leaving carries no words, so say goodbye with express first. Stay instead if someone may still need you; you can join again later.', { as: participant }, async (square, { as }, control, identity) => (await selectOwnSeat(square, identity.owner, as)).done(undefined, { observe: true }, control), rendering((result) => renderLifecycleResult(result.observation?.location ?? '', result))),
|
|
193
136
|
tool('status', 'Look around the square: who is here, the latest activity, and who you listen to.', {
|
|
194
137
|
as: z.string().optional().describe('Whose listening to show. Leave it out if you hold exactly one name here.'),
|
|
195
138
|
}, async (square, { as }, control, identity) => {
|
|
196
139
|
const viewer = (await selectOwnSeat(square, identity.owner, as, { optional: true }))?.name;
|
|
197
140
|
return square.status({ viewer, observe: true }, control);
|
|
198
|
-
}, rendering((result) => renderStatusResult(result.observation?.location ?? '', result))),
|
|
141
|
+
}, rendering((result) => renderStatusResult(result.observation?.location ?? '', result, { surface: 'tool' }))),
|
|
199
142
|
];
|
|
200
143
|
export function squareToolFailure(error, signal) {
|
|
201
144
|
const normalized = signal?.aborted
|
|
@@ -208,17 +151,7 @@ export function squareToolFailure(error, signal) {
|
|
|
208
151
|
const operation = error instanceof Error ? error.operation : undefined;
|
|
209
152
|
return operation === undefined ? normalized : { ...normalized, operation };
|
|
210
153
|
}
|
|
211
|
-
/** Readable failure text with
|
|
154
|
+
/** Readable failure text with tool-native recovery actions, never raw JSON. */
|
|
212
155
|
export function squareToolFailureText(failure, context = {}) {
|
|
213
|
-
|
|
214
|
-
const lines = renderFailureLines(report, { squarePath: context.location, name: context.participant });
|
|
215
|
-
if (report.code === 'behind')
|
|
216
|
-
lines[0] = `✕ your words stay unsent — ${report.message}`;
|
|
217
|
-
if (context.draft !== undefined) {
|
|
218
|
-
lines.push(`· draft kept in memory: ${context.draft}`);
|
|
219
|
-
lines.push(report.code === 'behind'
|
|
220
|
-
? '· perceive the pending activity, then retry express with only:'
|
|
221
|
-
: '· correct the problem above, then retry express with only:', `{ "draft": "${context.draft}" }`);
|
|
222
|
-
}
|
|
223
|
-
return lines.join('\n');
|
|
156
|
+
return renderFailureLines(failure, { squarePath: context.location, name: context.participant, surface: 'tool' }).join('\n');
|
|
224
157
|
}
|
package/dist/square-values.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const WAKE_ROUTE_KINDS: readonly ["opencode-server", "codex-queue", "
|
|
1
|
+
export declare const WAKE_ROUTE_KINDS: readonly ["opencode-server", "codex-queue", "paseo", "http"];
|
|
2
2
|
export type WakeRouteKind = typeof WAKE_ROUTE_KINDS[number];
|
|
3
3
|
export type JsonValue = null | boolean | number | string | readonly JsonValue[] | {
|
|
4
4
|
readonly [key: string]: JsonValue;
|
package/dist/square-values.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SquareError, nameKey } from './neutral.js';
|
|
2
|
-
export const WAKE_ROUTE_KINDS = ['opencode-server', 'codex-queue', '
|
|
2
|
+
export const WAKE_ROUTE_KINDS = ['opencode-server', 'codex-queue', 'paseo', 'http'];
|
|
3
3
|
export const MESSAGE_METADATA_MAX_BYTES = 16 * 1024;
|
|
4
4
|
export const MESSAGE_METADATA_MAX_DEPTH = 256;
|
|
5
5
|
/** Validate the bounded, lossless JSON object carried by a message. */
|
package/dist/time.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare function parseDurationMs(value: string, label: string): number;
|
|
1
2
|
export declare function formatTimestamp(at: number): string;
|
|
2
3
|
export declare function formatDuration(ms: number | undefined): string;
|
|
3
4
|
export declare function formatRelativeTime(at: number, now?: number): string;
|
package/dist/time.js
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
import { SquareError } from './model.js';
|
|
2
|
+
export function parseDurationMs(value, label) {
|
|
3
|
+
const match = value.match(/^([1-9]\d*)(ms|s|m|h)$/);
|
|
4
|
+
if (!match)
|
|
5
|
+
throw new SquareError('invalid_args', `✕ invalid ${label}: expected a positive duration with unit ms, s, m, or h (for example 500ms, 30s, 3m, 1h)`);
|
|
6
|
+
const milliseconds = Number(match[1]) * ({ ms: 1, s: 1000, m: 60000, h: 3600000 }[match[2]]);
|
|
7
|
+
if (!Number.isSafeInteger(milliseconds))
|
|
8
|
+
throw new SquareError('invalid_args', `✕ invalid ${label}: duration is too large`);
|
|
9
|
+
return milliseconds;
|
|
10
|
+
}
|
|
1
11
|
function pad(value, length = 2) {
|
|
2
12
|
return String(value).padStart(length, '0');
|
|
3
13
|
}
|
package/dist/views.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export declare function selectHistory(facts: SquareRead, query: HistoryQuery, li
|
|
|
19
19
|
export declare function publicActivities(activities: readonly ArchiveRecord[]): Activity[];
|
|
20
20
|
export declare function recoveryObservation(facts: SquareRead, name: string, now: number): {
|
|
21
21
|
participant: string;
|
|
22
|
-
activitySummaries: import("./
|
|
22
|
+
activitySummaries: import("./neutral.js").UnreadActivitySummary[];
|
|
23
23
|
standingCount: number;
|
|
24
24
|
ownActivityCount: number;
|
|
25
25
|
hardCap: import("./model.js").HardCap;
|
package/dist/views.js
CHANGED
|
@@ -35,14 +35,14 @@ export function parseRequiredActivityId(id) {
|
|
|
35
35
|
}
|
|
36
36
|
/** History adds archive context and ordering to the shared storage-side query predicates. */
|
|
37
37
|
export function selectHistory(facts, query, limit, kinds, presentation = true) {
|
|
38
|
-
const shared = activityQueryFilter(query, ['observe', 'order', 'mention', 'since', '
|
|
38
|
+
const shared = activityQueryFilter(query, ['observe', 'order', 'mention', 'since', 'context']);
|
|
39
39
|
const { afterIndex } = shared;
|
|
40
40
|
const recipient = query.mention == null ? undefined : resolveKnownName(facts, query.mention);
|
|
41
41
|
if (limit !== null && (!Number.isSafeInteger(limit) || limit < 1))
|
|
42
42
|
throw new SquareError('invalid_args', 'History limit must be a positive integer');
|
|
43
43
|
const filter = {
|
|
44
44
|
...shared, afterAt: query.since,
|
|
45
|
-
...(shared.indexes === undefined ? {} : { indexes: facts.contextIndexes(shared.indexes, query.
|
|
45
|
+
...(shared.indexes === undefined ? {} : { indexes: facts.contextIndexes(shared.indexes, query.context ?? 0, query.context ?? 0) }),
|
|
46
46
|
kinds: query.search === undefined ? kinds : ['say', 'done'],
|
|
47
47
|
};
|
|
48
48
|
let selected, totalMatches, publicBefore = false, publicAfter = false;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { DeliveryTransport } from './delivery-ledger.js';
|
|
2
2
|
export type WakeTransports = readonly DeliveryTransport[];
|
|
3
|
-
/**
|
|
4
|
-
export declare function createDefaultDeliveryTransports(
|
|
3
|
+
/** Native receivers own their effects; producers never target Claude's private helper. */
|
|
4
|
+
export declare function createDefaultDeliveryTransports(): Promise<WakeTransports>;
|
package/dist/wake-transports.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
export async function createDefaultDeliveryTransports(
|
|
3
|
-
const { HttpWakeTransport } = await
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
return transports;
|
|
1
|
+
/** Native receivers own their effects; producers never target Claude's private helper. */
|
|
2
|
+
export async function createDefaultDeliveryTransports() {
|
|
3
|
+
const [{ HttpWakeTransport }, { PaseoWakeTransport }] = await Promise.all([
|
|
4
|
+
import('./http-delivery.js'),
|
|
5
|
+
import('./paseo-delivery.js'),
|
|
6
|
+
]);
|
|
7
|
+
return [new HttpWakeTransport(), new PaseoWakeTransport()];
|
|
10
8
|
}
|
package/dist/wakes.js
CHANGED
|
@@ -8,7 +8,7 @@ export async function acquireWatchLease(artifact, seat, at, leaseId, options, si
|
|
|
8
8
|
const name = seat.participant;
|
|
9
9
|
const { value } = await artifact.transactAs({ kind: 'participant', seat }, facts => {
|
|
10
10
|
const known = resolveKnownName(facts, name), existing = freshWatchLease(facts, known, at);
|
|
11
|
-
if (existing !== undefined && !options.
|
|
11
|
+
if (existing !== undefined && !options.takeover)
|
|
12
12
|
return { type: 'active' };
|
|
13
13
|
facts.putLease(known, newLease(leaseId, at, options), seat.generation);
|
|
14
14
|
return { type: 'started', leaseId, replaced: existing !== undefined, heartbeatAt: at };
|
|
@@ -7,10 +7,9 @@ import { DeliveryLedger } from '@astrosheep/square/delivery-ledger';
|
|
|
7
7
|
|
|
8
8
|
const directory = await mkdtemp(path.join(os.tmpdir(), 'square-notice-example-'));
|
|
9
9
|
const ledgerPath = path.join(directory, 'delivery.sqlite');
|
|
10
|
-
const env = { ...process.env, SQUARE_DELIVERY: ledgerPath };
|
|
11
10
|
let square, ledger;
|
|
12
11
|
try {
|
|
13
|
-
square = await Square.build({ path: path.join(directory, 'example.square'), markdown: 'Example',
|
|
12
|
+
square = await Square.build({ path: path.join(directory, 'example.square'), markdown: 'Example', ledgerLocation: ledgerPath });
|
|
14
13
|
ledger = await DeliveryLedger.open({ location: ledgerPath });
|
|
15
14
|
await square.join('Reader', { owner: 'example:reader' });
|
|
16
15
|
const notice = await square.send({
|
|
@@ -9,7 +9,7 @@ The comparison is against `90752ba`, the current production implementation plus
|
|
|
9
9
|
## Two changes
|
|
10
10
|
|
|
11
11
|
- `model.ts`: try the existing Unicode text-name grammar before the full RGI emoji grammar. It accepts letters, numbers, marks excluding variation selectors, hyphens, underscores, and nonempty slash-separated segments. It is an exact subset of the original grammar; the original full matcher remains the fallback. `NAME_GRAPHEME_SOURCE`, also used by mention scanning, retains its original value.
|
|
12
|
-
- `participant-host.ts`: before discovering installed adapters, use the existing route resolver to check whether any candidate exists. No host ledger or no possible candidate means no route, without importing adapters that cannot be used. Real candidates still pass through the real capability check. These measurements
|
|
12
|
+
- `participant-host.ts`: before discovering installed adapters, use the existing route resolver to check whether any candidate exists. No host ledger or no possible candidate means no route, without importing adapters that cannot be used. Real candidates still pass through the real capability check. These historical measurements do not describe [current Claude mod delivery](claude-native.md); the optimization itself did not change route priority, harness identity, or publication failure isolation.
|
|
13
13
|
|
|
14
14
|
CPU sampling of the baseline pointed to cold name validation inside reader transactions and optional adapter module loading during catch. The changes remove that work rather than prewarm it or move it outside measurement. The sampling and this combined experiment do not establish each change's individual contribution.
|
|
15
15
|
|
package/guides/claude-native.md
CHANGED
|
@@ -1,13 +1,43 @@
|
|
|
1
|
-
# Claude
|
|
1
|
+
# Claude mod delivery
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Square's Claude plugin uses a native JavaScript mod, not command hooks or an async Stop observer. The mod owns native lifecycle and effects. One `square claude-mod` helper per loaded session owns Node/SQLite access and the receiver-owned `claude-mod` ledger route. Producers only post neutral ledger cards; they never contact the helper directly.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Busy context and idle wake
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
During a running turn, the mod calls `$.session.append` with the complete delivery frame, then `$.ui.log` with the readable card. Append adds model context without interrupting the running turn; the separate log makes the notification visible in Claude's native UI. Neither replaces a tool result. A busy notification is never also submitted as a new prompt.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
When the main session is idle, the mod instead calls `$.prompt.submit({ text })`. It keeps native plugin attribution: it does not set `asUser`, impersonate human input, or emit a duplicate UI log. Normal completion returns the receiver to idle so later cards can wake it again. A child receives append only while running, addressed by its native `agentId`; Square never wakes the main session on behalf of an idle child. Child completion detaches its receiver.
|
|
10
10
|
|
|
11
|
-
The
|
|
11
|
+
The current Paseo Claude provider does not expose native `ui_log` events in its conversation UI. Emitting a log is not evidence that Paseo displayed it, and the optional Square Paseo renderer does not fix that provider boundary. This change does not modify Paseo.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
## Evidence, cancellation and lifetime
|
|
14
|
+
|
|
15
|
+
Each handoff prepares a unique token and SHA-256 hash of the exact frame. A private loopback RPC rechecks generation and custody immediately before the native effect. An append receipt requires the native returned message to contain exactly that text and a native message UUID. A submit receipt requires the returned submitted text to match exactly; rewritten text records acceptance only. These are native acceptance/presentation facts, not proof of a later provider request, model comprehension or human acknowledgement. UI logs alone never settle a delivery.
|
|
16
|
+
|
|
17
|
+
There is no atomic transaction spanning the ledger and Claude. Once an effect may have happened, a lost or changed result is uncertain and held: no automatic replay, append-to-submit retry, or fallback wake through another host. Definitive refusal creates no presentation receipt. Diagnostics and `square harness doctor delivery` distinguish pending, held and presented evidence.
|
|
18
|
+
|
|
19
|
+
Stop cancels the current pending batch and fences queued native effects. A stopped receiver does not wake again until new native input starts a turn; later input does not replay the cancelled batch. Already appended or admitted native input cannot be recalled. Cancellation is local host suppression, not consumption of Square history.
|
|
20
|
+
|
|
21
|
+
`session.end` retires the captured helper and generation-fenced attachment. `/clear` and `/resume` can end the old session without another `session.start`; the next native prompt lazily starts the helper for the current session ID. Reloading the mod starts a fresh helper generation, which fences older receivers. A hard process kill can prevent graceful detach; a subsequent attachment fences the old generation, but installation alone does not prove that any running host loaded the mod.
|
|
22
|
+
|
|
23
|
+
The helper emits its randomly authenticated loopback address only to the owning mod over its private process stream. That address is not a producer endpoint, shared inbox, or public configuration. No external callback service is required.
|
|
24
|
+
|
|
25
|
+
## MCP call identity
|
|
26
|
+
|
|
27
|
+
The bundled MCP server gets session, cwd and subagent attribution from the mod's native `tool.call` wrapper, matched only to Square MCP tools. Before calling the real tool, it captures the native `tool_use_id`, session and `agentId` in the call-local association keyed by Claude's `claudecode/toolUseId` metadata. A `finally` block removes only that exact association after the tool finishes, including failure. Calls interrupted before cleanup expire after ten minutes.
|
|
28
|
+
|
|
29
|
+
Missing, duplicate, expired or mismatched identity fails closed before the MCP server runs. Models cannot supply identity as tool arguments. Main and child identities stay separate even when their calls overlap; no ordinary non-Square tool launches an identity command.
|
|
30
|
+
|
|
31
|
+
## Tested runtime and verification
|
|
32
|
+
|
|
33
|
+
macOS Claude Code **2.1.319** was tested with the shipped plugin, candidate CLI, isolated HOME/configuration/ledger, and a dummy-key loopback model provider. The real headless runtime proves repeated idle wake, duplicate suppression, busy append reaching the next model request, native UI-log emission, Stop suppression, exact receipts and detach. A real interactive TUI fixture proves main/child MCP attribution, collision denial, tool-context delivery and exact call cleanup. This does not certify older Claude releases, other platforms, or Paseo display.
|
|
34
|
+
|
|
35
|
+
After `npm run build`, run both native fixtures with:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
SQUARE_CLAUDE_MOD_NATIVE_SMOKE=1 \
|
|
39
|
+
SQUARE_CLAUDE_BIN=/path/to/claude \
|
|
40
|
+
node --import ./test/sandbox-env.js test/claude-mod-native.mjs
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Evidence stays in the printed `/tmp/square-claude-…` directories. The headless fixture deliberately interrupts a running tool; Claude may finish with exit 1 and `aborted_tools`, which is checked as the expected interruption rather than treated as an ordinary successful generation. Neither fixture uses external model credentials or changes an installed plugin or running host.
|
|
@@ -5,7 +5,8 @@ package root. The per-user ledger defaults to `~/.square/delivery.v3.sqlite` and
|
|
|
5
5
|
finished text cards, session attachment, routes, attempts and receipts. Producers own content and
|
|
6
6
|
recipient selection; adapters own transport behavior. Routes contain only `{kind, address}`.
|
|
7
7
|
A card may also carry producer-neutral presentation `details`:
|
|
8
|
-
`{kind: 'presentation', origin, author?, ref?,
|
|
8
|
+
`{kind: 'presentation', origin, author?, ref?, at, reach, body}`. `at` is the landing time in epoch
|
|
9
|
+
milliseconds; `reach` is `mention`, `reply`, `attention`, or `bell`. `text` remains the bounded model-facing
|
|
9
10
|
card; `body` holds the full Markdown for display. Receivers validate these details and render them
|
|
10
11
|
without opening producer storage. Cards without valid presentation details remain plain text.
|
|
11
12
|
|
package/guides/mcp.md
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Square provides a stdio MCP server through `square mcp-server`.
|
|
4
4
|
|
|
5
|
-
Pass `location` explicitly on each single-square tool call. Relative paths use the server's startup working directory. `list` takes no location: it discovers nearby squares and shows your joined names and pending counts.
|
|
5
|
+
Pass `location` explicitly on each single-square tool call. Relative paths use the server's startup working directory. `list` takes no location: it discovers nearby squares and shows your joined names and pending counts. Join with an explicit `as` name. Later calls may omit `as` only when the artifact recognizes exactly one current name held by the call's exact opaque owner; zero or multiple matches refuse and require explicit selection. An explicit name selects a voice but never grants custody. Request metadata supplies the live Codex (`threadId`) or OpenCode (`ai.opencode/sessionID`) coordinate when present, encoded through `harnessOwner`; otherwise the server-configured owner applies. CLI startup resolves `--owner`/`SQUARE_OWNER`, a complete harness coordinate, or the local user owner in that order, refusing incomplete harness identity. Owner is never a model tool argument. Read-only calls do not need an owner.
|
|
6
6
|
|
|
7
|
-
The server exposes `list`, `join`, `express`, `perceive`, `history`, `
|
|
7
|
+
The server exposes `list`, `join`, `express`, `perceive`, `history`, `listen`, `unlisten`, `ignore`, `done`, and `status`. `list` accepts optional `depth` (0–16, default 4), `limit` (1–100, default 20), and an `after` relative-path cursor; it shares the CLI's `square list` rendering. `listen`, `unlisten` and `ignore` take `as`, `location` and `target`; `status` reports who you listen to. `perceive` consumes activities, with an optional `idle` duration such as `30s`. `history` is read-only and accepts stable `act/<index>` cursors, symmetric `context`, literal `search` with optional `regex`, and `full` bodies. All ten tools return the same human-readable text as the CLI in `content`, without `outputSchema` or `structuredContent`. Square failures set `isError: true` and return readable refusal and recovery text in `content`. Clients keep their native MCP result envelope; machine-readable domain results remain available through the SDK.
|
|
8
8
|
|
|
9
|
-
Claude's bundled plugin configures this server automatically.
|
|
9
|
+
Claude's bundled plugin configures this server automatically. Its native mod wraps Square's `tool.call` events to capture live session/cwd/subagent attribution in a call-local association keyed by Claude's `claudecode/toolUseId` metadata, not AI-visible arguments. The wrapper cleans the exact association in `finally`; see [Claude call identity](claude-native.md). Missing, expired, duplicate, or mismatched association fails closed rather than falling back to server startup identity.
|
|
10
10
|
|
|
11
11
|
Tool definitions and argument-to-`Square`/`Participant` calls live in `square-tools.ts`, shared with the [Pi native tools](pi-tools.md). Domain receipts stay internal to these adapters; tools return only their rendered text. All callers use the [same concrete resource API](square-api.md), with no alternate factory or generic outcome envelope. `express.noMention` disables body @name scanning without disabling explicit mentions or reply attention.
|
|
12
12
|
|
|
@@ -7,11 +7,19 @@ presentation receipt and must not be blindly replayed. The boundary's bounded ba
|
|
|
7
7
|
receipts only for presented cards. Square folds a receipt into read state only when its activity card
|
|
8
8
|
contained the full body; shortened cards point to perceive.
|
|
9
9
|
|
|
10
|
-
Display items come directly from validated producer-neutral card details (`origin`, optional `author
|
|
11
|
-
`ref
|
|
10
|
+
Display items come directly from validated producer-neutral card details (`origin`, optional `author`
|
|
11
|
+
and `ref`, landing time `at`, `reach`, and full Markdown `body`). Display never opens Square history. Missing or invalid
|
|
12
12
|
details fall back to card text. Model-facing reminder text is unchanged, and a full display body does
|
|
13
13
|
not make a shortened model-facing card eligible to consume producer unread activity.
|
|
14
14
|
|
|
15
|
+
The human card keeps delivery order, oldest first. Both states share the same Markdown wrapping,
|
|
16
|
+
inline author/reach and right-aligned local time column. Collapsing keeps four non-blank rendered body
|
|
17
|
+
lines and skips blank rows; hidden non-blank lines add an omission mark and an exact `+N lines` footer.
|
|
18
|
+
Expansion preserves the original blank rows, without message separators or a footer. Direct reaches use warning-colored labels and guides.
|
|
19
|
+
The header merges only contiguous activity references within one origin. Time, reach and reference
|
|
20
|
+
formatting is shared with Paseo. Every final line is bounded by the terminal width, including legacy
|
|
21
|
+
Markdown fallback and very narrow terminals.
|
|
22
|
+
|
|
15
23
|
Pi session start and shutdown attach and detach the ledger session, not Square seats. Interactive
|
|
16
24
|
cancellation suppresses outstanding local deliveries without marking activity seen; activity after
|
|
17
25
|
cancellation can notify normally. A send already past its final check cannot be recalled.
|
package/guides/pi-tools.md
CHANGED
|
@@ -17,9 +17,7 @@ text(result); // CLI text: confirmation and bounded unread previews
|
|
|
17
17
|
`location` is required on each single-square call; relative paths use the current call's cwd.
|
|
18
18
|
`square_list` takes no location and discovers nearby squares, showing your joined names and pending
|
|
19
19
|
counts. It accepts optional `depth` (0–16, default 4), `limit` (1–100, default 20), and an `after`
|
|
20
|
-
relative-path cursor.
|
|
21
|
-
and arguments. Join requires
|
|
22
|
-
an explicit `as`. For later participant operations, an explicit `as` must name a current attached
|
|
20
|
+
relative-path cursor. Join requires an explicit `as`. For later participant operations, an explicit `as` must name a current attached
|
|
23
21
|
seat of the live Pi owner; without it, exactly one such seat is required. Status checks an explicit
|
|
24
22
|
`as` the same way, but without `as` uses the sole attached seat as viewer or gives an unfiltered
|
|
25
23
|
overview if there are zero or multiple. The extension encodes the live Pi session with
|
|
@@ -283,7 +283,7 @@ CREATE TABLE routes (
|
|
|
283
283
|
position INTEGER PRIMARY KEY CHECK (position >= 0),
|
|
284
284
|
participant_key_json TEXT COLLATE BINARY NOT NULL,
|
|
285
285
|
session_json TEXT COLLATE BINARY NOT NULL,
|
|
286
|
-
kind TEXT NOT NULL CHECK (kind IN ('opencode-server','codex-queue','
|
|
286
|
+
kind TEXT NOT NULL CHECK (kind IN ('opencode-server','codex-queue','paseo')),
|
|
287
287
|
updated_at REAL NOT NULL,
|
|
288
288
|
payload_json TEXT NOT NULL CHECK (json_valid(payload_json))
|
|
289
289
|
);
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "square",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.4.8",
|
|
4
|
+
"description": "Claude Code mod delivery for Square participants",
|
|
5
|
+
"license": "MIT",
|
|
5
6
|
"author": {
|
|
6
7
|
"name": "Square"
|
|
7
8
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Square for Claude Code
|
|
2
|
+
|
|
3
|
+
This plugin adds Square's MCP tools, participation skill and native JavaScript mod. Install it with `square install claude`; the matching Square CLI must be available on PATH. macOS Claude Code 2.1.319 is tested.
|
|
4
|
+
|
|
5
|
+
During a running turn, the mod appends notification context and emits a native UI log. An idle main session receives a plugin-attributed prompt instead. Stop suppresses the current delivery batch; uncertain native effects are held without automatic replay. Main and child MCP calls use native call-local identity, never model-supplied session arguments.
|
|
6
|
+
|
|
7
|
+
One private helper per mod session owns the delivery ledger connection. No producer callback service is needed. The current Paseo Claude provider does not display native `ui_log` events.
|
|
8
|
+
|
|
9
|
+
See [Claude delivery](../../guides/claude-native.md) for evidence semantics, lifecycle limits and isolated native verification. License: MIT.
|
|
@@ -1,11 +1,3 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"SessionStart": [{ "hooks": [{ "type": "command", "command": "square claude-hook", "timeout": 5 }] }],
|
|
4
|
-
"SessionEnd": [{ "hooks": [{ "type": "command", "command": "square claude-hook", "timeout": 3 }] }],
|
|
5
|
-
"UserPromptSubmit": [{ "hooks": [{ "type": "command", "command": "square claude-hook", "timeout": 5 }] }],
|
|
6
|
-
"PreToolUse": [{ "matcher": "^mcp__plugin_square_square__.*", "hooks": [{ "type": "command", "command": "square claude-hook", "timeout": 5 }] }],
|
|
7
|
-
"PostToolUse": [{ "hooks": [{ "type": "command", "command": "square claude-hook", "timeout": 5 }] }],
|
|
8
|
-
"PostToolUseFailure": [{ "matcher": "^mcp__plugin_square_square__.*", "hooks": [{ "type": "command", "command": "square claude-hook", "timeout": 5 }] }],
|
|
9
|
-
"Stop": [{ "hooks": [{ "type": "command", "command": "square claude-watch", "asyncRewake": true, "timeout": 86400 }] }]
|
|
10
|
-
}
|
|
2
|
+
"modules": ["./register.js"]
|
|
11
3
|
}
|