@astrosheep/square 0.3.79 → 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/cli/express-presentation.js +12 -18
- package/dist/decisions.d.ts +1 -8
- package/dist/decisions.js +2 -10
- package/dist/help.js +2 -2
- package/dist/model.d.ts +1 -3
- package/dist/square-actions.js +2 -3
- package/dist/square-facade.d.ts +0 -1
- package/guides/application-facade-delivery-evidence.md +1 -1
- package/package.json +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/skills/square/SKILL.md +1 -1
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/skills/square/SKILL.md +1 -1
|
@@ -81,7 +81,7 @@ export async function executeExpression(open, squarePath, requestedName, rawInpu
|
|
|
81
81
|
name = participant.name;
|
|
82
82
|
attempted = true;
|
|
83
83
|
const outcome = await participant.express(rawInput.replace(/\r\n/g, '\n').trim(), {
|
|
84
|
-
...opts, reply: opts.reply,
|
|
84
|
+
...opts, reply: opts.reply, observe: true,
|
|
85
85
|
}, {
|
|
86
86
|
signal: controller.signal,
|
|
87
87
|
onProgress(progress) {
|
|
@@ -150,15 +150,13 @@ export async function executeExpression(open, squarePath, requestedName, rawInpu
|
|
|
150
150
|
}
|
|
151
151
|
const facts = isSquareError(error) ? error.facts : undefined;
|
|
152
152
|
const reason = facts?.reason;
|
|
153
|
-
const message = reason === '
|
|
154
|
-
? '✕
|
|
155
|
-
: reason === '
|
|
156
|
-
?
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
: 'Invalid --reply: expected an activity id like act/12'
|
|
161
|
-
: refusalText(error);
|
|
153
|
+
const message = reason === 'bell_mention_conflict'
|
|
154
|
+
? '✕ a bell cannot be combined with a mention'
|
|
155
|
+
: reason === 'invalid_reply'
|
|
156
|
+
? facts?.activityExists === false
|
|
157
|
+
? `Invalid --reply: ${facts.activityId ?? opts.reply ?? 'that activity'} is not in this square's history`
|
|
158
|
+
: 'Invalid --reply: expected an activity id like act/12'
|
|
159
|
+
: refusalText(error);
|
|
162
160
|
const lines = [message, `· draft kept: ${draftPath}`];
|
|
163
161
|
const repair = reason === 'bell_mention_conflict' || (reason === 'not_standing' && detail?.subject === 'recipient');
|
|
164
162
|
if (isSquareError(error)) {
|
|
@@ -170,7 +168,7 @@ export async function executeExpression(open, squarePath, requestedName, rawInpu
|
|
|
170
168
|
if (repair) {
|
|
171
169
|
lines.push(participantsRecoveryCommand(squarePath), reason === 'bell_mention_conflict'
|
|
172
170
|
? ' · ring the bell without explicit mentions:'
|
|
173
|
-
: ' ·
|
|
171
|
+
: ' · retry without explicit recipients:', `${activityRetryCommand(squarePath, name, { ...opts, mentions: [] })} < ${quoteShell(draftPath)}`);
|
|
174
172
|
}
|
|
175
173
|
else if (error.code === 'invalid_args' || error.code === 'invalid_name') {
|
|
176
174
|
if (opts.mentions?.length)
|
|
@@ -179,13 +177,9 @@ export async function executeExpression(open, squarePath, requestedName, rawInpu
|
|
|
179
177
|
lines.push(`${commandPrefix(squarePath)} history --limit 10`);
|
|
180
178
|
}
|
|
181
179
|
}
|
|
182
|
-
if (reason === '
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
else if (reason === 'invalid_reply') {
|
|
186
|
-
const needsBareReach = facts?.hasReachWithoutReply === false;
|
|
187
|
-
const withoutReply = { ...opts, reply: undefined, ...(needsBareReach ? { noMention: true } : {}) };
|
|
188
|
-
lines.push(`${commandPrefix(squarePath)} history --limit 10`, ' · choose a valid --reply from history, or send without a reply using this repair:', ...(needsBareReach ? [' · this repair is bare; replace --no-mention with --mention <name> to address someone'] : []), `${activityRetryCommand(squarePath, name, withoutReply)} < ${quoteShell(draftPath)}`);
|
|
180
|
+
if (reason === 'invalid_reply') {
|
|
181
|
+
const withoutReply = { ...opts, reply: undefined };
|
|
182
|
+
lines.push(`${commandPrefix(squarePath)} history --limit 10`, ' · choose a valid --reply from history, or send without a reply using this repair:', `${activityRetryCommand(squarePath, name, withoutReply)} < ${quoteShell(draftPath)}`);
|
|
189
183
|
}
|
|
190
184
|
else if (!repair) {
|
|
191
185
|
lines.push(' · correct the problem above before retrying; keep your intended recipients and reply:', `${retryCommand} < ${quoteShell(draftPath)}`);
|
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 {
|
|
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 {
|
|
@@ -42,12 +42,6 @@ export type ActDecision = ({
|
|
|
42
42
|
};
|
|
43
43
|
/** Count the directed suffix, but hydrate only its last three previews per spelling. */
|
|
44
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
|
-
};
|
|
51
45
|
export declare function decideAct(facts: SquareRead, input: {
|
|
52
46
|
name: string;
|
|
53
47
|
body: string;
|
|
@@ -57,7 +51,6 @@ export declare function decideAct(facts: SquareRead, input: {
|
|
|
57
51
|
reach?: Reach;
|
|
58
52
|
reply?: number;
|
|
59
53
|
noMention?: boolean;
|
|
60
|
-
requireExplicitReach?: boolean;
|
|
61
54
|
}): ActDecision;
|
|
62
55
|
export declare function validateDoneBody(body: unknown): void;
|
|
63
56
|
export declare function coreDone(facts: SquareRead, name: string, body: string, now: number): TimedAct<'done'>;
|
package/dist/decisions.js
CHANGED
|
@@ -84,11 +84,6 @@ 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
|
-
}
|
|
92
87
|
export function decideAct(facts, input) {
|
|
93
88
|
const { now, force, body, reach, reply } = input;
|
|
94
89
|
const current = resolveStanding(facts, input.name, true);
|
|
@@ -114,7 +109,7 @@ export function decideAct(facts, input) {
|
|
|
114
109
|
throw activityRefusal('invalid_args', `${participantIdentity(requested)} is not standing in this square`, { reason: 'not_standing', targets: [requested] });
|
|
115
110
|
addMention(resolved);
|
|
116
111
|
}
|
|
117
|
-
const
|
|
112
|
+
const bodyMentions = reach === 'bell' || input.noMention === true ? [] : scanMentionCandidates(body);
|
|
118
113
|
const unmatchedMentions = [];
|
|
119
114
|
for (const token of bodyMentions) {
|
|
120
115
|
const resolved = resolveMention(token, joinedNames);
|
|
@@ -123,13 +118,10 @@ export function decideAct(facts, input) {
|
|
|
123
118
|
else
|
|
124
119
|
unmatchedMentions.push(token);
|
|
125
120
|
}
|
|
126
|
-
if (input.requireExplicitReach && !hasReachWithoutReply && reply === undefined) {
|
|
127
|
-
throw activityRefusal('invalid_args', 'Express needs a recipient, reply, bell, or explicit bare reach', { reason: 'explicit_reach_required' });
|
|
128
|
-
}
|
|
129
121
|
const header = facts.header();
|
|
130
122
|
if (reply !== undefined && (!Number.isSafeInteger(reply) || reply < 0 || reply >= header.nextActIndex)) {
|
|
131
123
|
const label = Number.isSafeInteger(reply) && reply >= 0 ? formatActivityId(reply) : String(reply);
|
|
132
|
-
throw activityRefusal('invalid_args', `Unknown reply activity: ${label}`, { reason: 'invalid_reply', activityId: label, activityExists: false
|
|
124
|
+
throw activityRefusal('invalid_args', `Unknown reply activity: ${label}`, { reason: 'invalid_reply', activityId: label, activityExists: false });
|
|
133
125
|
}
|
|
134
126
|
const replyAuthor = reply === undefined ? undefined : facts.activity(reply)?.actor;
|
|
135
127
|
const replyAuthorNotHere = replyAuthor === undefined || sameName(replyAuthor, name) || facts.participant(replyAuthor)?.joined ? undefined : replyAuthor;
|
package/dist/help.js
CHANGED
|
@@ -26,8 +26,8 @@ const COMMANDS = [
|
|
|
26
26
|
" --bell Call everyone; body @names stay literal. Cannot combine with --mention.",
|
|
27
27
|
' --no-mention Keep body @names literal; explicit --mention and --reply still apply.',
|
|
28
28
|
' --reply <activity-id> Also reach the author of an earlier activity (for example act/12).',
|
|
29
|
-
'
|
|
30
|
-
'
|
|
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
31
|
'',
|
|
32
32
|
'Options:',
|
|
33
33
|
' -f, --force Express without first catching unread activity or satisfying attention etiquette.',
|
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. */
|
|
@@ -30,8 +30,6 @@ export interface SquareErrorFacts {
|
|
|
30
30
|
targets?: readonly string[];
|
|
31
31
|
/** Whether the artifact holds that activity at all; an unknown id needs an ordinary page. */
|
|
32
32
|
activityExists?: boolean;
|
|
33
|
-
/** Express has a recipient, bell, body candidate, or explicit bare reach without its reply. */
|
|
34
|
-
hasReachWithoutReply?: boolean;
|
|
35
33
|
pending?: number;
|
|
36
34
|
holder?: string;
|
|
37
35
|
holdReason?: string;
|
package/dist/square-actions.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { coreDone, coreHold, coreIgnore, coreListen, coreListening, coreResume, decideAct, decideImplicitJoin, decideJoin,
|
|
1
|
+
import { coreDone, coreHold, coreIgnore, coreListen, coreListening, coreResume, decideAct, decideImplicitJoin, decideJoin, resolveKnownName, validateDoneBody } from './decisions.js';
|
|
2
2
|
import { isSquareError, nameKey, SquareError, validateName } from './model.js';
|
|
3
3
|
import { participantIdentity } from './participant-identity.js';
|
|
4
4
|
import { deliverPending } from './delivery-operations.js';
|
|
@@ -191,7 +191,6 @@ export async function express(square, name, body, options = {}, control) {
|
|
|
191
191
|
if (isSquareError(error))
|
|
192
192
|
error.facts = {
|
|
193
193
|
...error.facts, reason: 'invalid_reply', activityId: options.reply,
|
|
194
|
-
hasReachWithoutReply: expressionReach({ ...options, body }).hasReachWithoutReply,
|
|
195
194
|
};
|
|
196
195
|
throw error;
|
|
197
196
|
}
|
|
@@ -199,7 +198,7 @@ export async function express(square, name, body, options = {}, control) {
|
|
|
199
198
|
const committed = (await square.artifact.transact((facts) => {
|
|
200
199
|
// Selected-row decoding is storage work, not evidence that admission safely refused.
|
|
201
200
|
const proposed = decideAct(facts, { name, body, force: options.force ?? false, now, mentions: options.mentions,
|
|
202
|
-
...(options.reach === undefined ? {} : { reach: options.reach }), ...(options.noMention === undefined ? {} : { noMention: options.noMention }), ...(
|
|
201
|
+
...(options.reach === undefined ? {} : { reach: options.reach }), ...(options.noMention === undefined ? {} : { noMention: options.noMention }), ...(reply === undefined ? {} : { reply }) });
|
|
203
202
|
const holder = proposed.type === 'held' ? facts.hold().actor : undefined;
|
|
204
203
|
const decision = rejectUnsentActivity(() => {
|
|
205
204
|
const decision = proposed;
|
package/dist/square-facade.d.ts
CHANGED
|
@@ -29,7 +29,6 @@ export interface ExpressOptions extends ObservationOptions {
|
|
|
29
29
|
readonly reply?: ActivityId;
|
|
30
30
|
readonly noMention?: boolean;
|
|
31
31
|
readonly noWait?: boolean;
|
|
32
|
-
readonly requireExplicitReach?: boolean;
|
|
33
32
|
}
|
|
34
33
|
export interface ActivityResult {
|
|
35
34
|
readonly activity: Activity;
|
|
@@ -179,7 +179,7 @@ These are deliverer closures, not a reviewer verdict. Rows distinguish behavior
|
|
|
179
179
|
| SDK projection roundtrip / leaked observations | SDK express uses `prepareExpression` + `landExpression`, strips observation/waited and retains delivery; SDK catch uses `catchOwned` and returns only established CatchResult. SDK test asserts established fields and avoids application body projections. SDK Square history stays unbounded, Participant recent-ten; parity history fixture proves both, and projection test preserves detached delivered predicate. |
|
|
180
180
|
| Duplicate contracts, aliases and dead mixed views | Private status/unread producers import/re-export facade contracts; catch observation extends shared expression facts and enriched projection reuses it; required express participant replaces name alias. Retired helper search is empty. Recovery selects only gates/bounded unread summaries, not discarded feed/ordinals/perceptions. |
|
|
181
181
|
| Packed owner orchestration hides effects/count integrity | History, lifecycle branches, maintenance/discovery/recovery, catch conflict and SDK recognize are expanded into readable steps. Remaining one-line SDK delegates are supported simple resource calls, not packed async policy. Inspect implementation diff; all new source is included in count. |
|
|
182
|
-
| Invalid reply /
|
|
182
|
+
| Invalid reply / done repair regressions | Typed `invalid_reply` covers malformed and valid-but-absent IDs; typed reach/done reasons stay domain facts, CLI owns glyphs/flags. Express-recovery tests execute printed repaired commands, preserving raw drafts, body candidates and other recipients. Unknown/landed effects offer reconciliation, never replay. |
|
|
183
183
|
| Failing application clock prevents draft creation | `executeExpression` uses `saveActivityDraft`'s transport-local `Date.now` default, never the failed owner clock. Focused `express-recovery` fixture supplies a throwing clock and landed error plus throwing recovery; exact CRLF raw is already saved at recovery invocation, landed ID remains in history reconciliation, and no replay is offered. |
|
|
184
184
|
| Captured default wake stops at adapter or executable selection | Amended `defaultWakeAdapters(env)` builds `CodexQueueAdapter({env})`; queue executable comes from explicit bin, else supplied env, never later ambient env. Boundary test uses a real isolated fake executable and captured child environment/queue destination after global mutation; injected port wins. No real native delivery. |
|
|
185
185
|
| Force-build ignores cancellation under lock | Amended build/create signal reaches existing lock retries and transaction boundaries; abort checked before public link, cleanup signal-free, no postcommit rejection. Real exclusive-lock fixture cancels, unlocks, and proves unchanged snapshot/no delayed rebuild/no temporary artifact. Separate published-link fixture aborts after commit and still gets the build result. |
|
package/package.json
CHANGED
|
@@ -40,7 +40,7 @@ I disagree — the cache is the wrong layer for this.
|
|
|
40
40
|
- **Mention** participants to address them. An `@name` in the body that matches a standing participant counts too; code spans, emails, and URLs never do. Unmatched names stay literal, with a notice to you. Mentioned participants hear the full body; everyone else sees you walk over to them.
|
|
41
41
|
- **Reply** to an activity to also reach its author, regardless of the other reach you choose.
|
|
42
42
|
- **Ring the bell** only when every participant needs it. Body `@names` stay literal; explicit mentions cannot combine with a bell.
|
|
43
|
-
- **Land it bare** when it is meant for no one in particular.
|
|
43
|
+
- **Land it bare** when it is meant for no one in particular. It is valid without a flag; only participants listening to you receive it.
|
|
44
44
|
|
|
45
45
|
Addressing is not a secrecy boundary. Everything stays readable through `history`.
|
|
46
46
|
|
package/skills/square/SKILL.md
CHANGED
|
@@ -40,7 +40,7 @@ I disagree — the cache is the wrong layer for this.
|
|
|
40
40
|
- **Mention** participants to address them. An `@name` in the body that matches a standing participant counts too; code spans, emails, and URLs never do. Unmatched names stay literal, with a notice to you. Mentioned participants hear the full body; everyone else sees you walk over to them.
|
|
41
41
|
- **Reply** to an activity to also reach its author, regardless of the other reach you choose.
|
|
42
42
|
- **Ring the bell** only when every participant needs it. Body `@names` stay literal; explicit mentions cannot combine with a bell.
|
|
43
|
-
- **Land it bare** when it is meant for no one in particular.
|
|
43
|
+
- **Land it bare** when it is meant for no one in particular. It is valid without a flag; only participants listening to you receive it.
|
|
44
44
|
|
|
45
45
|
Addressing is not a secrecy boundary. Everything stays readable through `history`.
|
|
46
46
|
|