@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
@@ -1,9 +1,9 @@
1
1
  import { nameKey } from '../../model.js';
2
- import { observeSessionPending, sessionInbox } from '../../inbox.js';
2
+ import { nativePendingPreview, observeSessionPending, sessionInbox } from '../../inbox.js';
3
3
  import { hostLedgerForEnv } from '../../registry.js';
4
- import { openSquare } from '../../square-file-adapter.js';
4
+ import { openSquare } from '../../open-square.js';
5
5
  import { closeOpenSquare } from '../../open-square.js';
6
- import { nativePendingPreview, presentationSuppressesWake, projectPresentationEvidence } from '../../square-projections.js';
6
+ import { presentationSuppressesWake, projectPresentationEvidence } from '../../square-projections.js';
7
7
  import { presentPending } from '../../presentation-operations.js';
8
8
  import { deliverPending } from '../../delivery-operations.js';
9
9
  import { createWakeTransport } from '../../notifications.js';
@@ -13,8 +13,8 @@ import { createNativeInputId, sendNativeText } from '../../packages/agent-delive
13
13
  const routeKind = 'opencode-server';
14
14
  const key = (location, participant, index) => `${location}\0${nameKey(participant)}\0${index}`;
15
15
  /** The one native pending preview: the audience/catch/epoch fence before send and inside presentation. */
16
- function preview(state, owner, index) {
17
- return nativePendingPreview(state, { location: owner.location, routeKind, sessionId: owner.session, participant: owner.participant, actIndex: index, epoch: owner.epoch, address: { sessionId: owner.session }, cancelledThrough: owner.cancelledThrough, now: Date.now() });
16
+ function preview(facts, owner, index) {
17
+ return nativePendingPreview(facts, { location: owner.location, routeKind, sessionId: owner.session, participant: owner.participant, actIndex: index, epoch: owner.epoch, address: { sessionId: owner.session }, cancelledThrough: owner.cancelledThrough, now: Date.now() });
18
18
  }
19
19
  function createOpenCodeWakeTransport(target, env, lifetime) {
20
20
  const ledger = hostLedgerForEnv(env);
@@ -36,14 +36,14 @@ function createOpenCodeWakeTransport(target, env, lifetime) {
36
36
  return undefined;
37
37
  const square = await openSquare(owner.location, { hostLedger: ledger, env, signal });
38
38
  try {
39
- const prepared = preview((await square.artifact.read(signal)).state, owner, index);
39
+ const { value: prepared } = await square.artifact.read((facts) => preview(facts, owner, index), signal);
40
40
  if (!prepared)
41
41
  return undefined;
42
42
  const row = { location: owner.location, participant: owner.participant, session: target.sessionId,
43
43
  activity: request.activity, kind: 'wake', outcome: 'dispatching', routeKind, attemptN: request.attemptN, claimToken: request.claimToken,
44
44
  nativeDelivery: { harness: 'opencode', epoch: owner.epoch ?? 0, inputId: createNativeInputId(), payload: prepared.payload } };
45
45
  if (!await ledger.prepareNativeWake(row) || !await (beforeSend?.() ?? Promise.resolve(true)) || signal.aborted
46
- || preview((await square.artifact.read(signal)).state, owner, index)?.payload !== prepared.payload)
46
+ || (await square.artifact.read((facts) => preview(facts, owner, index)?.payload, signal)).value !== prepared.payload)
47
47
  return undefined;
48
48
  return { sending: sendNativeText(target, prepared.payload, { inputId: row.nativeDelivery.inputId, delivery: 'steer',
49
49
  timeoutMs: Math.max(1, deadline - Date.now()), signal }) };
@@ -82,7 +82,7 @@ export async function observeOpenCodeContext(sessionId, messages, env, signal) {
82
82
  continue;
83
83
  const square = await openSquare(row.location, { hostLedger: ledger, env, signal });
84
84
  try {
85
- const prepared = preview((await square.artifact.read(signal)).state, owner, index);
85
+ const { value: prepared } = await square.artifact.read((facts) => preview(facts, owner, index), signal);
86
86
  if (!prepared || prepared.payload !== row.nativeDelivery.payload)
87
87
  continue;
88
88
  const prior = await projectPresentationEvidence({ hostLedger: ledger, location: row.location, participant: row.participant, sessionId, activity: row.activity });
@@ -90,7 +90,7 @@ export async function observeOpenCodeContext(sessionId, messages, env, signal) {
90
90
  continue;
91
91
  await presentPending({ artifact: square.artifact, hostLedger: ledger, location: row.location, participant: row.participant, session: sessionId,
92
92
  activity: row.activity, sink: { present() { } }, markSeen: !prepared.clipped, signal,
93
- current: (state) => !signal.aborted && preview(state, owner, index)?.payload === row.nativeDelivery.payload });
93
+ current: (facts) => !signal.aborted && preview(facts, owner, index)?.payload === row.nativeDelivery.payload });
94
94
  }
95
95
  finally {
96
96
  await closeOpenSquare(square);
@@ -1,9 +1,9 @@
1
1
  import { automaticSessionStart, operationEnv } from '../../automatic-session.js';
2
2
  import { hostLedgerForEnv } from '../../registry.js';
3
3
  import { canonicalPath } from '../../canonical-path.js';
4
- import { openSquare } from '../../square-file-adapter.js';
4
+ import { openSquare } from '../../open-square.js';
5
5
  import { closeOpenSquare } from '../../open-square.js';
6
- import { Square } from '../../square-wiring.js';
6
+ import { Square } from '../../square-application.js';
7
7
  import { connectNative } from '../../packages/agent-delivery/src/opencode-native.js';
8
8
  import { observeOpenCodeContext, publishOpenCodeRoutes, receiveOpenCodePending } from './delivery.js';
9
9
  /** Actual OpenCode 2.0.20 server plugin. Harness identity belongs to ctx, never process.env. */
@@ -81,7 +81,7 @@ export default {
81
81
  for (const owner of (await ledger.listPresence({ session: sessionId })).filter((binding) => binding.channel === 'opencode')) {
82
82
  const square = await openSquare(owner.location, { hostLedger: ledger, env, signal: lifetime.signal });
83
83
  try {
84
- await ledger.suppressPresence(owner, (await square.artifact.read(lifetime.signal)).state.runtime.nextActIndex - 1);
84
+ await ledger.suppressPresence(owner, (await square.artifact.read((facts) => facts.header().nextActIndex - 1, lifetime.signal)).value);
85
85
  }
86
86
  finally {
87
87
  await closeOpenSquare(square);
package/dist/ports.d.ts CHANGED
@@ -1,16 +1,161 @@
1
- import type { InboxNotification, SquareState, StoredAct, WakeRoute, WatchLease } from './model.js';
1
+ import type { Act, HardCap, InboxNotification, StoredAct, WakeRoute, WakeRouteKind, WatchLease } from './model.js';
2
2
  export type { ClaimResult, EvidenceClaim, EvidenceRelease, EvidenceGc, EvidenceLookup, EvidenceRecord, HostLedgerPort, PresenceKey, PresenceLookup, PresenceRecord, PresenceClaimResult, PresenceChannel, PresenceResult, } from './host-ledger.js';
3
3
  import type { HostLedgerPort } from './host-ledger.js';
4
- /** Atomic artifact access. Never exposes framing, compression, locks, or storage schema. */
4
+ export type ActivityIndex = number;
5
+ export interface SquareHeader {
6
+ readonly revision: number;
7
+ readonly nextActIndex: number;
8
+ readonly lastActIndex: number;
9
+ readonly hardCap: HardCap;
10
+ readonly throttlePerMinute?: number;
11
+ readonly preamble: readonly string[];
12
+ readonly warmup: readonly string[];
13
+ }
14
+ export interface ParticipantFact {
15
+ readonly name: string;
16
+ readonly firstIndex: number;
17
+ readonly joined: boolean;
18
+ readonly done: boolean;
19
+ readonly sayCount: number;
20
+ readonly lastActiveAt?: number;
21
+ readonly lastJoinIndex?: number;
22
+ }
23
+ export interface AttentionFact {
24
+ readonly listener: string;
25
+ readonly target: string;
26
+ readonly kind: 'listen' | 'ignore';
27
+ readonly sinceIndex: number;
28
+ }
29
+ export interface ActivityHead {
30
+ readonly index: ActivityIndex;
31
+ readonly at: number;
32
+ readonly kind: StoredAct['kind'];
33
+ readonly actor?: string;
34
+ readonly reach?: 'bell';
35
+ readonly reply?: ActivityIndex;
36
+ }
37
+ export interface ActivityFilter {
38
+ readonly afterIndex?: number;
39
+ readonly beforeIndex?: number;
40
+ readonly throughIndex?: number;
41
+ readonly indexes?: readonly ActivityIndex[];
42
+ readonly kinds?: readonly StoredAct['kind'][];
43
+ readonly actors?: readonly string[];
44
+ readonly actorMatch?: 'identity' | 'exact';
45
+ readonly actorRequired?: true;
46
+ readonly afterAt?: number;
47
+ }
48
+ export interface IndexPage {
49
+ readonly edge: 'oldest' | 'newest';
50
+ readonly limit: number;
51
+ }
52
+ export interface FactPage<T> {
53
+ readonly items: readonly T[];
54
+ readonly hasMore: boolean;
55
+ }
56
+ export interface DirectedFilter {
57
+ readonly afterIndex?: number;
58
+ readonly beforeIndex?: number;
59
+ readonly throughIndex?: number;
60
+ readonly unseenOnly?: true;
61
+ readonly senders?: readonly string[];
62
+ readonly attentionOnly?: true;
63
+ }
64
+ export interface DirectedHead {
65
+ readonly index: ActivityIndex;
66
+ readonly at: number;
67
+ readonly actor: string;
68
+ readonly recipient: string;
69
+ readonly route: 'mention' | 'reply' | 'attention' | 'bell';
70
+ }
71
+ export interface LandedRecipient {
72
+ readonly recipient: string;
73
+ readonly route: DirectedHead['route'];
74
+ }
75
+ export interface ReceiptFacts {
76
+ readonly latestAt?: number;
77
+ readonly latestActorActivityIndex?: number;
78
+ }
79
+ export interface ReceiptAssignment {
80
+ readonly owner: string;
81
+ readonly index: ActivityIndex;
82
+ }
83
+ export type JsonValue = null | boolean | number | string | readonly JsonValue[] | {
84
+ readonly [key: string]: JsonValue;
85
+ };
86
+ export interface RouteFact {
87
+ readonly location: string;
88
+ readonly participant: string;
89
+ readonly sessionId: string;
90
+ readonly channel: string;
91
+ readonly kind: WakeRouteKind;
92
+ readonly address: Readonly<Record<string, string>>;
93
+ readonly updatedAt: number;
94
+ readonly epoch?: JsonValue;
95
+ readonly extra: Readonly<Record<string, JsonValue>>;
96
+ }
97
+ /** Domain facts exist only during a synchronous artifact callback. Values are detached. */
98
+ export interface SquareRead {
99
+ header(): SquareHeader;
100
+ participant(requestedName: string): ParticipantFact | undefined;
101
+ roster(): readonly ParticipantFact[];
102
+ attention(listener: string): readonly AttentionFact[];
103
+ incomingAttention(sender: string): readonly AttentionFact[];
104
+ hold(): {
105
+ readonly active: boolean;
106
+ readonly at?: number;
107
+ readonly reason?: string;
108
+ readonly actor?: string;
109
+ };
110
+ activity(index: ActivityIndex): StoredAct | undefined;
111
+ activityHeads(filter: ActivityFilter, page: IndexPage): FactPage<ActivityHead>;
112
+ activities(indexes: readonly ActivityIndex[]): readonly StoredAct[];
113
+ activityCount(filter: ActivityFilter): number;
114
+ contextIndexes(centers: readonly ActivityIndex[], before: number, after: number): readonly ActivityIndex[];
115
+ sayOrdinals(indexes: readonly ActivityIndex[]): Readonly<Record<number, number>>;
116
+ recentSayTimes(afterExclusive: number, takeLatest: number, actor?: string, bellOnly?: true): readonly number[];
117
+ audience(index: ActivityIndex): readonly LandedRecipient[];
118
+ directed(recipient: string, filter: DirectedFilter, page: IndexPage): FactPage<DirectedHead>;
119
+ directedAt(recipient: string, index: ActivityIndex): DirectedHead | undefined;
120
+ directedCount(recipient: string, filter: DirectedFilter): number;
121
+ pendingAudience(afterExclusive: number, throughInclusive: number, activityLimit: number): {
122
+ readonly groups: readonly {
123
+ readonly index: ActivityIndex;
124
+ readonly at: number;
125
+ readonly actor: string;
126
+ readonly recipients: readonly LandedRecipient[];
127
+ }[];
128
+ readonly hasMore: boolean;
129
+ };
130
+ receipt(requestedName: string, index: ActivityIndex): {
131
+ readonly at: number;
132
+ } | undefined;
133
+ receiptFacts(requestedName: string, afterIndex: number): ReceiptFacts;
134
+ receiptAssignments(): readonly ReceiptAssignment[];
135
+ lease(requestedName: string): WatchLease | undefined;
136
+ routes(): readonly RouteFact[];
137
+ }
138
+ export interface SquareWrite extends SquareRead {
139
+ land(activity: Act & {
140
+ readonly at: number;
141
+ }): StoredAct;
142
+ markSeen(requestedName: string, indexes: readonly ActivityIndex[], at: number): number;
143
+ putLease(requestedName: string, lease: WatchLease): boolean;
144
+ removeLease(requestedName: string, expectedLeaseId: string): boolean;
145
+ replaceRoutes(routes: readonly RouteFact[]): boolean;
146
+ }
147
+ export interface SquareReadResult<R> {
148
+ readonly value: R;
149
+ readonly version: number;
150
+ }
151
+ export interface SquareCommitResult<R> extends SquareReadResult<R> {
152
+ readonly changed: boolean;
153
+ }
154
+ /** Atomic artifact access; no framing, SQL or lazy transaction capability escapes. */
5
155
  export interface SquareArtifactPort {
6
- read(signal?: AbortSignal): Promise<{
7
- state: SquareState;
8
- version: number;
9
- }>;
10
- transact<R>(fn: (state: SquareState, version: number) => {
11
- state?: SquareState;
12
- result: R;
13
- }, signal?: AbortSignal): Promise<R>;
156
+ read<R>(select: (facts: SquareRead) => R, signal?: AbortSignal): Promise<SquareReadResult<R>>;
157
+ transact<R>(change: (facts: SquareWrite) => R, signal?: AbortSignal): Promise<SquareCommitResult<R>>;
158
+ revision(signal?: AbortSignal): Promise<number>;
14
159
  changed(sinceVersion: number, timeoutMs: number, signal?: AbortSignal): Promise<boolean>;
15
160
  close(): Promise<void>;
16
161
  }
@@ -121,5 +266,5 @@ export interface PresentPendingInput {
121
266
  /** Boundary previews that were clipped remain pending for a later full presentation. */
122
267
  readonly markSeen?: boolean;
123
268
  /** Receiver-specific fences checked inside the artifact observation transaction. */
124
- readonly current?: (state: SquareState) => boolean;
269
+ readonly current?: (facts: SquareRead) => boolean;
125
270
  }
@@ -1,3 +1,3 @@
1
1
  import type { PresentationResult, PresentPendingInput } from './ports.js';
2
- /** Present one pending activity; artifact seen remains the authoritative receipt. */
2
+ /** Presentation evidence is not a receipt; only the fenced receipt transaction marks seen. */
3
3
  export declare function presentPending(input: PresentPendingInput): Promise<PresentationResult>;
@@ -1,65 +1,56 @@
1
1
  import { formatActivityId, parseActivityId } from './square-core.js';
2
- import { deriveDeliveryModel } from './delivery.js';
3
- import { recordObservation } from './runtime.js';
4
- /** Present one pending activity; artifact seen remains the authoritative receipt. */
2
+ import { pendingHead, sameJoin } from './delivery.js';
3
+ /** Presentation evidence is not a receipt; only the fenced receipt transaction marks seen. */
5
4
  export async function presentPending(input) {
6
5
  const index = typeof input.activity === 'number' ? input.activity : parseActivityId(input.activity);
7
6
  if (index === undefined)
8
7
  return { presented: false };
9
- let before;
10
- try {
11
- before = await input.artifact.read(input.signal);
12
- }
13
- catch {
14
- return { presented: false };
15
- }
16
- const delivery = deriveDeliveryModel(before.state);
17
- const item = before.state.acts.find((activity) => activity.index === index);
18
- if (input.current?.(before.state) === false || item === undefined || delivery.isSeen(input.participant, index) || !delivery.pendingFor(input.participant).some((notification) => notification.item.index === index))
8
+ const before = await input.artifact.read((facts) => {
9
+ if (input.current?.(facts) === false || !pendingHead(facts, input.participant, index))
10
+ return undefined;
11
+ return { item: facts.activity(index), participant: facts.participant(input.participant) };
12
+ }, input.signal).catch(() => undefined);
13
+ if (before?.value === undefined)
19
14
  return { presented: false };
15
+ const { item, participant } = before.value;
16
+ const current = (facts) => sameJoin(facts, participant) && input.current?.(facts) !== false
17
+ && pendingHead(facts, participant.name, index) !== undefined;
18
+ const coordinate = { location: input.location, participant: input.participant, session: input.session, activity: formatActivityId(index), kind: 'presentation' };
20
19
  let claimToken;
20
+ const release = async () => {
21
+ if (claimToken !== undefined)
22
+ await input.hostLedger.releaseEvidence({ ...coordinate, claimToken });
23
+ };
21
24
  if (input.hostLedger !== undefined && input.session !== undefined) {
22
- const claim = await input.hostLedger.claimEvidence({ location: input.location, participant: input.participant, session: input.session, activity: formatActivityId(index), kind: 'presentation', leaseMs: input.timeoutMs ?? 5000 });
25
+ const claim = await input.hostLedger.claimEvidence({ ...coordinate, leaseMs: input.timeoutMs ?? 5000 });
23
26
  if (claim.status !== 'acquired')
24
27
  return { presented: false };
25
28
  claimToken = claim.claimToken;
26
- let current;
27
- try {
28
- current = await input.artifact.read(input.signal);
29
- }
30
- catch {
31
- await input.hostLedger.releaseEvidence({ location: input.location, participant: input.participant, session: input.session, activity: formatActivityId(index), kind: 'presentation', claimToken }).catch(() => undefined);
32
- return { presented: false };
33
- }
34
- if (input.current?.(current.state) === false || deriveDeliveryModel(current.state).isSeen(input.participant, index)) {
35
- await input.hostLedger.releaseEvidence({ location: input.location, participant: input.participant, session: input.session, activity: formatActivityId(index), kind: 'presentation', claimToken }).catch(() => undefined);
29
+ const fresh = await input.artifact.read(current, input.signal).catch(() => undefined);
30
+ if (!fresh?.value) {
31
+ await release().catch(() => undefined);
36
32
  return { presented: false };
37
33
  }
38
- try {
39
- await input.sink.present(item);
40
- }
41
- catch (error) {
42
- await input.hostLedger.appendEvidence({ location: input.location, participant: input.participant, session: input.session, activity: formatActivityId(index), kind: 'presentation', outcome: 'failed', message: error instanceof Error ? error.message : String(error), claimToken });
43
- throw error;
44
- }
45
34
  }
46
- else
35
+ try {
47
36
  await input.sink.present(item);
48
- const committed = await input.artifact.transact((state) => {
49
- const current = deriveDeliveryModel(state);
50
- if (input.current?.(state) === false || current.isSeen(input.participant, index) || !current.pendingFor(input.participant).some((notification) => notification.item.index === index))
51
- return { result: false };
52
- if (input.markSeen === false)
53
- return { result: true };
54
- const changed = recordObservation(state, input.participant, index, 'seen', input.now ?? Date.now());
55
- return changed ? { state, result: true } : { result: false };
37
+ }
38
+ catch (error) {
39
+ if (claimToken !== undefined)
40
+ await input.hostLedger.appendEvidence({ ...coordinate, outcome: 'failed', message: error instanceof Error ? error.message : String(error), claimToken });
41
+ throw error;
42
+ }
43
+ const { value: committed } = await input.artifact.transact((facts) => {
44
+ if (!current(facts))
45
+ return false;
46
+ return input.markSeen === false || facts.markSeen(participant.name, [index], input.now ?? Date.now()) > 0;
56
47
  }, input.signal);
57
48
  if (!committed) {
58
- if (input.hostLedger !== undefined && input.session !== undefined && claimToken !== undefined)
59
- await input.hostLedger.releaseEvidence({ location: input.location, participant: input.participant, session: input.session, activity: formatActivityId(index), kind: 'presentation', claimToken });
49
+ await release();
60
50
  return { presented: false };
61
51
  }
62
- if (input.hostLedger !== undefined && input.session !== undefined && claimToken !== undefined)
63
- await input.hostLedger.appendEvidence({ location: input.location, participant: input.participant, session: input.session, activity: formatActivityId(index), kind: 'presentation', outcome: input.markSeen === false ? 'clipped' : 'presented', ...(input.markSeen === false ? { message: 'presentation clipped' } : {}), claimToken });
52
+ if (claimToken !== undefined)
53
+ await input.hostLedger.appendEvidence({ ...coordinate, outcome: input.markSeen === false ? 'clipped' : 'presented',
54
+ ...(input.markSeen === false ? { message: 'presentation clipped' } : {}), claimToken });
64
55
  return { presented: true, activity: item };
65
56
  }
@@ -1,7 +1,7 @@
1
- import { type StoredAct, type SquareState, type PublicAct, type RoomChangeAct } from './model.js';
1
+ import { type StoredAct, type RoomChangeAct } from './model.js';
2
2
  export { participantIdentity } from './participant-identity.js';
3
3
  import { type Perception } from './square-core.js';
4
- import type { UnreadActivitySummary, ParticipantStatus } from './decisions.js';
4
+ import type { Activity, UnreadActivitySummary, ParticipantSummary } from './square-facade.js';
5
5
  export type WatchStatus = 'stale' | 'empty-now' | 'quorum' | 'capped';
6
6
  interface HeaderOptions {
7
7
  participantCount?: number;
@@ -23,7 +23,7 @@ interface ActivityLimitOptions extends ParticipantOutputOptions {
23
23
  interface ActivityBlockedOptions extends ParticipantOutputOptions {
24
24
  retryCommand: string;
25
25
  forceCommand: string;
26
- activitySummaries: UnreadActivitySummary[];
26
+ activitySummaries: readonly UnreadActivitySummary[];
27
27
  draftPath?: string;
28
28
  }
29
29
  interface ExpressWaitingOptions {
@@ -44,7 +44,7 @@ interface WatchStatusOptions extends ParticipantOutputOptions {
44
44
  status: WatchStatus;
45
45
  idleMs?: number;
46
46
  presence?: {
47
- participants: ParticipantStatus[];
47
+ participants: readonly ParticipantSummary[];
48
48
  now: number;
49
49
  };
50
50
  showCatchHint?: boolean;
@@ -58,6 +58,7 @@ export declare function formatRefusal(squarePath: string, bodyLines: string[], o
58
58
  participantCount?: number;
59
59
  held?: boolean;
60
60
  }): string;
61
+ export declare function refusalText(error: unknown): string;
61
62
  export declare function quoteShell(value: string): string;
62
63
  export declare function commandPrefix(squarePath: string): string;
63
64
  export declare function participantCommandPrefix(squarePath: string, name: string): string;
@@ -66,44 +67,42 @@ export declare function joinRecoveryCommand(squarePath: string, name: string): s
66
67
  /** Taking a name is an explicit recovery choice, never an automatic retry. */
67
68
  export declare function takeoverRecoveryLines(squarePath: string, name: string): string[];
68
69
  export declare function participantsRecoveryCommand(squarePath: string): string;
69
- export declare function renderPresenceLines(participants: ParticipantStatus[], now: number, limit?: number): string[];
70
+ export declare function renderPresenceLines(recent: readonly ParticipantSummary[], now: number, limit?: number): string[];
70
71
  export declare function expressHintLine(ownActivityCount: number): string | undefined;
71
72
  export declare function truncateChars(body: string, maxChars: number): {
72
73
  text: string;
73
74
  remaining: number;
74
75
  };
75
76
  export declare function truncateExternalDiagnostic(diagnostic: string): string;
76
- export declare function renderRoomChangeText(event: RoomChangeAct): string;
77
+ export declare function renderRoomChangeText(event: RoomChangeAct | Activity): string;
77
78
  interface RenderedEvent {
78
79
  text: string;
79
80
  /** The rendered body was cut short by `preview`. */
80
81
  bodyClipped: boolean;
81
82
  }
82
- export declare function renderEventCli(event: StoredAct, opts?: {
83
+ export declare function renderEventCli(event: StoredAct | Activity, opts?: {
83
84
  now?: number;
84
85
  preview?: number;
85
86
  actNumber?: number;
86
87
  mention?: string;
87
88
  }): string;
88
- export declare function renderAmbientEventFacts(event: StoredAct, viewer: string, opts?: {
89
+ export declare function renderAmbientEventFacts(event: StoredAct | Activity, viewer: string, opts?: {
89
90
  now?: number;
90
91
  preview?: number;
91
92
  presencePreview?: number;
92
93
  actNumber?: number;
93
94
  mention?: string;
94
- squareState?: SquareState;
95
95
  perception?: Perception;
96
96
  }): RenderedEvent;
97
- export declare function renderAmbientEvent(event: StoredAct, viewer: string, opts?: {
97
+ export declare function renderAmbientEvent(event: StoredAct | Activity, viewer: string, opts?: {
98
98
  now?: number;
99
99
  preview?: number;
100
100
  presencePreview?: number;
101
101
  actNumber?: number;
102
102
  mention?: string;
103
- squareState?: SquareState;
104
103
  perception?: Perception;
105
104
  }): string;
106
- export declare function renderPendingFeed(history: StoredAct[], publicItems: PublicAct[], viewer: string | undefined, squareState: SquareState): string;
105
+ export declare function renderPendingFeed(publicItems: readonly StoredAct[], sayNumbers: Readonly<Record<number, number>>, perceptions: Readonly<Record<number, Perception>>, viewer: string): string;
107
106
  export declare function renderActivityBlocked(opts: ActivityBlockedOptions): string;
108
107
  /** An indeterminate or already-committed send only points to inspection, never resend. */
109
108
  export declare function renderActivityUncertain(opts: ParticipantOutputOptions & {
@@ -114,7 +113,7 @@ export declare function renderActivityUncertain(opts: ParticipantOutputOptions &
114
113
  export declare function renderExpressWaiting(opts: ExpressWaitingOptions): string;
115
114
  export declare function renderExpressNoWait(opts: ExpressNoWaitOptions): string;
116
115
  export declare function renderPresenceAnchor(names: readonly string[]): string;
117
- export declare function renderGrepActivitiesView(visible: StoredAct[], totalMatches: number, noTruncate: boolean | undefined, squarePath: string, pattern: string, fixed?: boolean, perception?: (act: StoredAct) => Perception): string;
116
+ export declare function renderGrepActivitiesView(visible: readonly StoredAct[], totalMatches: number, noTruncate: boolean | undefined, squarePath: string, pattern: string, fixed?: boolean, perception?: (act: StoredAct) => Perception): string;
118
117
  export declare function renderActivityLimit(opts: ActivityLimitOptions): string;
119
118
  export declare function renderWatchAlreadyActive(opts: ParticipantOutputOptions): string;
120
119
  export declare function renderWatchForceTakeover(_opts: ParticipantOutputOptions): string;
@@ -123,9 +122,3 @@ export declare function renderWatchReplaced(_opts: ParticipantOutputOptions): st
123
122
  export declare function renderWatchStatus(opts: WatchStatusOptions): string;
124
123
  export declare function renderDoctorClean(): string;
125
124
  export declare function renderDoctorUnfixable(reason: string): string;
126
- export declare function renderWatchOutput(history: StoredAct[], publicItems: PublicAct[], opts: {
127
- mention?: string;
128
- viewer: string;
129
- squareState?: SquareState;
130
- perceptions?: ReadonlyMap<number, Perception>;
131
- }): string;