@astrosheep/square 0.3.70 → 0.3.72

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 (126) 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/packages/agent-delivery/src/claude.d.ts +2 -11
  51. package/dist/packages/agent-delivery/src/index.d.ts +2 -42
  52. package/dist/packages/agent-delivery/src/opencode.d.ts +2 -11
  53. package/dist/packages/agent-delivery/src/paseo.d.ts +2 -13
  54. package/dist/packages/agent-delivery/src/pi-client.d.ts +2 -9
  55. package/dist/packages/agent-delivery/src/types.d.ts +42 -0
  56. package/dist/participant-host.d.ts +6 -3
  57. package/dist/participant-host.js +44 -38
  58. package/dist/perception-projection.d.ts +6 -15
  59. package/dist/perception-projection.js +6 -26
  60. package/dist/plugins/opencode/delivery.js +9 -9
  61. package/dist/plugins/opencode/index.js +3 -3
  62. package/dist/ports.d.ts +156 -11
  63. package/dist/presentation-operations.d.ts +1 -1
  64. package/dist/presentation-operations.js +35 -44
  65. package/dist/presentation.d.ts +12 -19
  66. package/dist/presentation.js +33 -54
  67. package/dist/registry.d.ts +2 -0
  68. package/dist/registry.js +1 -1
  69. package/dist/routes.d.ts +7 -14
  70. package/dist/routes.js +34 -72
  71. package/dist/runtime.d.ts +12 -39
  72. package/dist/runtime.js +33 -164
  73. package/dist/square-actions.d.ts +11 -21
  74. package/dist/square-actions.js +171 -275
  75. package/dist/square-application.d.ts +31 -55
  76. package/dist/square-application.js +625 -141
  77. package/dist/square-core.d.ts +3 -94
  78. package/dist/square-core.js +8 -353
  79. package/dist/square-domain.d.ts +24 -0
  80. package/dist/square-domain.js +77 -0
  81. package/dist/square-facade.d.ts +360 -9
  82. package/dist/square-migration.d.ts +24 -0
  83. package/dist/square-migration.js +211 -0
  84. package/dist/square-projections.d.ts +2 -42
  85. package/dist/square-projections.js +1 -37
  86. package/dist/square-sqlite.d.ts +59 -0
  87. package/dist/square-sqlite.js +702 -0
  88. package/dist/stream.d.ts +3 -10
  89. package/dist/stream.js +81 -52
  90. package/dist/views.d.ts +27 -95
  91. package/dist/views.js +217 -87
  92. package/dist/wake-eligibility.d.ts +15 -5
  93. package/dist/wake-eligibility.js +32 -16
  94. package/dist/wakes.d.ts +3 -3
  95. package/dist/wakes.js +31 -27
  96. package/guides/application-facade-delivery-evidence.md +200 -0
  97. package/guides/application-facade-design.md +95 -0
  98. package/guides/change-waits.md +4 -4
  99. package/guides/migration.md +77 -0
  100. package/guides/postcommit-simplification-performance.md +36 -0
  101. package/guides/primitive-catch-consolidation.md +85 -0
  102. package/guides/primitive-history-consolidation.md +76 -0
  103. package/guides/primitive-status-consolidation.md +87 -0
  104. package/guides/primitive-storage-consolidation.md +128 -0
  105. package/guides/row-runtime-delivery-evidence.md +160 -0
  106. package/guides/row-storage-design.md +567 -0
  107. package/package.json +1 -1
  108. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  109. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  110. package/plugins/opencode/delivery.ts +11 -11
  111. package/plugins/opencode/index.ts +3 -3
  112. package/plugins/paseo/client/square-message.tsx +77 -64
  113. package/plugins/paseo/shared/message.ts +16 -0
  114. package/plugins/pi/square-pi.js +5 -1
  115. package/dist/activity.d.ts +0 -14
  116. package/dist/activity.js +0 -248
  117. package/dist/square-file-adapter.d.ts +0 -26
  118. package/dist/square-file-adapter.js +0 -85
  119. package/dist/square-state.d.ts +0 -6
  120. package/dist/square-state.js +0 -138
  121. package/dist/square-storage.d.ts +0 -14
  122. package/dist/square-storage.js +0 -188
  123. package/dist/square-wiring.d.ts +0 -33
  124. package/dist/square-wiring.js +0 -109
  125. package/dist/watch.d.ts +0 -8
  126. package/dist/watch.js +0 -252
@@ -1,14 +1,5 @@
1
- import { type Endpoint } from '@opencode/client/service';
2
- import type { OpenCodeDeliveryResult, OpenCodeTarget, OpenCodeSendTextOptions } from './types.js';
3
- export interface OpenCodeConnectExistingOptions {
4
- readonly harness: 'opencode';
5
- readonly sessionId: string;
6
- readonly endpoint?: Endpoint;
7
- readonly registrationFile?: string;
8
- readonly timeoutMs?: number;
9
- readonly signal?: AbortSignal;
10
- }
1
+ import type { OpenCodeConnectOptions, OpenCodeDeliveryResult, OpenCodeTarget, OpenCodeSendTextOptions } from './types.js';
11
2
  /** Resolve an existing service and persisted session. Never starts or stops a service. */
12
- export declare function connectExisting(options: OpenCodeConnectExistingOptions): Promise<OpenCodeTarget>;
3
+ export declare function connectExisting(options: OpenCodeConnectOptions): Promise<OpenCodeTarget>;
13
4
  /** Submit once through the shared native sender; HTTP metadata alone refines rejection. */
14
5
  export declare function sendText(target: OpenCodeTarget, text: string, options?: OpenCodeSendTextOptions): Promise<OpenCodeDeliveryResult>;
@@ -1,17 +1,6 @@
1
- import type { PaseoDeliveryResult, PaseoSendTextOptions, PaseoTarget } from './types.js';
2
- export interface PaseoConnectExistingOptions {
3
- readonly harness: 'paseo';
4
- readonly agentId: string;
5
- readonly endpoint?: string;
6
- readonly password?: string;
7
- readonly authHeader?: string;
8
- readonly localCredential?: string;
9
- readonly paseoHome?: string;
10
- readonly timeoutMs?: number;
11
- readonly signal?: AbortSignal;
12
- }
1
+ import type { PaseoConnectOptions, PaseoDeliveryResult, PaseoSendTextOptions, PaseoTarget } from './types.js';
13
2
  /** Connect to an existing daemon and address one existing agent. Never starts or stops anything. */
14
- export declare function connectPaseo(options: PaseoConnectExistingOptions): Promise<PaseoTarget>;
3
+ export declare function connectPaseo(options: PaseoConnectOptions): Promise<PaseoTarget>;
15
4
  /**
16
5
  * Every call owns its daemon connection: one for connect to prove the endpoint
17
6
  * and credential, and one per send, closed again as soon as the daemon answers.
@@ -1,10 +1,3 @@
1
- import type { PiDeliveryResult, PiSendTextOptions, PiTarget } from './types.js';
2
- export interface PiConnectExistingOptions {
3
- readonly harness: 'pi';
4
- readonly sessionId: string;
5
- readonly endpoint: string;
6
- readonly timeoutMs?: number;
7
- readonly signal?: AbortSignal;
8
- }
9
- export declare function connectPi(options: PiConnectExistingOptions): Promise<PiTarget>;
1
+ import type { PiConnectOptions, PiDeliveryResult, PiSendTextOptions, PiTarget } from './types.js';
2
+ export declare function connectPi(options: PiConnectOptions): Promise<PiTarget>;
10
3
  export declare function sendPiText(target: PiTarget, text: string, options?: PiSendTextOptions): Promise<PiDeliveryResult>;
@@ -46,6 +46,48 @@ export interface PaseoSendTextOptions {
46
46
  readonly signal?: AbortSignal;
47
47
  }
48
48
  export type SendTextOptions = OpenCodeSendTextOptions | ClaudeSendTextOptions | PiSendTextOptions | PaseoSendTextOptions;
49
+ export interface OpenCodeConnectOptions {
50
+ readonly harness: 'opencode';
51
+ readonly sessionId: string;
52
+ readonly endpoint?: {
53
+ readonly url: string;
54
+ readonly auth?: {
55
+ readonly type: 'basic';
56
+ readonly username: string;
57
+ readonly password: string;
58
+ };
59
+ };
60
+ readonly registrationFile?: string;
61
+ readonly timeoutMs?: number;
62
+ readonly signal?: AbortSignal;
63
+ }
64
+ export interface ClaudeConnectOptions {
65
+ readonly harness: 'claude';
66
+ readonly sessionId: string;
67
+ readonly endpoint: string;
68
+ readonly token?: string;
69
+ readonly claudeHome?: string;
70
+ readonly timeoutMs?: number;
71
+ readonly signal?: AbortSignal;
72
+ }
73
+ export interface PiConnectOptions {
74
+ readonly harness: 'pi';
75
+ readonly sessionId: string;
76
+ readonly endpoint: string;
77
+ readonly timeoutMs?: number;
78
+ readonly signal?: AbortSignal;
79
+ }
80
+ export interface PaseoConnectOptions {
81
+ readonly harness: 'paseo';
82
+ readonly agentId: string;
83
+ readonly endpoint?: string;
84
+ readonly password?: string;
85
+ readonly authHeader?: string;
86
+ readonly localCredential?: string;
87
+ readonly paseoHome?: string;
88
+ readonly timeoutMs?: number;
89
+ readonly signal?: AbortSignal;
90
+ }
49
91
  interface OpenCodeAttempt {
50
92
  readonly harness: 'opencode';
51
93
  readonly sessionId: string;
@@ -12,8 +12,11 @@ export declare function processIdentity(env: NodeJS.ProcessEnv): {
12
12
  channel: PresenceChannel;
13
13
  };
14
14
  export declare function identityRouteDraft(context: HostContext, participant: string): Promise<Omit<WakeRoute, 'updatedAt'> | undefined>;
15
- export declare function publishIdentityRoute(context: HostContext, participant: string, epoch?: number): Promise<void>;
15
+ /** One postcommit host observation; presence and route publication fail independently. */
16
+ export declare function publishCommittedIdentity(context: HostContext, participant: string, epoch?: number): Promise<void>;
16
17
  export declare function retireIdentityRoute(context: HostContext, participant: string, expectedEpoch?: number): Promise<void>;
17
18
  export declare function assertLiveOwner(context: HostContext, participant: string, expectedEpoch?: number): Promise<boolean>;
18
- /** Presence is best effort and runs only after the artifact mutation commits. */
19
- export declare function ensureLocalPresence(context: HostContext, participant: string, epoch?: number): Promise<void>;
19
+ /** Best-effort postcommit presence; a returned epoch also witnesses a readable host observation. */
20
+ export declare function ensureLocalPresence(context: HostContext, participant: string, epoch?: number): Promise<{
21
+ readonly epoch?: number;
22
+ } | undefined>;
@@ -1,5 +1,6 @@
1
- import { presenceEpoch, readParticipantOwner, sessionOwnsParticipant } from './registry.js';
1
+ import { currentOwner, presenceEpoch, sessionOwnsParticipant } from './registry.js';
2
2
  import { firstHarnessSession } from './participant-identity.js';
3
+ import { canonicalPath } from './canonical-path.js';
3
4
  import { defaultWakeRouteCapabilities, publishWakeRoute, retireWakeRouteFromArtifact, resolvePrimaryWakeRoute, ROUTE_FRESH_MS } from './routes.js';
4
5
  export function processIdentity(env) {
5
6
  const found = firstHarnessSession(env);
@@ -13,19 +14,17 @@ export async function identityRouteDraft(context, participant) {
13
14
  const capabilities = context.hostLedger === undefined ? { canUse: () => false } : await defaultWakeRouteCapabilities(context.hostLedger);
14
15
  return resolvePrimaryWakeRoute({ location: context.location, participant, sessionId: identity.session, provider }, context.env ?? process.env, capabilities);
15
16
  }
16
- async function currentOwnerEpoch(context, participant) {
17
- const hostLedger = context.hostLedger;
18
- if (hostLedger === undefined || context.location === undefined || context.location === 'memory')
19
- return undefined;
20
- const owner = await readParticipantOwner(context.location, participant, hostLedger);
21
- return owner === undefined ? undefined : presenceEpoch(owner);
22
- }
23
- export async function publishIdentityRoute(context, participant, epoch) {
24
- const route = await identityRouteDraft(context, participant);
25
- if (route === undefined)
17
+ /** One postcommit host observation; presence and route publication fail independently. */
18
+ export async function publishCommittedIdentity(context, participant, epoch) {
19
+ const observed = await ensureLocalPresence(context, participant, epoch).catch(() => undefined);
20
+ if (observed === undefined)
26
21
  return;
27
- const ownerEpoch = epoch ?? await currentOwnerEpoch(context, participant);
28
- await publishWakeRoute(context.artifact, { ...route, ...(ownerEpoch === undefined ? {} : { epoch: ownerEpoch }) }, { at: context.clock(), requireCurrentSession: true }).catch(() => undefined);
22
+ try {
23
+ const route = await identityRouteDraft(context, participant);
24
+ if (route !== undefined)
25
+ await publishWakeRoute(context.artifact, { ...route, ...observed }, { at: context.clock(), requireCurrentSession: true });
26
+ }
27
+ catch { /* Optional route enrichment cannot undo the committed activity. */ }
29
28
  }
30
29
  export async function retireIdentityRoute(context, participant, expectedEpoch) {
31
30
  if (context.location === undefined || context.location === 'memory')
@@ -43,35 +42,42 @@ export async function assertLiveOwner(context, participant, expectedEpoch) {
43
42
  return true;
44
43
  return sessionOwnsParticipant(context.location, participant, identity.session, hostLedger, expectedEpoch);
45
44
  }
46
- /** Presence is best effort and runs only after the artifact mutation commits. */
45
+ /** Best-effort postcommit presence; a returned epoch also witnesses a readable host observation. */
47
46
  export async function ensureLocalPresence(context, participant, epoch) {
48
47
  if (context.hostLedger === undefined || context.location === undefined || context.location === 'memory')
49
48
  return;
50
49
  const identity = processIdentity(context.env ?? process.env);
51
- const ownerEpoch = epoch ?? await currentOwnerEpoch(context, participant);
52
- const now = context.clock();
53
- // Semantic publish: an unexpired presence row already stands when it carries the same owner epoch.
50
+ const now = Date.now();
51
+ let existing;
54
52
  try {
55
- const existing = await context.hostLedger.listPresence({ location: context.location, participant, session: identity.session, now });
56
- if (existing.some((row) => row.channel === identity.channel
57
- && now - (row.updatedAt ?? 0) < ROUTE_FRESH_MS
58
- && (ownerEpoch === undefined || presenceEpoch(row) === ownerEpoch)))
59
- return;
53
+ existing = await context.hostLedger.listPresence({ location: await canonicalPath(context.location), participant, now });
54
+ }
55
+ catch (error) {
56
+ process.stderr.write(`! host presence degraded: ${error instanceof Error ? error.message : String(error)}\n`);
57
+ return;
58
+ }
59
+ const owner = currentOwner(existing);
60
+ const ownerEpoch = epoch ?? (owner === undefined ? undefined : presenceEpoch(owner));
61
+ const observed = ownerEpoch === undefined ? {} : { epoch: ownerEpoch };
62
+ // A fresh exact local binding at the observed (or explicit claim) generation already stands.
63
+ if (existing.some((row) => row.session === identity.session && row.channel === identity.channel
64
+ && now - (row.updatedAt ?? 0) < ROUTE_FRESH_MS
65
+ && (ownerEpoch === undefined || presenceEpoch(row) === ownerEpoch)))
66
+ return observed;
67
+ try {
68
+ const result = await context.hostLedger.ensurePresence({
69
+ location: context.location,
70
+ participant,
71
+ session: identity.session,
72
+ channel: identity.channel,
73
+ updatedAt: Date.now(),
74
+ ...(ownerEpoch === undefined || ownerEpoch <= 0 ? {} : { epoch: ownerEpoch }),
75
+ });
76
+ if (result.status === 'degraded')
77
+ throw result.error;
78
+ }
79
+ catch (error) {
80
+ process.stderr.write(`! host presence degraded: ${error instanceof Error ? error.message : String(error)}\n`);
60
81
  }
61
- catch { /* fall through to the best-effort ensure below */ }
62
- const result = await context.hostLedger.ensurePresence({
63
- location: context.location,
64
- participant,
65
- session: identity.session,
66
- channel: identity.channel,
67
- // Presence rows are host-ledger wall-time evidence; the square clock belongs to artifact activities.
68
- updatedAt: Date.now(),
69
- ...(ownerEpoch === undefined || ownerEpoch <= 0 ? {} : { epoch: ownerEpoch }),
70
- }).catch((error) => ({
71
- status: 'degraded',
72
- record: { location: context.location, participant, session: identity.session, channel: identity.channel },
73
- error,
74
- }));
75
- if (result.status === 'degraded')
76
- process.stderr.write(`! host presence degraded: ${result.error instanceof Error ? result.error.message : String(result.error)}\n`);
82
+ return observed;
77
83
  }
@@ -1,15 +1,6 @@
1
- import { type LandedAudienceReplay, type Perception } from './square-core.js';
2
- import { type SquareState, type StoredAct } from './model.js';
3
- /**
4
- * The small, state-only perception surface shared by catch and delivery.
5
- * Notification planning deliberately remains outside this projection.
6
- */
7
- export interface PerceptionProjection {
8
- directedTo(activity: StoredAct, name: string): boolean;
9
- perceive(activity: StoredAct, name: string): Perception;
10
- cursorFor(name: string): number;
11
- isSeen(name: string, index: number): boolean;
12
- readonly replayedActivityCount: number;
13
- }
14
- /** Build perception over one audience replay of a SquareState snapshot. */
15
- export declare function derivePerceptionProjection(state: SquareState, landed?: LandedAudienceReplay): PerceptionProjection;
1
+ import type { Perception } from './square-core.js';
2
+ import { type StoredAct } from './model.js';
3
+ import type { SquareRead } from './ports.js';
4
+ export declare function directedTo(facts: SquareRead, activity: StoredAct, name: string): boolean;
5
+ /** Audience is settled at landing, never reconstructed from a partial history. */
6
+ export declare function perceiveActivity(facts: SquareRead, activity: StoredAct, name: string): Perception;
@@ -1,28 +1,8 @@
1
- import { formatActivityId, replayLandedAudiences } from './square-core.js';
2
1
  import { sameName } from './model.js';
3
- import { readCursor } from './runtime.js';
4
- /** Build perception over one audience replay of a SquareState snapshot. */
5
- export function derivePerceptionProjection(state, landed = replayLandedAudiences(state.acts)) {
6
- function canonicalName(name) {
7
- return landed.resolveParticipant(name) ?? name;
8
- }
9
- function directedTo(activity, name) {
10
- return activity.kind === 'say' && landed.includes(activity, name);
11
- }
12
- function perceive(activity, name) {
13
- if (activity.kind !== 'say' || sameName(activity.actor, name))
14
- return 'full';
15
- return directedTo(activity, name) ? 'full' : 'presence';
16
- }
17
- function isSeen(name, index) {
18
- const recipient = canonicalName(name);
19
- return state.runtime.observations?.[recipient]?.[formatActivityId(index)]?.state === 'seen';
20
- }
21
- return {
22
- directedTo,
23
- perceive,
24
- cursorFor: (name) => readCursor(state, name, landed),
25
- isSeen,
26
- replayedActivityCount: landed.replayedActivityCount,
27
- };
2
+ export function directedTo(facts, activity, name) {
3
+ return activity.kind === 'say' && facts.directedAt(name, activity.index) !== undefined;
4
+ }
5
+ /** Audience is settled at landing, never reconstructed from a partial history. */
6
+ export function perceiveActivity(facts, activity, name) {
7
+ return activity.kind !== 'say' || sameName(activity.actor, name) || directedTo(facts, activity, name) ? 'full' : 'presence';
28
8
  }
@@ -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>;