@astrosheep/square 0.3.96 → 0.3.98

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.
Files changed (40) hide show
  1. package/dist/activity-query.d.ts +4 -0
  2. package/dist/activity-query.js +51 -0
  3. package/dist/artifact.d.ts +1 -1
  4. package/dist/artifact.js +11 -17
  5. package/dist/boundary-presentation.js +15 -19
  6. package/dist/channel.d.ts +4 -2
  7. package/dist/channel.js +6 -0
  8. package/dist/claude-delivery.js +1 -8
  9. package/dist/cli/observation-commands.js +7 -4
  10. package/dist/codex-hook.d.ts +0 -2
  11. package/dist/codex-hook.js +0 -1
  12. package/dist/decisions.d.ts +2 -11
  13. package/dist/decisions.js +3 -8
  14. package/dist/delivery-health.js +3 -13
  15. package/dist/delivery.d.ts +1 -11
  16. package/dist/delivery.js +0 -7
  17. package/dist/identity.d.ts +0 -1
  18. package/dist/identity.js +0 -1
  19. package/dist/inbox.js +9 -7
  20. package/dist/notifications.d.ts +1 -1
  21. package/dist/operation-presentation.js +5 -7
  22. package/dist/ports.d.ts +5 -9
  23. package/dist/search.js +4 -5
  24. package/dist/sqlite-square.d.ts +0 -1
  25. package/dist/sqlite-square.js +40 -12
  26. package/dist/square-facade.d.ts +32 -15
  27. package/dist/square-sqlite.js +47 -6
  28. package/dist/square-tools.js +11 -1
  29. package/dist/views.d.ts +2 -3
  30. package/dist/views.js +11 -27
  31. package/guides/operation-cancellation.md +5 -1
  32. package/guides/primitive-status-consolidation.md +1 -1
  33. package/guides/receive-primitive.md +4 -2
  34. package/guides/row-runtime-delivery-evidence.md +3 -3
  35. package/guides/row-storage-design.md +5 -1
  36. package/guides/square-api.md +108 -9
  37. package/package.json +1 -2
  38. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  39. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  40. package/plugins/codex/hooks/hooks.json +6 -6
@@ -0,0 +1,4 @@
1
+ import type { ActivityFilter } from './ports.js';
2
+ import type { ActivityQuery } from './square-facade.js';
3
+ /** Both public read paths compile the same filters; neither resolves senders through membership. */
4
+ export declare function activityQueryFilter(query: ActivityQuery, extraFields?: readonly string[]): ActivityFilter;
@@ -0,0 +1,51 @@
1
+ import { SquareError, validateName } from './model.js';
2
+ import { parseActivityId } from './square-core.js';
3
+ import { validateMetadataMatch } from './delivery-deferral.js';
4
+ import { compileSearchPattern } from './search.js';
5
+ /** Both public read paths compile the same filters; neither resolves senders through membership. */
6
+ export function activityQueryFilter(query, extraFields = []) {
7
+ const fail = (message) => { throw new SquareError('invalid_args', message); };
8
+ const fields = ['at', 'from', 'match', 'search', 'cursor', 'limit', ...extraFields];
9
+ if (typeof query !== 'object' || query === null || Array.isArray(query))
10
+ fail('Activity query must be an object.');
11
+ for (const field of Object.keys(query))
12
+ if (!fields.includes(field))
13
+ fail(`Unknown activity query field: ${field}`);
14
+ const indexes = query.at === undefined ? undefined : !Array.isArray(query.at) ? fail('Query at must be an array of activity IDs.')
15
+ : Array.from(query.at, id => {
16
+ const index = parseActivityId(id);
17
+ return index === undefined ? fail(`Invalid activity id: ${id}`) : index;
18
+ });
19
+ const actors = query.from === undefined ? undefined : !Array.isArray(query.from) ? fail('Query from must be an array of sender names.')
20
+ : Array.from(query.from, name => {
21
+ if (typeof name !== 'string')
22
+ fail('Query from must contain sender names.');
23
+ validateName(name);
24
+ return name;
25
+ });
26
+ const metadataMatch = query.match === undefined ? undefined : validateMetadataMatch(query.match);
27
+ let afterIndex, beforeIndex;
28
+ if (query.cursor !== undefined) {
29
+ const cursor = query.cursor;
30
+ if (typeof cursor !== 'object' || cursor === null || Array.isArray(cursor)
31
+ || !['before', 'after'].includes(cursor.direction) || Object.keys(cursor).some(key => !['id', 'direction'].includes(key)))
32
+ fail('Query cursor requires an activity ID and before/after direction.');
33
+ const index = parseActivityId(cursor.id);
34
+ if (index === undefined)
35
+ fail(`Invalid activity id: ${cursor.id}`);
36
+ if (cursor.direction === 'after')
37
+ afterIndex = index;
38
+ else
39
+ beforeIndex = index;
40
+ }
41
+ let search = query.search;
42
+ if (search !== undefined) {
43
+ if (typeof search !== 'object' || search === null || Array.isArray(search)
44
+ || typeof search.text !== 'string' || search.text === '' || !['literal', 'regex'].includes(search.mode)
45
+ || Object.keys(search).some(key => !['text', 'mode'].includes(key)))
46
+ fail('Query search requires non-empty text and literal/regex mode.');
47
+ compileSearchPattern(search.text, search.mode === 'literal');
48
+ search = { text: search.text, mode: search.mode };
49
+ }
50
+ return { indexes, actors, metadataMatch, search, afterIndex, beforeIndex };
51
+ }
@@ -34,7 +34,7 @@ export declare function writeSquareFile(location: string, state: SquareState, si
34
34
  /** Explicit test/export projection. Hot callers must use the typed port. */
35
35
  export declare function readSquareSnapshot(location: string, signal?: AbortSignal): Promise<SquareSnapshot>;
36
36
  export declare function readSquareRevision(location: string, signal?: AbortSignal): Promise<number>;
37
- export declare function observeSquareRevision(location: string): Promise<import('./file-changes.js').VersionObserver<number | null>>;
38
37
  export declare function readSquareDeliveryRevision(location: string, signal?: AbortSignal): Promise<number>;
38
+ export declare function observeSquareRevision(location: string): Promise<import('./file-changes.js').VersionObserver<number | null>>;
39
39
  export declare function observeSquareDeliveryRevision(location: string): Promise<import('./file-changes.js').VersionObserver<number | null>>;
40
40
  export declare function diagnoseSquareFile(location: string): Promise<DiagnoseResult>;
package/dist/artifact.js CHANGED
@@ -120,7 +120,7 @@ const readMethods = [
120
120
  'header', 'participant', 'roster', 'attention', 'incomingAttention',
121
121
  'activity', 'activityHeads', 'activities', 'activityCount', 'contextIndexes', 'sayOrdinals', 'recentSayTimes',
122
122
  'audience', 'directed', 'directedAt', 'directedCount', 'pendingAudience',
123
- 'receipt', 'receiptFacts', 'receiptAssignments', 'lease', 'participantCounts', 'seat', 'seatsForOwner',
123
+ 'receipt', 'receiptFacts', 'lease', 'participantCounts', 'seat', 'seatsForOwner',
124
124
  'deliveryEvidence', 'deliveryByToken', 'cancelledThrough', 'deliveryDeferrals',
125
125
  ];
126
126
  const writeMethods = ['applyLanding', 'markSeen', 'putLease', 'removeLease', 'storeSeat', 'removeSeat', 'updateWakeAddress', 'reattachSeat', 'detachSeat', 'cancelThrough', 'claimDelivery', 'prepareDelivery', 'completeDelivery', 'observeNativeDelivery', 'putDeliveryDeferral', 'renewDeliveryDeferral', 'releaseDeliveryDeferral'];
@@ -404,26 +404,14 @@ export function readSquareSnapshot(location, signal) {
404
404
  export function readSquareRevision(location, signal) {
405
405
  return inspectFile(location, db => createSqliteDriver(db).header().revision, signal);
406
406
  }
407
- export function observeSquareRevision(location) {
408
- return observeFileVersion(location, 'square-revision', async (file, signal) => {
409
- try {
410
- return await readSquareRevision(file, signal);
411
- }
412
- catch (error) {
413
- abort(signal);
414
- if (error instanceof InternalSquareError && error.code === 'not_found')
415
- return null;
416
- throw error;
417
- }
418
- });
419
- }
420
407
  export function readSquareDeliveryRevision(location, signal) {
421
408
  return inspectFile(location, db => createSqliteDriver(db).header().deliveryRevision, signal);
422
409
  }
423
- export function observeSquareDeliveryRevision(location) {
424
- return observeFileVersion(location, 'square-delivery-revision', async (file, signal) => {
410
+ function observeRevision(location, revision) {
411
+ const read = revision === 'square-revision' ? readSquareRevision : readSquareDeliveryRevision;
412
+ return observeFileVersion(location, revision, async (file, signal) => {
425
413
  try {
426
- return await readSquareDeliveryRevision(file, signal);
414
+ return await read(file, signal);
427
415
  }
428
416
  catch (error) {
429
417
  abort(signal);
@@ -433,6 +421,12 @@ export function observeSquareDeliveryRevision(location) {
433
421
  }
434
422
  });
435
423
  }
424
+ export function observeSquareRevision(location) {
425
+ return observeRevision(location, 'square-revision');
426
+ }
427
+ export function observeSquareDeliveryRevision(location) {
428
+ return observeRevision(location, 'square-delivery-revision');
429
+ }
436
430
  export async function diagnoseSquareFile(location) {
437
431
  try {
438
432
  return { problems: [], ...await inspectFile(await canonicalPath(location), inspectSqlite) };
@@ -22,17 +22,15 @@ function renderBoundary(inbox) {
22
22
  const omission = (count) => `${count} more ${count === 1 ? 'activity' : 'activities'} not shown.`;
23
23
  for (const entry of queued) {
24
24
  const { membership, notification } = entry;
25
- const block = [
26
- renderAttentionPreview({
27
- squarePath: membership.location,
28
- actIndex: notification.actIndex,
29
- recipient: membership.seat.participant,
30
- actor: notification.actor,
31
- route: notification.route,
32
- body: notification.body,
33
- delivery: notification.delivery,
34
- }),
35
- ].join('\n');
25
+ const block = renderAttentionPreview({
26
+ squarePath: membership.location,
27
+ actIndex: notification.actIndex,
28
+ recipient: membership.seat.participant,
29
+ actor: notification.actor,
30
+ route: notification.route,
31
+ body: notification.body,
32
+ delivery: notification.delivery,
33
+ });
36
34
  if (notification.delivery?.mode !== 'direct') {
37
35
  remainingAgora -= 1;
38
36
  const omittedAfter = omitted + remainingAgora;
@@ -47,7 +45,7 @@ function renderBoundary(inbox) {
47
45
  deliveries.push(notification.delivery);
48
46
  complete.push({
49
47
  membership,
50
- actIndexes: [notification.actIndex],
48
+ actIndex: notification.actIndex,
51
49
  markSeen: !attentionBodyIsClipped(notification.body, notification.delivery),
52
50
  });
53
51
  }
@@ -76,12 +74,10 @@ async function presentPendingAtBoundaryUnlocked(identity, present, lookup = sess
76
74
  artifact = createArtifactPort(location);
77
75
  artifacts.set(location, artifact);
78
76
  }
79
- for (const index of entry.actIndexes) {
80
- const claim = await preparePendingPresentation({ artifact, location, participant: seat.participant,
81
- activity: index, seat, markSeen: entry.markSeen, signal });
82
- if (claim !== undefined)
83
- claims.push({ key: pendingKey(entry.membership, index), claim });
84
- }
77
+ const claim = await preparePendingPresentation({ artifact, location, participant: seat.participant,
78
+ activity: entry.actIndex, seat, markSeen: entry.markSeen, signal });
79
+ if (claim !== undefined)
80
+ claims.push({ key: pendingKey(entry.membership, entry.actIndex), claim });
85
81
  }
86
82
  }
87
83
  catch (error) {
@@ -94,7 +90,7 @@ async function presentPendingAtBoundaryUnlocked(identity, present, lookup = sess
94
90
  const fresh = renderBoundary(pending.map(membership => ({ ...membership,
95
91
  notifications: membership.notifications.filter(note => eligible.has(pendingKey(membership, note.actIndex))),
96
92
  })));
97
- const included = new Set(fresh.complete.flatMap(entry => entry.actIndexes.map(index => pendingKey(entry.membership, index))));
93
+ const included = new Set(fresh.complete.map(entry => pendingKey(entry.membership, entry.actIndex)));
98
94
  return { payload: fresh.context, included: claims.filter(({ key }) => included.has(key)).map(({ claim }) => claim) };
99
95
  }, present, { signal, classifyError: () => 'failed' });
100
96
  if (report.observation.kind === 'threw')
package/dist/channel.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { type HarnessSessionChannel } from './participant-identity.js';
2
2
  import type { SquareResourceOwner, WakeTransportPort } from './ports.js';
3
- import type { ActivityId, ChannelSendInput, ChannelSendResult, DeliveryDeferral, HeldSeat, MetadataMatch, OperationControl, Owner, Participant, WakeAddress } from './square-facade.js';
3
+ import type { ActivityId, ChannelSendInput, ChannelSendResult, DeliveryDeferral, HeldSeat, MetadataMatch, OperationControl, Owner, Participant, WakeAddress, PendingOptions, PendingResult, AckResult } from './square-facade.js';
4
4
  export { harnessOwner, decodeHarnessOwner, localUserOwner } from './owner.js';
5
- export type { ActivityDelivery, ActivityMetadata, ChannelTruncate } from './square-facade.js';
5
+ export type { ActivityDelivery, ActivityMetadata, ActivityQuery, ActivitySearch, ActivityCursor, ChannelTruncate, PerceivedActivity, PendingOptions, PendingResult, AckResult } from './square-facade.js';
6
6
  export type { ActivityId, ChannelSendInput, ChannelSendResult, DeliveryDeferral, HeldSeat, MetadataMatch, OperationControl, Owner, WakeAddress, SquareResourceOwner, WakeTransportPort, HarnessSessionChannel };
7
7
  export interface ResolvedHarnessSession {
8
8
  readonly channel: HarnessSessionChannel;
@@ -61,6 +61,8 @@ export declare class ChannelEndpoint {
61
61
  constructor(channel: Channel, participant: Participant);
62
62
  get name(): string;
63
63
  get generation(): ActivityId;
64
+ pending(options?: PendingOptions, control?: OperationControl): Promise<PendingResult>;
65
+ ack(ids: readonly ActivityId[], control?: OperationControl): Promise<AckResult>;
64
66
  leave(control?: OperationControl): Promise<void>;
65
67
  deferDelivery(input: Readonly<{
66
68
  match: MetadataMatch;
package/dist/channel.js CHANGED
@@ -70,6 +70,12 @@ export class ChannelEndpoint {
70
70
  }
71
71
  get name() { return this.participant.name; }
72
72
  get generation() { return this.participant.generation; }
73
+ pending(options, control) {
74
+ return this.participant.pending(options, control);
75
+ }
76
+ ack(ids, control) {
77
+ return this.participant.ack(ids, control);
78
+ }
73
79
  async leave(control) { await this.participant.done(undefined, {}, control); }
74
80
  deferDelivery(input, control) {
75
81
  return this.participant.deferDelivery(input, control);
@@ -12,12 +12,6 @@ const marker = /(?:^|\n)\[square-inbox:([a-zA-Z0-9-]+)\](?:\n|$)/g;
12
12
  export function nativeTokens(text) { return [...text.matchAll(marker)].map((match) => match[1]); }
13
13
  export function nativePayload(token, preview) { return `[square-inbox:${token}]\n${preview}\n[/square-inbox:${token}]`; }
14
14
  function nativeSupported(route) { return route.address.loaded === 'true' && !!route.address.endpoint; }
15
- function sameAddress(left, right) {
16
- if (left === undefined)
17
- return false;
18
- const keys = Object.keys(left);
19
- return keys.length === Object.keys(right).length && keys.every((key) => left[key] === right[key]);
20
- }
21
15
  function hashPayload(payload) { return createHash('sha256').update(payload).digest('hex'); }
22
16
  function previewFacts(facts, request) {
23
17
  const index = parseActivityId(request.activity);
@@ -80,8 +74,7 @@ function currentPayload(facts, request) {
80
74
  const seat = facts.seat(request.participant), evidence = facts.deliveryByToken(request.claimToken);
81
75
  if (seat === undefined || seat.attachment !== 'attached' || evidence === undefined || seat.generation !== evidence.generation
82
76
  || seat.generation !== request.seat.generation || seat.owner !== request.seat.owner
83
- || evidence.activity !== request.activity || !['dispatching', 'unknown', 'accepted'].includes(evidence.outcome)
84
- || seat.wakeAddress?.kind !== 'claude-native' || !sameAddress(seat.wakeAddress.address, request.route.address))
77
+ || evidence.activity !== request.activity || !['dispatching', 'unknown', 'accepted'].includes(evidence.outcome))
85
78
  return undefined;
86
79
  const native = previewFacts(facts, request);
87
80
  if (native === undefined)
@@ -1,7 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import { once } from 'node:events';
3
3
  import { runClaudeMod } from '../claude-mod.js';
4
- import { runCodexHookAsync } from '../codex-hook.js';
4
+ import { runCodexHook } from '../codex-hook.js';
5
5
  import { isSquareError } from '../model.js';
6
6
  import { commandPrefix, participantIdentity, truncateChars, withPathOutput, quoteShell, } from '../presentation.js';
7
7
  import { PARTICIPANTS_DEFAULT_LIMIT, PARTICIPANTS_MAX_LIMIT, renderHistoryResult, renderParticipantsResult, renderStatusResult, } from '../operation-presentation.js';
@@ -375,8 +375,11 @@ function parseHistory(argv, context) {
375
375
  if (before !== undefined && after !== undefined)
376
376
  fail('--before and --after cannot be combined.');
377
377
  return {
378
- query: { limit, from: participants, since, after, before, at: at.length === 0 ? undefined : at,
379
- contextBefore: beforeContext, contextAfter: afterContext, mention, grep, fixed, order },
378
+ query: { limit, from: participants.length === 0 ? undefined : participants, since,
379
+ cursor: after !== undefined ? { id: after, direction: 'after' } : before === undefined ? undefined : { id: before, direction: 'before' },
380
+ at: at.length === 0 ? undefined : at,
381
+ contextBefore: beforeContext, contextAfter: afterContext, mention, order,
382
+ search: grep !== undefined ? { text: grep, mode: 'regex' } : fixed === undefined ? undefined : { text: fixed, mode: 'literal' } },
380
383
  noTruncate, format,
381
384
  continuationArgs: historyContinuationArgs(argv),
382
385
  };
@@ -523,4 +526,4 @@ function hookCommand(runHook) {
523
526
  };
524
527
  }
525
528
  export const claudeModCommand = hookCommand(runClaudeMod);
526
- export const codexHookCommand = hookCommand(runCodexHookAsync);
529
+ export const codexHookCommand = hookCommand(runCodexHook);
@@ -5,10 +5,8 @@ export interface CodexHookInput {
5
5
  session_id?: unknown;
6
6
  hook_event_name?: unknown;
7
7
  cwd?: unknown;
8
- source?: unknown;
9
8
  }
10
9
  export type HookTrace = (line: string) => void;
11
10
  export declare const CODEX_HOOK_BUDGET_MS = 4500;
12
11
  export declare function codexHookResponse(input: CodexHookInput, lookup?: (identity: Owner, env?: NodeJS.ProcessEnv, signal?: AbortSignal) => Promise<PendingMembership[]> | PendingMembership[], env?: NodeJS.ProcessEnv, deliveryTransports?: WakeTransports, trace?: HookTrace): Promise<object | undefined>;
13
12
  export declare function runCodexHook(inputText: string, env?: NodeJS.ProcessEnv): Promise<string>;
14
- export declare const runCodexHookAsync: typeof runCodexHook;
@@ -47,4 +47,3 @@ export async function runCodexHook(inputText, env = process.env) {
47
47
  const result = await codexHookResponse(input, sessionInbox, env);
48
48
  return result === undefined ? '' : `${JSON.stringify(result)}\n`;
49
49
  }
50
- export const runCodexHookAsync = runCodexHook;
@@ -7,12 +7,7 @@ type TimedAct<K extends Act['kind'] = Act['kind']> = Extract<Act, {
7
7
  };
8
8
  import type { ExpressResult, ParticipantSummary, UnreadActivitySummary } from './square-facade.js';
9
9
  export declare function resolveKnownName(facts: SquareRead, name: string): string;
10
- export interface DecideJoinResult {
11
- joinedName: string;
12
- addParticipant: boolean;
13
- joinAct?: TimedAct<'join'>;
14
- }
15
- export declare function decideJoin(facts: SquareRead, name: string, now: number): DecideJoinResult;
10
+ export declare function decideJoin(facts: SquareRead, name: string, now: number): TimedAct<'join'> | undefined;
16
11
  export type ActDecision = ({
17
12
  type: 'sent';
18
13
  act: TimedAct;
@@ -21,8 +16,6 @@ export type ActDecision = ({
21
16
  activitySummaries: UnreadActivitySummary[];
22
17
  } | {
23
18
  type: 'capped';
24
- count: number;
25
- hardCap: number;
26
19
  } | {
27
20
  type: 'throttled';
28
21
  delayMs: number;
@@ -45,9 +38,7 @@ export declare function decideAct(facts: SquareRead, input: {
45
38
  }): ActDecision;
46
39
  export declare function validateDoneBody(body: unknown): void;
47
40
  export declare function coreDone(facts: SquareRead, name: string, body: string, now: number): TimedAct<'done'>;
48
- export declare function coreListen(facts: SquareRead, actor: string, target: string, now: number): TimedAct<"listen" | "ignore" | "unlisten"> | undefined;
49
- export declare function coreIgnore(facts: SquareRead, actor: string, target: string, now: number): TimedAct<"listen" | "ignore" | "unlisten"> | undefined;
50
- export declare function coreUnlisten(facts: SquareRead, actor: string, target: string, now: number): TimedAct<"listen" | "ignore" | "unlisten"> | undefined;
41
+ export declare function attentionChange(facts: SquareRead, kind: 'listen' | 'ignore' | 'unlisten', actor: string, target: string, now: number): TimedAct<'listen' | 'ignore' | 'unlisten'> | undefined;
51
42
  export declare function coreListening(facts: SquareRead, actor: string): readonly string[];
52
43
  export declare function compareParticipantActivity(a: Pick<ParticipantSummary, 'name' | 'lastActiveAt'>, b: Pick<ParticipantSummary, 'name' | 'lastActiveAt'>): number;
53
44
  export declare function coreParticipants(facts: SquareRead, now: number): ParticipantSummary[];
package/dist/decisions.js CHANGED
@@ -13,9 +13,7 @@ export function resolveKnownName(facts, name) {
13
13
  export function decideJoin(facts, name, now) {
14
14
  validateName(name);
15
15
  const current = facts.participant(name);
16
- const joinedName = current?.name ?? name;
17
- return current?.joined ? { joinedName, addParticipant: false }
18
- : { joinedName, addParticipant: current === undefined, joinAct: { kind: 'join', actor: joinedName, at: now } };
16
+ return current?.joined ? undefined : { kind: 'join', actor: current?.name ?? name, at: now };
19
17
  }
20
18
  function validateActivityName(name) {
21
19
  try {
@@ -131,7 +129,7 @@ export function decideAct(facts, input) {
131
129
  if (!current.joined)
132
130
  throw activityRefusal('not_joined', `${name} has not joined this square`);
133
131
  if (header.hardCap !== null && current.sayCount >= header.hardCap)
134
- return { type: 'capped', count: current.sayCount, hardCap: header.hardCap };
132
+ return { type: 'capped' };
135
133
  if (reach === 'bell') {
136
134
  const recent = facts.recentSayTimes(now - 3600000, 1, name, true);
137
135
  if (recent.length > 0)
@@ -162,7 +160,7 @@ export function coreDone(facts, name, body, now) {
162
160
  requireStanding(current);
163
161
  return { kind: 'done', actor: current.name, at: now };
164
162
  }
165
- function attentionChange(facts, kind, actor, target, now) {
163
+ export function attentionChange(facts, kind, actor, target, now) {
166
164
  const current = resolveStanding(facts, actor);
167
165
  validateName(target);
168
166
  requireStanding(current);
@@ -175,9 +173,6 @@ function attentionChange(facts, kind, actor, target, now) {
175
173
  }
176
174
  return existing?.kind === kind ? undefined : { kind, actor: current.name, target, at: now };
177
175
  }
178
- export function coreListen(facts, actor, target, now) { return attentionChange(facts, 'listen', actor, target, now); }
179
- export function coreIgnore(facts, actor, target, now) { return attentionChange(facts, 'ignore', actor, target, now); }
180
- export function coreUnlisten(facts, actor, target, now) { return attentionChange(facts, 'unlisten', actor, target, now); }
181
176
  export function coreListening(facts, actor) {
182
177
  const current = resolveStanding(facts, actor);
183
178
  return facts.attention(current.name).filter((item) => item.kind === 'listen').map((item) => item.target);
@@ -32,17 +32,7 @@ export async function classifyDeliveryHealth(squarePath, opts) {
32
32
  || row.outcome === 'dispatching' && (row.expiresAt === undefined || row.expiresAt > now)));
33
33
  const terminal = evidence.findLast((row) => row.kind === 'wake' && (row.outcome === 'accepted' || row.outcome === 'unknown'));
34
34
  const attemptRow = evidence.findLast((row) => row.kind === 'wake' && (row.outcome === 'accepted' || row.outcome === 'unknown' || row.outcome === 'failed'));
35
- const attempt = attemptRow === undefined || seat === undefined || attemptRow.route?.kind === undefined ? undefined : {
36
- at: attemptRow.at,
37
- attention: { squarePath, recipient: seat.participant, actIndex: note.index },
38
- routeKind: attemptRow.route.kind,
39
- outcome: attemptRow.outcome,
40
- attemptN: attemptRow.attemptN,
41
- ...(attemptRow.signature === undefined ? {} : { signature: attemptRow.signature }),
42
- ...(attemptRow.message === undefined ? {} : { message: attemptRow.message }),
43
- ...(attemptRow.diagnostic === undefined ? {} : { diagnostic: attemptRow.diagnostic }),
44
- ...(attemptRow.nativeObservations === undefined ? {} : { nativeObservations: attemptRow.nativeObservations }),
45
- };
35
+ const attempt = attemptRow === undefined || seat === undefined ? undefined : deliveryAttempt(squarePath, attemptRow, { squarePath, recipient: seat.participant, actIndex: note.index });
46
36
  const kind = presentation
47
37
  ? 'presented-not-delivered'
48
38
  : terminal?.outcome === 'accepted'
@@ -127,12 +117,12 @@ function formatRelease(release, now) {
127
117
  const diagnostic = release.diagnostic === undefined ? '' : ` · ${displayField(JSON.stringify(release.diagnostic))}`;
128
118
  return ` · ${formatActivityId(release.attention.actIndex)} → ${displayField(participantIdentity(release.attention.recipient))} · ${route} · ${reason} · ${age}${message}${diagnostic}`;
129
119
  }
130
- function deliveryAttempt(squarePath, row) {
120
+ function deliveryAttempt(squarePath, row, attention = { squarePath, recipient: row.participant, actIndex: Number(row.activity.slice(4)) }) {
131
121
  if (row.route?.kind === undefined || row.outcome === 'released' || row.outcome === 'dispatching')
132
122
  return undefined;
133
123
  return {
134
124
  at: row.at,
135
- attention: { squarePath, recipient: row.participant, actIndex: Number(row.activity.slice(4)) },
125
+ attention,
136
126
  routeKind: row.route.kind,
137
127
  outcome: row.outcome,
138
128
  attemptN: row.attemptN,
@@ -1,14 +1,6 @@
1
- import type { DirectedNotificationRoute, StoredAct, WatchLease } from './model.js';
1
+ import type { DirectedNotificationRoute, WatchLease } from './model.js';
2
2
  import type { DeliveryEvidence, DirectedHead, ParticipantFact, SquareArtifactPort, SquareRead, WakeRequest } from './ports.js';
3
3
  export type { DirectedNotificationRoute } from './model.js';
4
- export type SayItem = StoredAct & {
5
- kind: 'say';
6
- };
7
- export interface PlannedNotification {
8
- item: SayItem;
9
- recipient: string;
10
- route: DirectedNotificationRoute;
11
- }
12
4
  /** Exact stored activity projection for transports that send an awareness reminder. */
13
5
  export declare function wakePreview(facts: SquareRead, request: WakeRequest): string | undefined;
14
6
  export interface RoutedNotification {
@@ -26,8 +18,6 @@ export declare function pendingHead(facts: SquareRead, name: string, index: numb
26
18
  export declare function pendingRecipients(facts: SquareRead, index: number): DirectedHead[];
27
19
  /** Still-pending directed heads for one recipient, oldest first. Bodies belong to presentation. */
28
20
  export declare function pendingHeads(facts: SquareRead, name: string): DirectedHead[];
29
- /** Complete recipient projection, assembled inside its one transaction. */
30
- export declare function pendingNotifications(facts: SquareRead, name: string): PlannedNotification[];
31
21
  export interface PendingScan {
32
22
  readonly roster: readonly ParticipantFact[];
33
23
  readonly throughIndex: number;
package/dist/delivery.js CHANGED
@@ -58,13 +58,6 @@ export function pendingHeads(facts, name) {
58
58
  }
59
59
  return heads;
60
60
  }
61
- /** Complete recipient projection, assembled inside its one transaction. */
62
- export function pendingNotifications(facts, name) {
63
- const heads = pendingHeads(facts, name), recipient = heads[0]?.recipient ?? name;
64
- const routes = new Map(heads.map((head) => [head.index, head.route]));
65
- return facts.activities(heads.map((head) => head.index)).flatMap((item) => item.kind === 'say'
66
- ? [{ item, recipient, route: routes.get(item.index) }] : []);
67
- }
68
61
  export function capturePendingScan(facts) {
69
62
  return { roster: facts.roster().filter((participant) => participant.joined), throughIndex: facts.header().lastActIndex };
70
63
  }
@@ -8,6 +8,5 @@ export declare const SQUARE_IDENTITY: Readonly<{
8
8
  cliName: "square";
9
9
  pluginId: "square@astrosheep";
10
10
  hookCommand: "square codex-hook";
11
- hookMarker: "square-codex-hook";
12
11
  clientName: "astrosheep_square";
13
12
  }>;
package/dist/identity.js CHANGED
@@ -22,6 +22,5 @@ export const SQUARE_IDENTITY = Object.freeze({
22
22
  cliName: 'square',
23
23
  pluginId: 'square@astrosheep',
24
24
  hookCommand: 'square codex-hook',
25
- hookMarker: 'square-codex-hook',
26
25
  clientName: 'astrosheep_square',
27
26
  });
package/dist/inbox.js CHANGED
@@ -3,7 +3,7 @@ import { indexedSquareEntries, observeSquareIndex } from './square-index.js';
3
3
  import { canonicalPath } from './canonical-path.js';
4
4
  import { formatActivityId, parseActivityId } from './square-core.js';
5
5
  import { freshWatchLease, WATCH_STALE_MS } from './runtime.js';
6
- import { pendingHead, pendingNotifications, leaseOwnsNotification } from './delivery.js';
6
+ import { pendingHead, pendingHeads, leaseOwnsNotification } from './delivery.js';
7
7
  import { deliveryDeferralExpiry } from './delivery-deferral.js';
8
8
  import { attentionBodyIsClipped, renderAttentionPreview, renderSquareReminder } from './attention-presentation.js';
9
9
  export function pendingKey(membership, actIndex) {
@@ -146,12 +146,14 @@ async function projectPending(candidates, cache, signal) {
146
146
  const header = facts.header();
147
147
  let cached = cache?.get(location);
148
148
  if (cached?.coordinate !== coordinate || cached.version !== header.revision) {
149
- cached = { coordinate, version: header.revision, value: locatedSeats(facts, location, seats).map(membership => ({
150
- ...membership, notifications: pendingNotifications(facts, membership.seat.participant).map(({ item, route }) => ({
151
- actIndex: item.index, actor: item.actor, at: item.at, body: item.body ?? '(structured activity)', route,
152
- ...(item.delivery === undefined ? {} : { delivery: item.delivery }),
153
- })),
154
- })) };
149
+ cached = { coordinate, version: header.revision, value: locatedSeats(facts, location, seats).map(membership => {
150
+ const heads = pendingHeads(facts, membership.seat.participant);
151
+ const routes = new Map(heads.map(head => [head.index, head.route]));
152
+ return { ...membership, notifications: facts.activities(heads.map(head => head.index)).flatMap(item => item.kind === 'say'
153
+ ? [{ actIndex: item.index, actor: item.actor, at: item.at, body: item.body ?? '(structured activity)',
154
+ route: routes.get(item.index), ...(item.delivery === undefined ? {} : { delivery: item.delivery }) }]
155
+ : []) };
156
+ }) };
155
157
  }
156
158
  // Cache conversation content, never leases or delivery eligibility: evidence can change
157
159
  // without a conversation revision and must not force body hydration or self-wake retries.
@@ -1,6 +1,5 @@
1
1
  import { type WakeRouteKind } from './model.js';
2
2
  import type { WakeTransportPort } from './ports.js';
3
- export type { PlannedNotification } from './delivery.js';
4
3
  export declare const PRIVILEGED_HOOK_BUDGET_MS = 3000;
5
4
  export declare function wakeGraceMs(env?: NodeJS.ProcessEnv): number;
6
5
  export type WakeTransports = Readonly<Partial<Record<WakeRouteKind, WakeTransportPort>>>;
@@ -24,3 +23,4 @@ export interface SweepPendingNotificationsOptions {
24
23
  }
25
24
  /** Select old pending attention at a bounded action boundary for an explicit executor. */
26
25
  export declare function sweepPendingNotifications(squarePath: string, opts?: SweepPendingNotificationsOptions): Promise<number[]>;
26
+ export {};
@@ -155,10 +155,8 @@ function historyQueryArgs(query) {
155
155
  args.push('-A', String(query.contextAfter));
156
156
  if (query.mention !== undefined)
157
157
  args.push('--mention', query.mention);
158
- if (query.grep !== undefined)
159
- args.push('--grep', query.grep);
160
- if (query.fixed !== undefined)
161
- args.push('--fixed', query.fixed);
158
+ if (query.search !== undefined)
159
+ args.push(query.search.mode === 'regex' ? '--grep' : '--fixed', query.search.text);
162
160
  if (query.order !== undefined)
163
161
  args.push('--order', query.order);
164
162
  return args;
@@ -169,10 +167,10 @@ export function renderHistoryResult(squarePath, data, options = {}) {
169
167
  return undefined;
170
168
  const query = options.query ?? {};
171
169
  const noTruncate = options.noTruncate === true;
172
- const pattern = query.grep ?? query.fixed;
173
- const output = pattern === undefined || pattern === ''
170
+ const search = query.search;
171
+ const output = search === undefined
174
172
  ? renderHistoryProjection(projection, projection.visibleActivities, noTruncate, squarePath)
175
- : renderGrepActivitiesView(projection.visibleActivities, projection.totalMatches, noTruncate, squarePath, pattern, query.fixed !== undefined, () => 'full');
173
+ : renderGrepActivitiesView(projection.visibleActivities, projection.totalMatches, noTruncate, squarePath, search.text, search.mode === 'literal', () => 'full');
176
174
  const next = projection.continuation;
177
175
  const base = options.continuationArgs ?? historyQueryArgs(query);
178
176
  const continuation = next === undefined ? '' : `\n\n${historyContinuationCommand(squarePath, [...base, `--${next.direction}`, next.cursor, '--limit', String(next.limit)])}`;
package/dist/ports.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { Act, ActivityId, Owner, HardCap, Seat, StoredAct, WakeAddress, WakeRoute, WatchLease } from './model.js';
2
2
  import type { AttemptOutcome } from './delivery-attempts.js';
3
3
  import type { MetadataMatch, DeliveryDeferralFact } from './delivery-deferral.js';
4
+ import type { ActivitySearch } from './square-facade.js';
4
5
  export type ActivityIndex = number;
5
6
  export interface SquareHeader {
6
7
  readonly revision: number;
@@ -49,6 +50,9 @@ export interface ActivityFilter {
49
50
  readonly actors?: readonly string[];
50
51
  readonly actorMatch?: 'identity' | 'exact';
51
52
  readonly afterAt?: number;
53
+ /** Shared query predicates, applied before paging or counting. */
54
+ readonly metadataMatch?: MetadataMatch;
55
+ readonly search?: ActivitySearch;
52
56
  }
53
57
  export interface IndexPage {
54
58
  readonly edge: 'oldest' | 'newest';
@@ -58,10 +62,7 @@ export interface FactPage<T> {
58
62
  readonly items: readonly T[];
59
63
  readonly hasMore: boolean;
60
64
  }
61
- export interface DirectedFilter {
62
- readonly afterIndex?: number;
63
- readonly beforeIndex?: number;
64
- readonly throughIndex?: number;
65
+ export interface DirectedFilter extends Omit<ActivityFilter, 'actors' | 'actorMatch'> {
65
66
  readonly unseenOnly?: true;
66
67
  readonly senders?: readonly string[];
67
68
  readonly attentionOnly?: true;
@@ -78,10 +79,6 @@ export interface ReceiptFacts {
78
79
  readonly latestAt?: number;
79
80
  readonly latestActorActivityIndex?: number;
80
81
  }
81
- export interface ReceiptAssignment {
82
- readonly owner: string;
83
- readonly index: ActivityIndex;
84
- }
85
82
  export type SeatRef = Pick<Seat, 'participant' | 'owner' | 'generation'>;
86
83
  export type ArtifactWriteAuthority = {
87
84
  readonly kind: 'send';
@@ -202,7 +199,6 @@ export interface SquareRead {
202
199
  readonly at: number;
203
200
  } | undefined;
204
201
  receiptFacts(requestedName: string, afterIndex: number): ReceiptFacts;
205
- receiptAssignments(): readonly ReceiptAssignment[];
206
202
  lease(requestedName: string): WatchLease | undefined;
207
203
  deliveryDeferrals(participant: string, generation: ActivityId): readonly DeliveryDeferralFact[];
208
204
  }
package/dist/search.js CHANGED
@@ -1,7 +1,6 @@
1
- // Shared activity-body search semantics for filtering and presentation.
2
- // Regex mode intentionally uses JavaScript's built-in engine: patterns come from
3
- // the local CLI user and are not treated as a hostile multi-tenant input. Use
4
- // --fixed for literal text; do not claim regex mode has ReDoS protection.
1
+ // Shared case-insensitive search semantics for storage filtering and presentation.
2
+ // Regex mode intentionally uses JavaScript's built-in engine, without a ReDoS
3
+ // timeout. Callers should use literal mode for plain text and avoid untrusted regexes.
5
4
  import { SquareError } from './model.js';
6
5
  export function compileSearchPattern(pattern, fixed) {
7
6
  if (fixed)
@@ -11,7 +10,7 @@ export function compileSearchPattern(pattern, fixed) {
11
10
  }
12
11
  catch (err) {
13
12
  const detail = err instanceof Error ? err.message.replace(/^Invalid regular expression:\s*/i, '') : 'invalid expression';
14
- throw new SquareError('invalid_args', `Invalid --grep regex: ${detail}`);
13
+ throw new SquareError('invalid_args', `Invalid search regex: ${detail}`);
15
14
  }
16
15
  }
17
16
  function compactWhitespace(text) {
@@ -34,7 +34,6 @@ export declare class SqliteSquare {
34
34
  join(requested: string, options: JoinOptions, control?: OperationControl): Promise<JoinResult>;
35
35
  /** Restore only a standing seat; a concurrent departure or takeover cannot be revived. */
36
36
  reattach(name: string, { owner, wakeAddress }: Pick<JoinOptions, 'owner' | 'wakeAddress'>, control?: OperationControl): Promise<Participant | null>;
37
- snapshot(control?: OperationControl): Promise<import("./square-facade.js").SquareSnapshot>;
38
37
  history(query?: HistoryQuery, control?: OperationControl): Promise<HistoryResult>;
39
38
  status(options?: StatusOptions, control?: OperationControl): Promise<StatusResult>;
40
39
  participants(options?: ObservationOptions, control?: OperationControl): Promise<ParticipantsResult>;