@astrosheep/square 0.3.70 → 0.3.71

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 (115) hide show
  1. package/dist/activity-feed.d.ts +4 -5
  2. package/dist/activity-feed.js +14 -10
  3. package/dist/artifact.d.ts +26 -33
  4. package/dist/artifact.js +346 -317
  5. package/dist/attention-presentation.js +2 -9
  6. package/dist/automatic-session.js +6 -6
  7. package/dist/boundary-presentation.js +3 -3
  8. package/dist/catch-decisions.d.ts +19 -12
  9. package/dist/catch-decisions.js +32 -68
  10. package/dist/claude-delivery.js +7 -7
  11. package/dist/claude-mod.js +18 -7
  12. package/dist/cli/catch-presentation.d.ts +7 -0
  13. package/dist/cli/catch-presentation.js +55 -0
  14. package/dist/cli/context.d.ts +1 -1
  15. package/dist/cli/context.js +5 -23
  16. package/dist/cli/express-presentation.d.ts +10 -0
  17. package/dist/cli/express-presentation.js +196 -0
  18. package/dist/cli/maintenance-commands.d.ts +4 -0
  19. package/dist/cli/maintenance-commands.js +29 -6
  20. package/dist/cli/observation-commands.d.ts +7 -5
  21. package/dist/cli/observation-commands.js +275 -262
  22. package/dist/cli/program.d.ts +1 -1
  23. package/dist/cli/program.js +43 -56
  24. package/dist/cli/registry.js +2 -1
  25. package/dist/cli/square-commands.d.ts +3 -9
  26. package/dist/cli/square-commands.js +134 -255
  27. package/dist/codex-queue.js +4 -1
  28. package/dist/decisions.d.ts +26 -74
  29. package/dist/decisions.js +190 -335
  30. package/dist/delivery-health.js +13 -22
  31. package/dist/delivery-operations.d.ts +0 -11
  32. package/dist/delivery-operations.js +44 -33
  33. package/dist/delivery.d.ts +33 -32
  34. package/dist/delivery.js +70 -81
  35. package/dist/help.js +6 -1
  36. package/dist/inbox.d.ts +27 -0
  37. package/dist/inbox.js +55 -18
  38. package/dist/index.d.ts +2 -4
  39. package/dist/index.js +1 -1
  40. package/dist/list.d.ts +7 -1
  41. package/dist/list.js +49 -122
  42. package/dist/mcp.d.ts +1 -1
  43. package/dist/mcp.js +7 -37
  44. package/dist/model.d.ts +4 -44
  45. package/dist/model.js +1 -3
  46. package/dist/notifications.d.ts +1 -1
  47. package/dist/notifications.js +13 -7
  48. package/dist/open-square.d.ts +16 -2
  49. package/dist/open-square.js +40 -1
  50. package/dist/perception-projection.d.ts +6 -15
  51. package/dist/perception-projection.js +6 -26
  52. package/dist/plugins/opencode/delivery.js +9 -9
  53. package/dist/plugins/opencode/index.js +3 -3
  54. package/dist/ports.d.ts +156 -11
  55. package/dist/presentation-operations.d.ts +1 -1
  56. package/dist/presentation-operations.js +35 -44
  57. package/dist/presentation.d.ts +12 -19
  58. package/dist/presentation.js +33 -54
  59. package/dist/routes.d.ts +7 -14
  60. package/dist/routes.js +33 -62
  61. package/dist/runtime.d.ts +12 -39
  62. package/dist/runtime.js +33 -164
  63. package/dist/square-actions.d.ts +11 -21
  64. package/dist/square-actions.js +171 -271
  65. package/dist/square-application.d.ts +31 -55
  66. package/dist/square-application.js +637 -141
  67. package/dist/square-core.d.ts +3 -94
  68. package/dist/square-core.js +8 -353
  69. package/dist/square-domain.d.ts +24 -0
  70. package/dist/square-domain.js +77 -0
  71. package/dist/square-facade.d.ts +360 -9
  72. package/dist/square-migration.d.ts +24 -0
  73. package/dist/square-migration.js +211 -0
  74. package/dist/square-projections.d.ts +2 -42
  75. package/dist/square-projections.js +1 -37
  76. package/dist/square-sqlite.d.ts +59 -0
  77. package/dist/square-sqlite.js +702 -0
  78. package/dist/stream.d.ts +3 -10
  79. package/dist/stream.js +81 -52
  80. package/dist/views.d.ts +27 -95
  81. package/dist/views.js +217 -87
  82. package/dist/wake-eligibility.d.ts +15 -5
  83. package/dist/wake-eligibility.js +32 -16
  84. package/dist/wakes.d.ts +3 -3
  85. package/dist/wakes.js +31 -27
  86. package/guides/application-facade-delivery-evidence.md +200 -0
  87. package/guides/application-facade-design.md +95 -0
  88. package/guides/change-waits.md +4 -4
  89. package/guides/migration.md +77 -0
  90. package/guides/primitive-catch-consolidation.md +85 -0
  91. package/guides/primitive-history-consolidation.md +76 -0
  92. package/guides/primitive-status-consolidation.md +87 -0
  93. package/guides/primitive-storage-consolidation.md +128 -0
  94. package/guides/row-runtime-delivery-evidence.md +160 -0
  95. package/guides/row-storage-design.md +567 -0
  96. package/package.json +1 -1
  97. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  98. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  99. package/plugins/opencode/delivery.ts +11 -11
  100. package/plugins/opencode/index.ts +3 -3
  101. package/plugins/paseo/client/square-message.tsx +70 -52
  102. package/plugins/paseo/shared/message.ts +15 -0
  103. package/plugins/pi/square-pi.js +3 -0
  104. package/dist/activity.d.ts +0 -14
  105. package/dist/activity.js +0 -248
  106. package/dist/square-file-adapter.d.ts +0 -26
  107. package/dist/square-file-adapter.js +0 -85
  108. package/dist/square-state.d.ts +0 -6
  109. package/dist/square-state.js +0 -138
  110. package/dist/square-storage.d.ts +0 -14
  111. package/dist/square-storage.js +0 -188
  112. package/dist/square-wiring.d.ts +0 -33
  113. package/dist/square-wiring.js +0 -109
  114. package/dist/watch.d.ts +0 -8
  115. package/dist/watch.js +0 -252
@@ -25,17 +25,13 @@ export function renderAttentionDescription({ actor, recipient, route }) {
25
25
  }
26
26
  }
27
27
  export function renderAttentionPreview(attention) {
28
- // The fence is what keeps the body from being re-rendered by Markdown. It grows past any
29
- // backtick run in the body so a body containing a fence cannot break out of this one.
30
- // Everything outside the fence is the system's voice, which participant bodies cannot fake.
31
- const fence = '`'.repeat(Math.max(3, longestBacktickRun(attention.body) + 1));
32
28
  return [
33
- `${fence}square-activity`,
29
+ '<square-activity>',
34
30
  `· ${displayAttentionPath(attention.squarePath)} · ${formatActivityId(attention.actIndex)}`,
35
31
  `● ${renderAttentionDescription(attention)}`,
36
32
  '',
37
33
  previewAttentionBody(attention.body),
38
- fence,
34
+ '</square-activity>',
39
35
  // A clipped body ends with the full command to read it all: a labeled caption, then the
40
36
  // command bare on its own line so it can be copied and run as it is.
41
37
  ...(attentionBodyIsClipped(attention.body)
@@ -43,6 +39,3 @@ export function renderAttentionPreview(attention) {
43
39
  : []),
44
40
  ].join('\n');
45
41
  }
46
- function longestBacktickRun(value) {
47
- return value.match(/`+/g)?.reduce((longest, run) => Math.max(longest, run.length), 0) ?? 0;
48
- }
@@ -1,9 +1,9 @@
1
1
  import { promises as fs } from 'node:fs';
2
2
  import path from 'node:path';
3
- import { openSquare } from './square-file-adapter.js';
3
+ import { openSquare } from './open-square.js';
4
4
  import { closeOpenSquare } from './open-square.js';
5
- import { Square } from './square-wiring.js';
6
- import { entryPresentation } from './views.js';
5
+ import { Square } from './square-application.js';
6
+ import { currentParticipant } from './views.js';
7
7
  import { automaticParticipant, harnessSessionSources } from './participant-identity.js';
8
8
  import { SquareError } from './model.js';
9
9
  import { projectSessionBindings } from './square-projections.js';
@@ -49,10 +49,10 @@ export async function automaticSessionStart(provider, sessionId, cwd, env = proc
49
49
  const name = automaticParticipant(provider, sessionId, env);
50
50
  const hostLedger = hostLedgerForEnv(env);
51
51
  let bindings;
52
- let entry;
52
+ let standing;
53
53
  try {
54
54
  bindings = await hostLedger.listPresence({ location: squarePath, participant: name });
55
- entry = await entryPresentation(reader, name);
55
+ standing = await currentParticipant(reader, name);
56
56
  }
57
57
  finally {
58
58
  await closeOpenSquare(reader);
@@ -60,7 +60,7 @@ export async function automaticSessionStart(provider, sessionId, cwd, env = proc
60
60
  if (bindings.some((binding) => binding.session !== sessionId)) {
61
61
  throw new SquareError('already_joined', `✕ ${name} already stands here — another session holds the name`);
62
62
  }
63
- if (entry.joined && !bindings.some((binding) => binding.session === sessionId)) {
63
+ if (standing !== undefined && !bindings.some((binding) => binding.session === sessionId)) {
64
64
  throw new SquareError('already_joined', `✕ ${name} already stands here — another session holds the name`);
65
65
  }
66
66
  const scopedEnv = operationEnv(provider, sessionId, env);
@@ -2,7 +2,7 @@ import { hostLedgerRoot } from './host-ledger-root.js';
2
2
  import { createHash } from 'node:crypto';
3
3
  import path from 'node:path';
4
4
  import { withFileLock } from './file-lock.js';
5
- import { openSquare } from './square-file-adapter.js';
5
+ import { openSquare } from './open-square.js';
6
6
  import { closeOpenSquare } from './open-square.js';
7
7
  import { presentPending } from './presentation-operations.js';
8
8
  import { hostLedgerForEnv } from './registry.js';
@@ -117,8 +117,8 @@ async function presentPendingAtBoundaryUnlocked(sessionId, present, lookup = ses
117
117
  // A stale projection can outlive its activity; still surface the bounded preview,
118
118
  // but there is no artifact observation to commit.
119
119
  if (!outcome.presented) {
120
- const snapshot = await square.artifact.read(signal).catch(() => undefined);
121
- if (snapshot?.state.acts.every((activity) => activity.index !== index))
120
+ const exists = await square.artifact.read((facts) => facts.activity(index) !== undefined, signal).catch(() => undefined);
121
+ if (exists?.value === false)
122
122
  await renderOnce();
123
123
  }
124
124
  });
@@ -1,17 +1,24 @@
1
- import { type Perception } from './square-core.js';
2
- import { type PerceptionProjection } from './perception-projection.js';
3
- import { type SquareState, type StoredAct } from './model.js';
1
+ import { type ActivityId } from './square-core.js';
2
+ import { type StoredAct } from './model.js';
3
+ import type { DirectedFilter, SquareRead, SquareWrite } from './ports.js';
4
4
  import type { CatchOptions } from './square-facade.js';
5
- export interface CatchDecision {
6
- readonly viewer: string;
7
- readonly delivered: readonly StoredAct[];
8
- readonly perceptions: ReadonlyMap<number, Perception>;
9
- readonly consumedThrough: string | null;
10
- readonly changed: boolean;
5
+ export interface CatchSelection {
6
+ readonly participant: string;
7
+ readonly activities: readonly StoredAct[];
8
+ readonly consumedThrough: ActivityId | null;
11
9
  readonly remaining: number;
10
+ readonly sayNumbers: Readonly<Record<number, number>>;
11
+ readonly standingCount: number;
12
+ readonly held: boolean;
13
+ readonly now: number;
14
+ readonly ownActivityCount: number;
15
+ readonly hardCap: number | null;
16
+ readonly terminalStatus: 'capped' | 'quorum' | undefined;
12
17
  }
18
+ /** Capture a selected page (or an empty completion) without opening another resource. */
19
+ export declare function observeCatchSelection(facts: SquareRead, participant: string, activities: readonly StoredAct[], filter: DirectedFilter, at: number): CatchSelection;
13
20
  export declare const CATCH_DEFAULT_LIMIT = 10;
14
21
  export declare const CATCH_MAX_LIMIT = 100;
15
- export type CatchProjection = Pick<PerceptionProjection, 'cursorFor' | 'directedTo' | 'perceive' | 'isSeen'>;
16
- /** Pure catch selection and acknowledgement over one SquareState snapshot. */
17
- export declare function decideCatch(state: SquareState, requestedName: string, options: CatchOptions, at: number, project?: (state: SquareState) => CatchProjection): CatchDecision;
22
+ export declare function catchLimit(value: number | undefined): number;
23
+ /** Selection, receipts and resulting cursor belong to one artifact write transaction. */
24
+ export declare function decideCatch(facts: SquareWrite, requestedName: string, options: CatchOptions, at: number): CatchSelection;
@@ -1,88 +1,52 @@
1
- import { activityAuthors, audienceOf, formatActivityId, parseActivityId } from './square-core.js';
2
- import { derivePerceptionProjection } from './perception-projection.js';
3
- import { recordObservation } from './runtime.js';
4
- import { matchesCatchSelection } from './catch-selection.js';
5
- import { participantIdentity } from './participant-identity.js';
6
- import { SquareError, validateName } from './model.js';
7
- import { resolveRosterName } from './runtime.js';
1
+ import { formatActivityId, parseActivityId } from './square-core.js';
2
+ import { inSquareCount, readCursor, watchTerminalStatus } from './runtime.js';
3
+ import { SquareError } from './model.js';
4
+ import { resolveKnownName } from './decisions.js';
5
+ /** Capture a selected page (or an empty completion) without opening another resource. */
6
+ export function observeCatchSelection(facts, participant, activities, filter, at) {
7
+ const cursor = readCursor(facts, participant);
8
+ return {
9
+ participant, activities, consumedThrough: cursor < 0 ? null : formatActivityId(cursor),
10
+ remaining: facts.directedCount(participant, filter),
11
+ sayNumbers: activities.length === 0 ? {} : facts.sayOrdinals(activities.map((item) => item.index)),
12
+ standingCount: inSquareCount(facts), held: facts.hold().active, now: at,
13
+ ownActivityCount: facts.participant(participant).sayCount, hardCap: facts.header().hardCap,
14
+ terminalStatus: watchTerminalStatus(facts, participant),
15
+ };
16
+ }
8
17
  export const CATCH_DEFAULT_LIMIT = 10;
9
18
  export const CATCH_MAX_LIMIT = 100;
10
- function catchLimit(value) {
19
+ export function catchLimit(value) {
11
20
  const limit = value ?? CATCH_DEFAULT_LIMIT;
12
21
  if (!Number.isSafeInteger(limit) || limit < 1 || limit > CATCH_MAX_LIMIT) {
13
22
  throw new SquareError('invalid_args', `Catch limit must be a positive integer no greater than ${CATCH_MAX_LIMIT}.`);
14
23
  }
15
24
  return limit;
16
25
  }
17
- function resolveCatchName(state, requestedName) {
18
- validateName(requestedName);
19
- const known = resolveRosterName(state, requestedName);
20
- if (known === undefined) {
21
- throw new SquareError('invalid_args', `✕ ${participantIdentity(requestedName)} has never stepped into this square`, { reason: 'never_joined' });
22
- }
23
- return known;
24
- }
25
- /** Pure catch selection and acknowledgement over one SquareState snapshot. */
26
- export function decideCatch(state, requestedName, options, at, project = derivePerceptionProjection) {
27
- const viewer = resolveCatchName(state, requestedName);
26
+ /** Selection, receipts and resulting cursor belong to one artifact write transaction. */
27
+ export function decideCatch(facts, requestedName, options, at) {
28
+ const viewer = resolveKnownName(facts, requestedName);
28
29
  const limit = catchLimit(options.limit);
29
- const delivery = project(state);
30
+ let delivered;
31
+ let filter = { unseenOnly: true };
30
32
  if (options.id !== undefined) {
31
- if (['idle', 'from', 'mention', 'limit'].some((key) => Object.hasOwn(options, key))) {
33
+ if (['idle', 'from', 'mention', 'limit'].some((key) => Object.hasOwn(options, key)))
32
34
  throw new SquareError('invalid_args', 'Catch id cannot be combined with idle, from, mention or limit.');
33
- }
34
35
  const index = parseActivityId(options.id);
35
36
  if (index === undefined)
36
37
  throw new SquareError('invalid_args', 'Invalid catch id: expected an activity id like act/12.');
37
- const activity = state.acts.find((item) => item.index === index);
38
- if (activity === undefined || activity.kind !== 'say' || !delivery.directedTo(activity, viewer)) {
38
+ const activity = facts.activity(index);
39
+ if (activity === undefined || activity.kind !== 'say' || facts.directedAt(viewer, index) === undefined) {
39
40
  throw new SquareError('invalid_args', `✕ ${formatActivityId(index)} isn't addressed to you`, {
40
- reason: 'not_addressed',
41
- activityId: formatActivityId(index),
42
- activityExists: activity !== undefined,
41
+ reason: 'not_addressed', activityId: formatActivityId(index), activityExists: activity !== undefined,
43
42
  });
44
43
  }
45
- const perception = delivery.perceive(activity, viewer);
46
- const changed = recordObservation(state, viewer, index, 'seen', at);
47
- const cursor = delivery.cursorFor(viewer);
48
- return {
49
- viewer,
50
- delivered: [activity],
51
- perceptions: new Map([[index, perception]]),
52
- consumedThrough: cursor < 0 ? null : formatActivityId(cursor),
53
- changed,
54
- remaining: state.acts.filter((item) => item.kind === 'say' && delivery.directedTo(item, viewer) && !delivery.isSeen(viewer, item.index)).length,
55
- };
56
- }
57
- const filter = { participants: options.from, mention: options.mention === true ? viewer : undefined };
58
- const authors = activityAuthors(state.acts);
59
- const delivered = [];
60
- const perceptions = new Map();
61
- const matching = [];
62
- for (const activity of state.acts) {
63
- if (activity.kind !== 'say' || !delivery.directedTo(activity, viewer))
64
- continue;
65
- const replyAuthor = activity.reply === undefined ? undefined : authors.get(activity.reply);
66
- if (!matchesCatchSelection(activity.actor, audienceOf(activity), filter, replyAuthor))
67
- continue;
68
- if (delivery.isSeen(viewer, activity.index))
69
- continue;
70
- matching.push(activity);
44
+ delivered = [activity];
71
45
  }
72
- delivered.push(...matching.slice(0, limit));
73
- for (const activity of delivered)
74
- perceptions.set(activity.index, delivery.perceive(activity, viewer));
75
- let changed = false;
76
- for (const activity of delivered) {
77
- changed = recordObservation(state, viewer, activity.index, 'seen', at) || changed;
46
+ else {
47
+ filter = { unseenOnly: true, ...(options.from === undefined ? {} : { senders: options.from }), ...(options.mention === true ? { attentionOnly: true } : {}) };
48
+ delivered = facts.activities(facts.directed(viewer, filter, { edge: 'oldest', limit }).items.map((item) => item.index));
78
49
  }
79
- const consumed = delivery.cursorFor(viewer);
80
- return {
81
- viewer,
82
- delivered,
83
- perceptions,
84
- consumedThrough: consumed < 0 ? null : formatActivityId(consumed),
85
- changed,
86
- remaining: Math.max(0, matching.length - delivered.length),
87
- };
50
+ facts.markSeen(viewer, delivered.map((item) => item.index), at);
51
+ return observeCatchSelection(facts, viewer, delivered, filter, at);
88
52
  }
@@ -1,8 +1,9 @@
1
1
  import { stat } from 'node:fs/promises';
2
- import { openSquare } from './square-file-adapter.js';
2
+ import { openSquare } from './open-square.js';
3
3
  import { closeOpenSquare } from './open-square.js';
4
4
  import { hostLedgerForEnv } from './registry.js';
5
- import { nativePendingPreview, projectPresentationEvidence, presentationSuppressesWake } from './square-projections.js';
5
+ import { projectPresentationEvidence, presentationSuppressesWake } from './square-projections.js';
6
+ import { nativePendingPreview } from './inbox.js';
6
7
  import { presentPending } from './presentation-operations.js';
7
8
  import { canonicalPath } from './canonical-path.js';
8
9
  import { parseActivityId } from './square-core.js';
@@ -54,8 +55,7 @@ async function currentDelivery(row, hostLedger) {
54
55
  return undefined;
55
56
  const square = await openSquare(owner.location, { hostLedger });
56
57
  try {
57
- const state = (await square.artifact.read()).state;
58
- const preview = nativePendingPreview(state, { location: row.location, routeKind: 'claude-native', sessionId: row.session, participant: row.participant, actIndex: index, epoch: native.epoch, address: { endpoint: native.endpoint }, cancelledThrough: owner.cancelledThrough, now: Date.now() });
58
+ const { value: preview } = await square.artifact.read((facts) => nativePendingPreview(facts, { location: row.location, routeKind: 'claude-native', sessionId: row.session, participant: row.participant, actIndex: index, epoch: native.epoch, address: { endpoint: native.endpoint }, cancelledThrough: owner.cancelledThrough, now: Date.now() }));
59
59
  if (!preview || nativePayload(row.claimToken, preview.payload) !== native.payload)
60
60
  return undefined;
61
61
  return { cancelledThrough: owner.cancelledThrough, clipped: preview.clipped };
@@ -82,7 +82,7 @@ async function dispatchClaude(request, hostLedger, timeoutMs, beforeSend, env =
82
82
  let payload;
83
83
  try {
84
84
  const index = parseActivityId(request.activity);
85
- const preview = index === undefined ? undefined : nativePendingPreview((await square.artifact.read()).state, { location: request.location, routeKind: 'claude-native', sessionId: request.route.sessionId, participant: request.participant, actIndex: index, epoch: request.route.epoch, address: { endpoint: request.route.address.endpoint }, cancelledThrough: owner.cancelledThrough, now: Date.now() });
85
+ const preview = index === undefined ? undefined : (await square.artifact.read((facts) => nativePendingPreview(facts, { location: request.location, routeKind: 'claude-native', sessionId: request.route.sessionId, participant: request.participant, actIndex: index, epoch: request.route.epoch, address: { endpoint: request.route.address.endpoint }, cancelledThrough: owner.cancelledThrough, now: Date.now() }))).value;
86
86
  if (!preview)
87
87
  return { outcome: 'not-capable', diagnostic: 'Catch or consumption owns this activity.' };
88
88
  payload = nativePayload(request.claimToken, preview.payload);
@@ -127,11 +127,11 @@ export async function observeClaudeDelivery(sessionId, text, operation, env = pr
127
127
  continue;
128
128
  const square = await openSquare(row.location, { hostLedger: ledger, env });
129
129
  try {
130
- await presentPending({ artifact: square.artifact, hostLedger: ledger, location: row.location, participant: row.participant, session: sessionId, activity: row.activity, sink: { present() { } }, markSeen: !pending.clipped, current: (state) => {
130
+ await presentPending({ artifact: square.artifact, hostLedger: ledger, location: row.location, participant: row.participant, session: sessionId, activity: row.activity, sink: { present() { } }, markSeen: !pending.clipped, current: (facts) => {
131
131
  const index = parseActivityId(row.activity);
132
132
  if (index === undefined)
133
133
  return false;
134
- const preview = nativePendingPreview(state, { location: row.location, routeKind: 'claude-native', sessionId: row.session, participant: row.participant, actIndex: index, epoch: native.epoch, address: { endpoint: native.endpoint }, cancelledThrough: pending.cancelledThrough, now: Date.now() });
134
+ const preview = nativePendingPreview(facts, { location: row.location, routeKind: 'claude-native', sessionId: row.session, participant: row.participant, actIndex: index, epoch: native.epoch, address: { endpoint: native.endpoint }, cancelledThrough: pending.cancelledThrough, now: Date.now() });
135
135
  return preview !== undefined && nativePayload(row.claimToken, preview.payload) === native.payload;
136
136
  } });
137
137
  }
@@ -1,9 +1,9 @@
1
1
  import { automaticSessionStart, operationEnv } from './automatic-session.js';
2
2
  import { claimSessionParticipant, hostLedgerForEnv } from './registry.js';
3
- import { openSquare } from './square-file-adapter.js';
3
+ import { openSquare } from './open-square.js';
4
4
  import { closeOpenSquare } from './open-square.js';
5
5
  import { publishWakeRoute, retireWakeRouteFromArtifact } from './routes.js';
6
- import { Square } from './square-wiring.js';
6
+ import { Square } from './square-application.js';
7
7
  import { observeClaudeDelivery } from './claude-delivery.js';
8
8
  import { processActNotificationsOnce } from './notifications.js';
9
9
  import { sweepPending } from './delivery-operations.js';
@@ -61,8 +61,9 @@ export async function runClaudeMod(inputText, env = process.env) {
61
61
  const probe = await openSquare(row.location, { hostLedger: ledger, env: scoped, signal });
62
62
  let eligible = false;
63
63
  try {
64
- const state = (await probe.artifact.read(signal)).state;
65
- const last = state.acts.findLast((activity) => activity.actor === row.participant && ['join', 'done', 'kick'].includes(activity.kind));
64
+ const { value: last } = await probe.artifact.read((facts) => facts.activityHeads({
65
+ actors: [row.participant], actorMatch: 'exact', kinds: ['join', 'done'],
66
+ }, { edge: 'newest', limit: 1 }).items[0], signal);
66
67
  eligible = last?.kind === 'done' && `act/${last.index}` === row.activity;
67
68
  }
68
69
  finally {
@@ -111,8 +112,7 @@ export async function runClaudeMod(inputText, env = process.env) {
111
112
  await ledger.ensurePresence({ ...current, epoch });
112
113
  const square = await openSquare(binding.location, { hostLedger: ledger, env: scoped, signal });
113
114
  try {
114
- const state = (await square.artifact.read(signal)).state;
115
- const existing = state.routes?.find((route) => route.participant === binding.participant && route.sessionId === input.sessionId && route.kind === 'claude-native' && route.epoch === epoch);
115
+ const { value: existing } = await square.artifact.read((facts) => facts.routes().find((route) => route.participant === binding.participant && route.sessionId === input.sessionId && route.kind === 'claude-native' && route.epoch === epoch), signal);
116
116
  if (!supported) {
117
117
  if (existing)
118
118
  await retireWakeRouteFromArtifact(square.artifact, existing, { expectedEpoch: epoch });
@@ -120,7 +120,18 @@ export async function runClaudeMod(inputText, env = process.env) {
120
120
  }
121
121
  const cutoff = cancelled?.epoch === epoch || (lateCancelledJoin && current.epoch === binding.epoch && (current.updatedAt ?? Infinity) <= input.cancelAt) ? input.cancelAt : undefined;
122
122
  if (cutoff !== undefined) {
123
- const through = state.acts.filter((activity) => activity.at <= cutoff).reduce((highest, activity) => Math.max(highest, activity.index), -1);
123
+ const { value: through } = await square.artifact.read((facts) => {
124
+ let beforeIndex;
125
+ for (;;) {
126
+ const page = facts.activityHeads({ beforeIndex }, { edge: 'newest', limit: 100 });
127
+ const last = [...page.items].reverse().find((activity) => activity.at <= cutoff);
128
+ if (last !== undefined)
129
+ return last.index;
130
+ if (!page.hasMore)
131
+ return -1;
132
+ beforeIndex = page.items[0].index;
133
+ }
134
+ }, signal);
124
135
  await ledger.suppressPresence({ ...current, epoch }, through);
125
136
  }
126
137
  if (!existing || existing.address.endpoint !== input.endpoint)
@@ -0,0 +1,7 @@
1
+ import type { ApplicationOutcome, CatchApplicationData, CatchApplicationOptions, CatchProjection, OperationProgress } from '../square-facade.js';
2
+ export declare function renderCatchProgress(squarePath: string, progress: OperationProgress): string | undefined;
3
+ /** Render settled catch facts; no selection, terminal decisions, receipts or wait machinery. */
4
+ export declare function renderCatchOutcome(squarePath: string, outcome: ApplicationOutcome<CatchApplicationData, CatchProjection>, options?: CatchApplicationOptions): {
5
+ stdout: string;
6
+ exitCode: number;
7
+ };
@@ -0,0 +1,55 @@
1
+ import { parseActivityId } from '../square-application.js';
2
+ import { participantCommandPrefix, renderAmbientEvent, renderWatchAlreadyActive, renderWatchForceTakeover, renderWatchReplaceMissing, renderWatchReplaced, renderWatchStatus, withPathOutput, } from '../presentation.js';
3
+ export function renderCatchProgress(squarePath, progress) {
4
+ if (progress.kind !== 'catch-started' || !progress.replacementRequested)
5
+ return undefined;
6
+ const options = { squarePath, name: progress.participant };
7
+ return (progress.replaced
8
+ ? renderWatchForceTakeover(options) : renderWatchReplaceMissing(options)) + '\n';
9
+ }
10
+ function continuationCommand(squarePath, name, options) {
11
+ const args = ['--now'];
12
+ if (options.from?.length)
13
+ args.push('--from', options.from.join(','));
14
+ if (options.mention)
15
+ args.push('--mention');
16
+ if (options.limit !== undefined)
17
+ args.push('--limit', String(options.limit));
18
+ return `${participantCommandPrefix(squarePath, name)} catch ${args.join(' ')}`;
19
+ }
20
+ /** Render settled catch facts; no selection, terminal decisions, receipts or wait machinery. */
21
+ export function renderCatchOutcome(squarePath, outcome, options = {}) {
22
+ const { data, projection } = outcome;
23
+ const location = projection.location ?? squarePath;
24
+ const name = projection.participant;
25
+ const stop = projection.stop ?? data.status;
26
+ const header = { participantCount: projection.standingCount, held: projection.held };
27
+ const statusOptions = {
28
+ squarePath: location, name, presence: projection.presence,
29
+ showCatchHint: !projection.automaticDeliveryAvailable,
30
+ ownActivityCount: projection.ownActivityCount, hardCap: projection.hardCap,
31
+ };
32
+ if (stop === 'already-active' || stop === 'replaced') {
33
+ return {
34
+ stdout: withPathOutput(location, stop === 'already-active'
35
+ ? renderWatchAlreadyActive(statusOptions) : renderWatchReplaced(statusOptions), header),
36
+ exitCode: stop === 'already-active' ? 1 : 0,
37
+ };
38
+ }
39
+ const rendered = data.activities.map((activity) => renderAmbientEvent(activity, name, {
40
+ now: projection.now, actNumber: projection.sayNumbers[parseActivityId(activity.id)],
41
+ perception: activity.perception, mention: options.mention ? name : undefined,
42
+ })).filter(Boolean).join('\n\n');
43
+ const remaining = data.remaining === 0 ? ''
44
+ : `○ ${data.remaining} matching ${data.remaining === 1 ? 'activity remains' : 'activities remain'}\n${continuationCommand(location, name, options)}`;
45
+ const terminal = stop === undefined ? '' : renderWatchStatus({
46
+ ...statusOptions, status: stop,
47
+ ...(stop === 'stale' && options.idle !== undefined ? { idleMs: options.idle } : {}),
48
+ });
49
+ const fallback = stop === undefined && !projection.automaticDeliveryAvailable && data.remaining === 0
50
+ ? `${participantCommandPrefix(location, name)} catch --idle 30m\n stay available for new activity` : '';
51
+ return {
52
+ stdout: withPathOutput(location, [terminal, rendered, remaining, fallback].filter(Boolean).join('\n\n'), header),
53
+ exitCode: stop === 'capped' ? 1 : 0,
54
+ };
55
+ }
@@ -23,7 +23,6 @@ export declare function parseBoundedLimit(value: string | undefined, flag: strin
23
23
  export declare function parseNonNegativeInteger(value: string, flag: string): number;
24
24
  export declare function parseHardCap(value: string): HardCap;
25
25
  export declare function parseNameList(value: string, flag: string): string[];
26
- export declare function requireParticipant(name: string | undefined): string;
27
26
  export interface ParsedGlobalArgs {
28
27
  squarePath?: string;
29
28
  explicitSquarePath: boolean;
@@ -34,4 +33,5 @@ export interface ParsedGlobalArgs {
34
33
  export declare function parseGlobalArgs(rawArgs: string[]): Promise<ParsedGlobalArgs>;
35
34
  export declare function locationIsRequired(command: string): boolean;
36
35
  export declare function defaultContext(command: string, squarePath?: string, name?: string): CommandContext;
36
+ export declare function applicationFor(context: CommandContext): import("../square-facade.js").SquareApplication;
37
37
  export declare function requireSquarePath(context: CommandContext): string;
@@ -1,9 +1,7 @@
1
1
  import os from 'node:os';
2
2
  import { commandUsageHint } from '../help.js';
3
3
  import { parseParticipantList, validateName } from '../model.js';
4
- import { createHostLedgerPort } from '../host-ledger-file-adapter.js';
5
- import { projectSessionBindings } from '../square-projections.js';
6
- import { sessionIdsFromEnvironment } from '../participant-identity.js';
4
+ import { createSquareApplication } from '../square-application.js';
7
5
  export async function readStdin() {
8
6
  try {
9
7
  let content = '';
@@ -87,12 +85,6 @@ export function parseNameList(value, flag) {
87
85
  validateName(name);
88
86
  return names;
89
87
  }
90
- export function requireParticipant(name) {
91
- if (!name)
92
- fail('✕ missing required option: --as <name>');
93
- validateName(name);
94
- return name;
95
- }
96
88
  const LOCATION_REQUIRED_COMMANDS = new Set([
97
89
  'build',
98
90
  'stream',
@@ -110,6 +102,7 @@ const LOCATION_REQUIRED_COMMANDS = new Set([
110
102
  'participants',
111
103
  'mcp-server',
112
104
  'doctor',
105
+ 'migrate',
113
106
  ]);
114
107
  export async function parseGlobalArgs(rawArgs) {
115
108
  const args = [...rawArgs];
@@ -128,11 +121,6 @@ export async function parseGlobalArgs(rawArgs) {
128
121
  }
129
122
  }
130
123
  const command = args[0];
131
- if (name === undefined) {
132
- const configured = process.env.SQUARE_PARTICIPANT_NAME?.trim();
133
- if (configured)
134
- name = configured;
135
- }
136
124
  if (name !== undefined)
137
125
  validateName(name);
138
126
  const explicitSquarePath = requestedPath !== undefined;
@@ -142,15 +130,6 @@ export async function parseGlobalArgs(rawArgs) {
142
130
  fail(`✕ ${command} needs a square location\nsquare ls`);
143
131
  }
144
132
  const squarePath = requestedPath ?? configured;
145
- if (name === undefined && squarePath !== undefined && command !== undefined && locationIsRequired(command)) {
146
- const bindings = (await Promise.all(sessionIdsFromEnvironment().map((sessionId) => projectSessionBindings({
147
- hostLedger: createHostLedgerPort(),
148
- location: squarePath,
149
- sessionId,
150
- })))).flat();
151
- const names = new Set(bindings.map((binding) => binding.participant));
152
- name = names.size === 1 ? [...names][0] : undefined;
153
- }
154
133
  return { squarePath, explicitSquarePath: explicitSquarePath || configured !== undefined, multipleSquares: false, name, args };
155
134
  }
156
135
  export function locationIsRequired(command) {
@@ -159,6 +138,9 @@ export function locationIsRequired(command) {
159
138
  export function defaultContext(command, squarePath, name) {
160
139
  return { command, cwd: process.cwd(), env: { ...process.env }, squarePath, name, homeDir: os.homedir() };
161
140
  }
141
+ export function applicationFor(context) {
142
+ return createSquareApplication({ cwd: context.cwd, env: context.env, squarePath: context.squarePath, participant: context.name });
143
+ }
162
144
  export function requireSquarePath(context) {
163
145
  if (context.squarePath === undefined)
164
146
  fail(`✕ ${context.command} needs a square location\nsquare ls`);
@@ -0,0 +1,10 @@
1
+ import type { ApplicationOutcome, ExpressApplicationData, ExpressApplicationOptions, ExpressProjection, SquareApplication } from '../square-facade.js';
2
+ export interface ActivityOptions extends Omit<ExpressApplicationOptions, 'reply'> {
3
+ readonly reply?: string;
4
+ }
5
+ export declare function saveActivityDraft(squarePath: string, name: string, body: string, now?: number): string;
6
+ /** Retry choices are transport syntax, never a second admission decision. */
7
+ export declare function activityRetryCommand(squarePath: string, name: string, opts: ActivityOptions, force?: boolean): string;
8
+ export declare function renderExpression(squarePath: string, outcome: ApplicationOutcome<ExpressApplicationData, ExpressProjection>): string;
9
+ /** Body adaptation, terminal IO and drafts only; the application owns every attempt and wait. */
10
+ export declare function executeExpression(application: SquareApplication, squarePath: string, requestedName: string | undefined, rawInput: string, opts: ActivityOptions): Promise<void>;