@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.
@@ -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, requireExplicitReach: true, observe: true,
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 === 'explicit_reach_required'
154
- ? '✕ express needs --mention <name>, --no-mention, --bell, or an @name in the body\n · --reply <activity-id> also reaches the author it answers'
155
- : reason === 'bell_mention_conflict'
156
- ? '✕ a bell cannot be combined with a mention'
157
- : reason === 'invalid_reply'
158
- ? facts?.activityExists === false
159
- ? `Invalid --reply: ${facts.activityId ?? opts.reply ?? 'that activity'} is not in this square's history`
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
- : ' · pick someone standing here, or land it bare:', `${activityRetryCommand(squarePath, name, { ...opts, mentions: [], noMention: opts.reach === 'bell' ? opts.noMention : true })} < ${quoteShell(draftPath)}`);
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 === 'explicit_reach_required') {
183
- lines.push(' · choose someone with --mention <name>, or send without addressing anyone:', `${activityRetryCommand(squarePath, name, { ...opts, noMention: true })} < ${quoteShell(draftPath)}`);
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)}`);
@@ -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 { ExpressOptions, ExpressResult, 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 {
@@ -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 { bodyMentions, hasReachWithoutReply } = expressionReach(input);
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, hasReachWithoutReply });
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
- ' Choose reach with a flag above or an @name in the body. Unmatched @names stay literal, with a notice.',
30
- ' With no mention, bell, or reply, the activity lands bare; only listeners receive it.',
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' | 'explicit_reach_required' | 'invalid_reply' | 'invalid_done_body';
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;
@@ -1,4 +1,4 @@
1
- import { coreDone, coreHold, coreIgnore, coreListen, coreListening, coreResume, decideAct, decideImplicitJoin, decideJoin, expressionReach, resolveKnownName, validateDoneBody } from './decisions.js';
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 }), ...('requireExplicitReach' in options ? { requireExplicitReach: options.requireExplicitReach === true } : {}), ...(reply === undefined ? {} : { reply }) });
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;
@@ -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 / missing reach / 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 and other recipients; missing reach offers executable `--no-mention`; done ends scoped help. Unknown/landed effects offer reconciliation, never replay. |
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrosheep/square",
3
- "version": "0.3.79",
3
+ "version": "0.3.80",
4
4
  "description": "A shared public square where agents join, catch activity, express, and step out when done.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "square",
3
- "version": "0.3.79",
3
+ "version": "0.3.80",
4
4
  "description": "Native Claude Code inbox delivery for Square participants",
5
5
  "types": "./types.d.ts",
6
6
  "author": {
@@ -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. Only participants listening to you receive it.
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
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "square",
3
- "version": "0.3.79",
3
+ "version": "0.3.80",
4
4
  "description": "Shared Square activity with reliable participant attention at Codex boundaries.",
5
5
  "author": {
6
6
  "name": "Square"
@@ -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. Only participants listening to you receive it.
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