@astrosheep/square 0.4.0 → 0.4.1
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.
- package/dist/artifact.d.ts +1 -3
- package/dist/artifact.js +12 -36
- package/dist/attention-presentation.d.ts +2 -22
- package/dist/attention-presentation.js +9 -12
- package/dist/boundary-presentation.d.ts +3 -14
- package/dist/boundary-presentation.js +29 -104
- package/dist/card-presentation.d.ts +1 -0
- package/dist/card-presentation.js +2 -0
- package/dist/catch-decisions.js +1 -7
- package/dist/claude-delivery.d.ts +13 -8
- package/dist/claude-delivery.js +40 -93
- package/dist/claude-mcp-context.js +2 -2
- package/dist/claude-mod.js +28 -140
- package/dist/claude-transport.d.ts +3 -3
- package/dist/claude-transport.js +20 -44
- package/dist/cli/context.js +7 -9
- package/dist/cli/observation-commands.d.ts +2 -0
- package/dist/cli/observation-commands.js +12 -2
- package/dist/cli/registry.js +3 -1
- package/dist/codex-address.d.ts +2 -2
- package/dist/codex-boundary-state.d.ts +1 -5
- package/dist/codex-boundary-state.js +0 -4
- package/dist/codex-hook.d.ts +7 -10
- package/dist/codex-hook.js +69 -33
- package/dist/codex-queue.d.ts +10 -11
- package/dist/codex-queue.js +32 -32
- package/dist/codex-watch.d.ts +2 -0
- package/dist/codex-watch.js +100 -0
- package/dist/decisions.js +2 -2
- package/dist/delivery-deferral.d.ts +8 -17
- package/dist/delivery-deferral.js +0 -28
- package/dist/delivery-frame.d.ts +7 -0
- package/dist/delivery-frame.js +11 -0
- package/dist/delivery-ledger-sqlite.d.ts +97 -0
- package/dist/delivery-ledger-sqlite.js +496 -0
- package/dist/delivery-ledger.d.ts +196 -0
- package/dist/delivery-ledger.js +268 -0
- package/dist/delivery-lifetime.d.ts +6 -0
- package/dist/delivery-lifetime.js +45 -0
- package/dist/harness-links.js +61 -7
- package/dist/harness.js +12 -17
- package/dist/help.js +2 -0
- package/dist/http-delivery.d.ts +5 -9
- package/dist/http-delivery.js +20 -77
- package/dist/inbox.d.ts +6 -49
- package/dist/inbox.js +24 -371
- package/dist/index.d.ts +6 -3
- package/dist/index.js +4 -1
- package/dist/mcp.js +1 -1
- package/dist/model.d.ts +7 -47
- package/dist/model.js +1 -7
- package/dist/notifications.d.ts +24 -20
- package/dist/notifications.js +39 -108
- package/dist/operation-control.d.ts +4 -0
- package/dist/operation-control.js +20 -0
- package/dist/packages/agent-delivery/src/claude-native.d.ts +2 -0
- package/dist/packages/agent-delivery/src/claude-native.js +5 -0
- package/dist/packages/agent-delivery/src/codex-boundary.d.ts +11 -6
- package/dist/packages/agent-delivery/src/codex-boundary.js +31 -40
- package/dist/packages/agent-delivery/src/codex-hook.d.ts +17 -12
- package/dist/packages/agent-delivery/src/codex-hook.js +26 -39
- package/dist/packages/agent-delivery/src/codex.d.ts +3 -0
- package/dist/packages/agent-delivery/src/codex.js +41 -4
- package/dist/packages/agent-delivery/src/index.d.ts +3 -1
- package/dist/packages/agent-delivery/src/index.js +1 -1
- package/dist/packages/agent-delivery/src/paseo.js +11 -2
- package/dist/packages/agent-delivery/src/types.d.ts +1 -0
- package/dist/paseo-delivery.d.ts +5 -5
- package/dist/paseo-delivery.js +19 -97
- package/dist/paseo.d.ts +1 -1
- package/dist/paseo.js +1 -1
- package/dist/plugins/opencode/index.d.ts +5 -3
- package/dist/plugins/opencode/index.js +99 -21
- package/dist/plugins/opencode-shared/delivery.d.ts +23 -0
- package/dist/plugins/opencode-shared/delivery.js +109 -0
- package/dist/plugins/opencode-v1/index.d.ts +28 -0
- package/dist/plugins/opencode-v1/index.js +41 -0
- package/dist/plugins/opencode-v1/runtime.d.ts +56 -0
- package/dist/plugins/opencode-v1/runtime.js +152 -0
- package/dist/plugins/opencode-v1/transport.d.ts +13 -0
- package/dist/plugins/opencode-v1/transport.js +50 -0
- package/dist/plugins/paseo/shared/card-presentation.d.ts +10 -0
- package/dist/plugins/paseo/shared/card-presentation.js +7 -0
- package/dist/ports.d.ts +31 -90
- package/dist/session-lifecycle.d.ts +8 -7
- package/dist/session-lifecycle.js +40 -42
- package/dist/sqlite-square.d.ts +14 -23
- package/dist/sqlite-square.js +132 -139
- package/dist/square-api.js +4 -7
- package/dist/square-core.d.ts +0 -1
- package/dist/square-domain.d.ts +5 -17
- package/dist/square-domain.js +104 -221
- package/dist/square-facade.d.ts +18 -15
- package/dist/square-post.d.ts +53 -0
- package/dist/square-post.js +4 -0
- package/dist/square-posting.d.ts +13 -0
- package/dist/square-posting.js +174 -0
- package/dist/square-producer-sqlite.d.ts +11 -0
- package/dist/square-producer-sqlite.js +210 -0
- package/dist/square-sqlite.d.ts +15 -25
- package/dist/square-sqlite.js +150 -226
- package/dist/views.d.ts +2 -2
- package/dist/views.js +0 -1
- package/dist/wake-sink.d.ts +4 -11
- package/dist/wake-sink.js +8 -32
- package/dist/wake-transports.d.ts +4 -7
- package/dist/wake-transports.js +8 -39
- package/examples/symmetric-domains.mjs +29 -249
- package/guides/change-waits.md +13 -53
- package/guides/claude-native.md +19 -175
- package/guides/codex-delivery.md +55 -80
- package/guides/delivery-primitives.md +48 -207
- package/guides/operation-cancellation.md +2 -5
- package/guides/participant-ownership.md +25 -89
- package/guides/pi-native-delivery.md +19 -56
- package/guides/pi-tools.md +6 -8
- package/guides/square-api.md +113 -615
- package/package.json +8 -8
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/hooks/hooks.json +1 -1
- package/plugins/claude/{hooks/register.js → mods/square.js} +4 -7
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/codex/hooks/hooks.json +11 -23
- package/plugins/opencode/index.ts +91 -23
- package/plugins/opencode-shared/delivery.ts +91 -0
- package/plugins/opencode-v1/index.ts +36 -0
- package/plugins/opencode-v1/runtime.ts +134 -0
- package/plugins/opencode-v1/transport.ts +41 -0
- package/plugins/paseo/OVERVIEW.md +5 -5
- package/plugins/paseo/README.md +7 -6
- package/plugins/paseo/client/markdown.tsx +5 -5
- package/plugins/paseo/client/square-message.tsx +28 -24
- package/plugins/paseo/client/transform.ts +8 -5
- package/plugins/paseo/shared/card-presentation.ts +16 -0
- package/plugins/paseo/shared/message.ts +6 -11
- package/plugins/pi/square-pi.js +156 -417
- package/dist/channel-artifact.d.ts +0 -10
- package/dist/channel-artifact.js +0 -55
- package/dist/channel-delivery.d.ts +0 -11
- package/dist/channel-delivery.js +0 -51
- package/dist/channel-domain.d.ts +0 -3
- package/dist/channel-domain.js +0 -83
- package/dist/channel-facade.d.ts +0 -76
- package/dist/channel-facade.js +0 -1
- package/dist/channel-internal.d.ts +0 -126
- package/dist/channel-internal.js +0 -440
- package/dist/channel-metadata.d.ts +0 -11
- package/dist/channel-metadata.js +0 -1
- package/dist/channel-ports.d.ts +0 -241
- package/dist/channel-ports.js +0 -1
- package/dist/channel-presentation.d.ts +0 -17
- package/dist/channel-presentation.js +0 -22
- package/dist/channel-sqlite-schema.d.ts +0 -13
- package/dist/channel-sqlite-schema.js +0 -81
- package/dist/channel-transport.d.ts +0 -62
- package/dist/channel-transport.js +0 -1
- package/dist/channel.d.ts +0 -84
- package/dist/channel.js +0 -60
- package/dist/claude-delivery-status.d.ts +0 -13
- package/dist/claude-delivery-status.js +0 -46
- package/dist/cmd/notify-once.d.ts +0 -2
- package/dist/cmd/notify-once.js +0 -29
- package/dist/delivery-attempts.d.ts +0 -52
- package/dist/delivery-attempts.js +0 -73
- package/dist/delivery-effects.d.ts +0 -139
- package/dist/delivery-effects.js +0 -163
- package/dist/delivery-health.d.ts +0 -51
- package/dist/delivery-health.js +0 -173
- package/dist/delivery-operations.d.ts +0 -22
- package/dist/delivery-operations.js +0 -198
- package/dist/delivery-primitives.d.ts +0 -5
- package/dist/delivery-primitives.js +0 -3
- package/dist/delivery.d.ts +0 -56
- package/dist/delivery.js +0 -80
- package/dist/plugins/opencode/delivery.d.ts +0 -19
- package/dist/plugins/opencode/delivery.js +0 -223
- package/dist/presentation-operations.d.ts +0 -5
- package/dist/presentation-operations.js +0 -86
- package/dist/sqlite-channel.d.ts +0 -15
- package/dist/sqlite-channel.js +0 -600
- package/dist/square-index.d.ts +0 -16
- package/dist/square-index.js +0 -106
- package/dist/wake-preview.d.ts +0 -7
- package/dist/wake-preview.js +0 -15
- package/plugins/opencode/delivery.ts +0 -198
- /package/dist/{channel-values.d.ts → square-values.d.ts} +0 -0
- /package/dist/{channel-values.js → square-values.js} +0 -0
package/dist/artifact.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { type BuildOptions } from './model.js';
|
|
2
2
|
import type { SquareResourceOwner, SquareArtifactPort } from './ports.js';
|
|
3
3
|
import { type SquareState } from './square-sqlite.js';
|
|
4
|
-
import type { ChannelArtifact } from './channel-ports.js';
|
|
5
4
|
export { openExistingSquareDatabase } from './artifact-store.js';
|
|
6
|
-
export declare function channelArtifact(port: SquareArtifactPort): ChannelArtifact;
|
|
7
5
|
/** Explicit-lifetime holder of one reused artifact connection. Request cancellation
|
|
8
6
|
* never becomes the shared connection's lifetime; close drains the owner once. */
|
|
9
7
|
export declare function createSquareResourceOwner(): SquareResourceOwner;
|
|
@@ -24,7 +22,7 @@ export interface SquareSnapshot {
|
|
|
24
22
|
readonly state: SquareState;
|
|
25
23
|
readonly revision: number;
|
|
26
24
|
}
|
|
27
|
-
/**
|
|
25
|
+
/** Square social state and producer state share one synchronous artifact transaction. */
|
|
28
26
|
export declare function createArtifactPort(location: string, initial?: SquareState, externalSignal?: AbortSignal): SquareArtifactPort;
|
|
29
27
|
export declare function createSquareFile(location: string, state: SquareState, signal?: AbortSignal): Promise<boolean>;
|
|
30
28
|
export declare function writeSquareFile(location: string, state: SquareState, signal?: AbortSignal): Promise<void>;
|
package/dist/artifact.js
CHANGED
|
@@ -4,24 +4,10 @@ import { canonicalPath } from './canonical-path.js';
|
|
|
4
4
|
import { InternalSquareError, SquareError } from './model.js';
|
|
5
5
|
import { wrapSquareWrite } from './square-domain.js';
|
|
6
6
|
import { createSqliteDriver, initializeSqlite, exportSqliteState, inspectSqlite, emptyRuntimeState, squareStoreFormat } from './square-sqlite.js';
|
|
7
|
-
import { createChannelSqlite } from './sqlite-channel.js';
|
|
8
|
-
import { channelLowerSchema } from './channel-sqlite-schema.js';
|
|
9
7
|
import { resetStorageMeta } from './sqlite-framing.js';
|
|
10
8
|
import { createArtifactStore, createStoreFile, replaceStoreFile, inspectStore, observeStoreRevision } from './artifact-store.js';
|
|
11
|
-
import { channelReads, channelWrites } from './channel-artifact.js';
|
|
12
9
|
export { openExistingSquareDatabase } from './artifact-store.js';
|
|
13
|
-
|
|
14
|
-
return {
|
|
15
|
-
read: (select, signal) => port.read(facts => select(facts.channel), signal),
|
|
16
|
-
transactAs: (authority, change, signal) => port.transactAs(authority, facts => change(facts.channel), signal),
|
|
17
|
-
claimDelivery: (input, signal) => port.claimDelivery(input, signal),
|
|
18
|
-
prepareDelivery: (input, signal) => port.prepareDelivery(input, signal),
|
|
19
|
-
completeDelivery: (input, signal) => port.completeDelivery(input, signal),
|
|
20
|
-
cancelDeliveries: (seat, through, signal) => port.cancelDeliveries(seat, through, signal),
|
|
21
|
-
changed: (since, timeout, signal) => port.changed(since, timeout, signal), close: () => port.close(),
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
const format = squareStoreFormat(channelLowerSchema, resetStorageMeta);
|
|
10
|
+
const format = squareStoreFormat(resetStorageMeta);
|
|
25
11
|
/** Explicit-lifetime holder of one reused artifact connection. Request cancellation
|
|
26
12
|
* never becomes the shared connection's lifetime; close drains the owner once. */
|
|
27
13
|
export function createSquareResourceOwner() {
|
|
@@ -95,22 +81,21 @@ export async function createSquare(location, options, snippet, signal, includeGu
|
|
|
95
81
|
}
|
|
96
82
|
const readMethods = [
|
|
97
83
|
'header', 'participant', 'roster', 'attention', 'incomingAttention',
|
|
98
|
-
'activity', '
|
|
84
|
+
'activity', 'activityHeads', 'activities', 'activityCount', 'contextIndexes', 'sayOrdinals', 'recentSayTimes',
|
|
99
85
|
'audience', 'directed', 'directedAt', 'directedCount',
|
|
100
86
|
'receipt', 'receiptFacts', 'lease', 'participantCounts', 'seat', 'seatsForOwner',
|
|
101
|
-
'
|
|
87
|
+
'pendingPosts', 'postedPosts', 'ownsPost',
|
|
102
88
|
];
|
|
103
|
-
const writeMethods = ['applyLanding', 'putLease', 'removeLease', 'storeSeat', 'removeSeat', '
|
|
104
|
-
/**
|
|
89
|
+
const writeMethods = ['applyLanding', 'putLease', 'removeLease', 'storeSeat', 'removeSeat', 'markSeen', 'setSeatAttachment', 'insertNotice', 'ackMatching', 'acquirePost', 'settlePost', 'foldReceipt'];
|
|
90
|
+
/** Square social state and producer state share one synchronous artifact transaction. */
|
|
105
91
|
export function createArtifactPort(location, initial, externalSignal) {
|
|
106
|
-
const store = createArtifactStore(location, format, initial === undefined ? undefined : db => initializeSqlite(db, initial, 0
|
|
92
|
+
const store = createArtifactStore(location, format, initial === undefined ? undefined : db => initializeSqlite(db, initial, 0), externalSignal);
|
|
107
93
|
function transaction(select, write, authority, signal, settlement = false) {
|
|
108
94
|
return store.run(write, ({ db, guard, track }) => {
|
|
109
|
-
const
|
|
110
|
-
const driver = createSqliteDriver(db, channel);
|
|
95
|
+
const driver = createSqliteDriver(db);
|
|
111
96
|
track(driver);
|
|
112
|
-
const reads = Object.
|
|
113
|
-
const writes = Object.
|
|
97
|
+
const reads = Object.fromEntries(readMethods.map(name => [name, guard(driver[name])]));
|
|
98
|
+
const writes = Object.fromEntries(writeMethods.map(name => [name, guard(driver[name])]));
|
|
114
99
|
const gate = write ? wrapSquareWrite(reads, writes, authority) : undefined;
|
|
115
100
|
const value = select(gate?.facts ?? reads);
|
|
116
101
|
gate?.complete();
|
|
@@ -122,24 +107,15 @@ export function createArtifactPort(location, initial, externalSignal) {
|
|
|
122
107
|
transactAs: (authority, change, signal) => transaction(change, true, authority, signal),
|
|
123
108
|
settleAs: (authority, change) => transaction(change, true, authority, undefined, true),
|
|
124
109
|
revision: store.revision, deliveryRevision: store.deliveryRevision,
|
|
125
|
-
async claimDelivery(input, signal) { return (await port.transactAs({ kind: 'participant', seat: input.seat }, facts => facts.channel.claimDelivery(input), signal)).value; },
|
|
126
|
-
async prepareDelivery(input, signal) { return (await port.transactAs({ kind: 'participant', seat: input.seat }, facts => facts.channel.prepareDelivery(input), signal)).value; },
|
|
127
|
-
async completeDelivery(input, signal) { return (await port.transactAs({ kind: 'participant', seat: input.seat }, facts => facts.channel.completeDelivery(input), signal)).value; },
|
|
128
|
-
async cancelDeliveries(seat, through, signal) {
|
|
129
|
-
return (await port.transactAs({ kind: 'participant', seat }, facts => {
|
|
130
|
-
const boundary = through ?? facts.channel.header().lastMessageIndex;
|
|
131
|
-
return boundary < 0 ? false : facts.channel.cancelThrough(seat, boundary);
|
|
132
|
-
}, signal)).value;
|
|
133
|
-
},
|
|
134
110
|
changed: store.changed, close: store.close,
|
|
135
111
|
};
|
|
136
112
|
return port;
|
|
137
113
|
}
|
|
138
114
|
export function createSquareFile(location, state, signal) {
|
|
139
|
-
return createStoreFile(location, format, db => initializeSqlite(db, state, 0
|
|
115
|
+
return createStoreFile(location, format, db => initializeSqlite(db, state, 0), signal);
|
|
140
116
|
}
|
|
141
117
|
export function writeSquareFile(location, state, signal) {
|
|
142
|
-
return replaceStoreFile(location, format, (db, revision) => initializeSqlite(db, state, revision
|
|
118
|
+
return replaceStoreFile(location, format, (db, revision) => initializeSqlite(db, state, revision), signal);
|
|
143
119
|
}
|
|
144
120
|
export function readSquareSnapshot(location, signal) {
|
|
145
121
|
return inspectStore(location, format, db => ({ state: exportSqliteState(db), revision: createSqliteDriver(db).header().revision }), signal);
|
|
@@ -154,7 +130,7 @@ export const observeSquareRevision = (location) => observeStoreRevision(location
|
|
|
154
130
|
export const observeSquareDeliveryRevision = (location) => observeStoreRevision(location, format, true);
|
|
155
131
|
export async function diagnoseSquareFile(location) {
|
|
156
132
|
try {
|
|
157
|
-
return { problems: [], ...await inspectStore(await canonicalPath(location), format, db => inspectSqlite(db
|
|
133
|
+
return { problems: [], ...await inspectStore(await canonicalPath(location), format, db => inspectSqlite(db)) };
|
|
158
134
|
}
|
|
159
135
|
catch (error) {
|
|
160
136
|
return { unfixable: error instanceof Error ? error.message : String(error), problems: [] };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export { projectChannelNotification, renderSquareReminder } from './channel-presentation.js';
|
|
2
1
|
import type { DirectedNotificationRoute } from './model.js';
|
|
3
2
|
export declare const ATTENTION_BODY_MAX = 200;
|
|
4
3
|
export interface AttentionPreview {
|
|
@@ -8,7 +7,7 @@ export interface AttentionPreview {
|
|
|
8
7
|
actor: string;
|
|
9
8
|
route: DirectedNotificationRoute;
|
|
10
9
|
body: string;
|
|
11
|
-
|
|
10
|
+
metadataOnly?: boolean;
|
|
12
11
|
}
|
|
13
12
|
export declare function previewAttentionBody(body: string): string;
|
|
14
13
|
export declare function attentionBodyIsClipped(body: string): boolean;
|
|
@@ -20,26 +19,7 @@ export declare function projectNotification(attention: AttentionPreview): {
|
|
|
20
19
|
full: string;
|
|
21
20
|
preview: string;
|
|
22
21
|
description: string;
|
|
23
|
-
|
|
24
|
-
agora: boolean;
|
|
25
|
-
};
|
|
26
|
-
/** One stored message: Agora framing when an upper say owns it, Channel's raw frame otherwise. */
|
|
27
|
-
export declare function projectMessageNotification(input: {
|
|
28
|
-
squarePath: string;
|
|
29
|
-
recipient: string;
|
|
30
|
-
actor: string;
|
|
31
|
-
body: string;
|
|
32
|
-
preview?: string;
|
|
33
|
-
agora?: {
|
|
34
|
-
readonly actIndex: number;
|
|
35
|
-
readonly route: DirectedNotificationRoute;
|
|
36
|
-
};
|
|
37
|
-
}): {
|
|
38
|
-
text: string;
|
|
39
|
-
full: string;
|
|
40
|
-
preview: string;
|
|
41
|
-
description: string;
|
|
42
|
-
clipped: boolean;
|
|
22
|
+
isFull: boolean;
|
|
43
23
|
};
|
|
44
24
|
/** Full human expansion never substitutes a sender-owned preview for the stored body. */
|
|
45
25
|
export declare function renderAttentionFull(attention: AttentionPreview): string;
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { escapeSquareTags, projectChannelNotification } from './channel-presentation.js';
|
|
2
|
-
export { projectChannelNotification, renderSquareReminder } from './channel-presentation.js';
|
|
3
1
|
import { homedir } from 'node:os';
|
|
4
2
|
import { formatActivityId } from './square-core.js';
|
|
5
3
|
export const ATTENTION_BODY_MAX = 200;
|
|
@@ -21,14 +19,18 @@ export function renderAttentionDescription({ actor, route }) {
|
|
|
21
19
|
case 'bell': return `${actor} rang the bell`;
|
|
22
20
|
}
|
|
23
21
|
}
|
|
22
|
+
function escapeSquareTags(text) {
|
|
23
|
+
return text.replace(/<(\/?)(square-|channel-|system-reminder)/gi, '<\\$1$2');
|
|
24
|
+
}
|
|
24
25
|
function escapeAttribute(value) {
|
|
25
26
|
return value.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
|
|
26
27
|
}
|
|
27
28
|
/** The sole source adapter: transports and cards consume this projection, not delivery modes. */
|
|
28
29
|
export function projectNotification(attention) {
|
|
29
|
-
const body = attention.body.replace(/\r\n/g, '\n');
|
|
30
|
+
const body = attention.metadataOnly ? '(structured activity)' : attention.body.replace(/\r\n/g, '\n');
|
|
30
31
|
const description = renderAttentionDescription(attention);
|
|
31
32
|
const remaining = body.length - ATTENTION_BODY_MAX;
|
|
33
|
+
const isFull = !attention.metadataOnly && remaining <= 0;
|
|
32
34
|
const preview = previewAttentionBody(body);
|
|
33
35
|
const attributes = [
|
|
34
36
|
`square="${escapeAttribute(displayAttentionPath(attention.squarePath))}"`,
|
|
@@ -38,19 +40,14 @@ export function projectNotification(attention) {
|
|
|
38
40
|
...(remaining > 0 ? [`truncated="${remaining} more chars"`] : []),
|
|
39
41
|
];
|
|
40
42
|
return {
|
|
41
|
-
text: [`<square-activity ${attributes.join(' ')}>`, `${description}.`, '', escapeSquareTags(preview),
|
|
43
|
+
text: [`<square-activity ${attributes.join(' ')}>`, `${description}.`, '', escapeSquareTags(preview),
|
|
44
|
+
...(!isFull ? ['', escapeSquareTags(`Use square_catch with location=${JSON.stringify(attention.squarePath)}, as=${JSON.stringify(attention.recipient)}, id=${JSON.stringify(formatActivityId(attention.actIndex))} for the full activity.`)] : []),
|
|
45
|
+
'</square-activity>'].join('\n'),
|
|
42
46
|
full: ['<square-activity>', `· ${displayAttentionPath(attention.squarePath)} · ${attention.recipient} · ${formatActivityId(attention.actIndex)}`,
|
|
43
47
|
`● ${description}`, '', body, '</square-activity>'].join('\n'),
|
|
44
|
-
preview, description,
|
|
48
|
+
preview, description, isFull,
|
|
45
49
|
};
|
|
46
50
|
}
|
|
47
|
-
/** One stored message: Agora framing when an upper say owns it, Channel's raw frame otherwise. */
|
|
48
|
-
export function projectMessageNotification(input) {
|
|
49
|
-
if (input.agora === undefined)
|
|
50
|
-
return projectChannelNotification(input);
|
|
51
|
-
return projectNotification({ squarePath: input.squarePath, recipient: input.recipient, actor: input.actor,
|
|
52
|
-
route: input.agora.route, actIndex: input.agora.actIndex, body: input.body });
|
|
53
|
-
}
|
|
54
51
|
/** Full human expansion never substitutes a sender-owned preview for the stored body. */
|
|
55
52
|
export function renderAttentionFull(attention) { return projectNotification(attention).full; }
|
|
56
53
|
export function renderAttentionPreview(attention) { return projectNotification(attention).text; }
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
import type { Owner
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* Serialize a boundary across hook processes so one stale inbox cannot render twice.
|
|
5
|
-
*
|
|
6
|
-
* `lookup` returns the session's memberships for this boundary. It may reuse an observation it has
|
|
7
|
-
* verified against the current presence binding and artifact revision; the boundary still projects
|
|
8
|
-
* presentation evidence live. Every item is claimed and revalidated before the batch sink;
|
|
9
|
-
* after success, captured-seat commits record the winning batch, including budget omissions as
|
|
10
|
-
* clipped (no consumption receipt), without an eligibility veto. Coverage is conservative if a
|
|
11
|
-
* competing claim's removal lets a previously omitted body fit in the final preview.
|
|
12
|
-
* A source that cannot verify its reuse must read the inbox again.
|
|
13
|
-
*/
|
|
14
|
-
export declare function presentPendingAtBoundary<T>(identity: Owner, present: (context: string, delivered: PendingMembership[]) => T | Promise<T>, lookup?: (identity: Owner, env?: NodeJS.ProcessEnv, signal?: AbortSignal) => Promise<PendingMembership[]> | PendingMembership[], env?: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<T | undefined>;
|
|
1
|
+
import type { Owner } from './owner.js';
|
|
2
|
+
/** The sink must resolve only after the host has actually accepted the output. */
|
|
3
|
+
export declare function presentPendingAtBoundary<T>(owner: Owner, present: (context: string) => T | Promise<T>, env?: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<T | undefined>;
|
|
@@ -1,115 +1,40 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { withFileLock } from './file-lock.js';
|
|
4
|
-
import { createArtifactPort } from './artifact.js';
|
|
5
|
-
import { preparePendingPresentation } from './presentation-operations.js';
|
|
6
|
-
import { presentPreparedBatch } from './delivery-effects.js';
|
|
7
|
-
import { sessionInbox, projectBoundaryEligibility, pendingKey } from './inbox.js';
|
|
8
|
-
import { projectChannelNotification, projectNotification, renderSquareReminder } from './attention-presentation.js';
|
|
1
|
+
import { DeliveryLedger } from './delivery-ledger.js';
|
|
2
|
+
import { deliveryFrameOverhead, renderDeliveryReminder } from './delivery-frame.js';
|
|
9
3
|
const CONTEXT_MAX = 1200;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
function renderBoundary(inbox) {
|
|
14
|
-
const queued = inbox.flatMap((membership) => membership.notifications.map((notification) => ({ membership, notification })));
|
|
15
|
-
const blocks = [];
|
|
16
|
-
const agoraBlocks = [];
|
|
17
|
-
let remainingAgora = queued.filter(({ notification }) => notification.route !== undefined).length;
|
|
18
|
-
const entries = [];
|
|
19
|
-
let omitted = 0;
|
|
20
|
-
const omission = (count) => `${count} more ${count === 1 ? 'activity' : 'activities'} not shown.`;
|
|
21
|
-
for (const entry of queued) {
|
|
22
|
-
const { membership, notification } = entry;
|
|
23
|
-
// The omission line represents the rest of this batch too, but never consumes its bodies.
|
|
24
|
-
const coverage = { membership, messageIndex: notification.messageIndex, markSeen: false };
|
|
25
|
-
entries.push(coverage);
|
|
26
|
-
const projected = notification.route === undefined
|
|
27
|
-
? projectChannelNotification({ squarePath: membership.location, recipient: membership.seat.participant,
|
|
28
|
-
actor: notification.actor, body: notification.body, ...(notification.preview === undefined ? {} : { preview: notification.preview }) })
|
|
29
|
-
: projectNotification({ squarePath: membership.location, actIndex: notification.actIndex ?? notification.messageIndex,
|
|
30
|
-
recipient: membership.seat.participant, actor: notification.actor, route: notification.route,
|
|
31
|
-
body: notification.body, ...(notification.preview === undefined ? {} : { preview: notification.preview }) });
|
|
32
|
-
const block = projected.text;
|
|
33
|
-
if ('agora' in projected && projected.agora) {
|
|
34
|
-
remainingAgora -= 1;
|
|
35
|
-
const omittedAfter = omitted + remainingAgora;
|
|
36
|
-
const prospective = renderSquareReminder([...agoraBlocks, block, ...(omittedAfter > 0 ? [omission(omittedAfter)] : [])]);
|
|
37
|
-
if (prospective.length > CONTEXT_MAX) {
|
|
38
|
-
omitted += 1;
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
agoraBlocks.push(block);
|
|
42
|
-
}
|
|
43
|
-
blocks.push(block);
|
|
44
|
-
coverage.markSeen = !projected.clipped;
|
|
45
|
-
}
|
|
46
|
-
return {
|
|
47
|
-
context: renderSquareReminder([...blocks, ...(omitted > 0 ? [omission(omitted)] : [])]),
|
|
48
|
-
entries,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
async function presentPendingAtBoundaryUnlocked(identity, present, lookup = sessionInbox, env = process.env, signal) {
|
|
52
|
-
const inbox = await lookup(identity, env, signal);
|
|
53
|
-
if (signal?.aborted)
|
|
54
|
-
return undefined;
|
|
55
|
-
const pending = await projectBoundaryEligibility(inbox);
|
|
56
|
-
if (pending.length === 0 || signal?.aborted)
|
|
57
|
-
return undefined;
|
|
58
|
-
const artifacts = new Map();
|
|
59
|
-
const claims = [];
|
|
4
|
+
/** The sink must resolve only after the host has actually accepted the output. */
|
|
5
|
+
export async function presentPendingAtBoundary(owner, present, env = process.env, signal) {
|
|
6
|
+
const ledger = await DeliveryLedger.open({ location: env.SQUARE_DELIVERY });
|
|
60
7
|
try {
|
|
8
|
+
const session = await ledger.session(owner);
|
|
9
|
+
if (!session || session.state !== 'attached' || signal?.aborted)
|
|
10
|
+
return undefined;
|
|
11
|
+
const claim = await ledger.claimPresentation(owner, { generation: session.generation, budgetChars: CONTEXT_MAX - deliveryFrameOverhead({ tagged: true }), signal });
|
|
12
|
+
if (!claim)
|
|
13
|
+
return undefined;
|
|
14
|
+
let current;
|
|
61
15
|
try {
|
|
62
|
-
|
|
63
|
-
for (const entry of renderBoundary(pending).entries) {
|
|
64
|
-
const { location, seat } = entry.membership;
|
|
65
|
-
let artifact = artifacts.get(location);
|
|
66
|
-
if (artifact === undefined) {
|
|
67
|
-
// Keep connections alive through all signal-independent settlement/cleanup.
|
|
68
|
-
artifact = createArtifactPort(location);
|
|
69
|
-
artifacts.set(location, artifact);
|
|
70
|
-
}
|
|
71
|
-
const claim = await preparePendingPresentation({ artifact, location, participant: seat.participant,
|
|
72
|
-
message: entry.messageIndex, seat, markSeen: entry.markSeen, signal });
|
|
73
|
-
if (claim !== undefined)
|
|
74
|
-
claims.push({ key: pendingKey(entry.membership, entry.messageIndex), claim });
|
|
75
|
-
}
|
|
16
|
+
current = await claim.current(signal);
|
|
76
17
|
}
|
|
77
18
|
catch (error) {
|
|
78
|
-
|
|
79
|
-
await Promise.all(claims.map(({ claim }) => claim.release()));
|
|
19
|
+
await claim.settle('released');
|
|
80
20
|
throw error;
|
|
81
21
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
22
|
+
if (!current || signal?.aborted) {
|
|
23
|
+
await claim.settle('released');
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
let result;
|
|
27
|
+
try {
|
|
28
|
+
result = await present(renderDeliveryReminder(claim.cards.map(card => card.text), { tagged: true }));
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
await claim.settle('unknown');
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
await claim.settle('presented');
|
|
35
|
+
return result;
|
|
95
36
|
}
|
|
96
37
|
finally {
|
|
97
|
-
await
|
|
38
|
+
await ledger.close();
|
|
98
39
|
}
|
|
99
40
|
}
|
|
100
|
-
/**
|
|
101
|
-
* Serialize a boundary across hook processes so one stale inbox cannot render twice.
|
|
102
|
-
*
|
|
103
|
-
* `lookup` returns the session's memberships for this boundary. It may reuse an observation it has
|
|
104
|
-
* verified against the current presence binding and artifact revision; the boundary still projects
|
|
105
|
-
* presentation evidence live. Every item is claimed and revalidated before the batch sink;
|
|
106
|
-
* after success, captured-seat commits record the winning batch, including budget omissions as
|
|
107
|
-
* clipped (no consumption receipt), without an eligibility veto. Coverage is conservative if a
|
|
108
|
-
* competing claim's removal lets a previously omitted body fit in the final preview.
|
|
109
|
-
* A source that cannot verify its reuse must read the inbox again.
|
|
110
|
-
*/
|
|
111
|
-
export async function presentPendingAtBoundary(identity, present, lookup = sessionInbox, env = process.env, signal) {
|
|
112
|
-
const indexPath = squareIndexPath(env);
|
|
113
|
-
const ownerKey = createHash('sha256').update(JSON.stringify(identity)).digest('hex');
|
|
114
|
-
return withFileLock(`${indexPath}.boundary-${ownerKey}.lock`, { retryMs: 10, signal }, () => presentPendingAtBoundaryUnlocked(identity, present, lookup, env, signal));
|
|
115
|
-
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { isCardPresentation, type CardPresentation } from './plugins/paseo/shared/card-presentation.js';
|
package/dist/catch-decisions.js
CHANGED
|
@@ -64,19 +64,13 @@ export function decideCatch(facts, requestedName, options, at) {
|
|
|
64
64
|
}
|
|
65
65
|
let consumed;
|
|
66
66
|
try {
|
|
67
|
-
// Receipts are recorded by message coordinate; the batch hydration above already resolved each selected say.
|
|
68
|
-
let selected = new Map();
|
|
69
67
|
consumed = consumeReceived({
|
|
70
68
|
...squareReceiveRead(facts),
|
|
71
|
-
load(keys) { selected = facts.activities(keys).reduce((map, item) => map.set(item.index, item), new Map()); return selected; },
|
|
72
69
|
addressed(recipient, index) {
|
|
73
70
|
const head = facts.directed(recipient, { indexes: [index], kinds: ['say'] }, { edge: 'oldest', limit: 1 }).items[0];
|
|
74
71
|
return head === undefined ? undefined : { key: head.index, via: head.route };
|
|
75
72
|
},
|
|
76
|
-
insertReceipt: (ref, time) =>
|
|
77
|
-
const item = selected.get(ref.item);
|
|
78
|
-
return item?.kind === 'say' && facts.channel.markSeen(ref.recipient, [item.messageIndex], time) > 0;
|
|
79
|
-
},
|
|
73
|
+
insertReceipt: (ref, time) => facts.markSeen(ref.recipient, [ref.item], time) > 0,
|
|
80
74
|
}, viewer, selection, at);
|
|
81
75
|
}
|
|
82
76
|
catch (error) {
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
import { type DeliveryHandoffRef } from './delivery-ledger.js';
|
|
1
2
|
export { createClaudeWakeTransport, nativeTokens, nativePayload } from './claude-transport.js';
|
|
2
|
-
/** Recognize the whole frame first; one inactive token vetoes it before any evidence/receipt write. */
|
|
3
|
-
export declare function observeClaudeDelivery(sessionId: string, text: string, operation: 'guard' | 'admitted' | 'stored', env?: NodeJS.ProcessEnv): Promise<{
|
|
4
|
-
recognized: boolean;
|
|
5
|
-
current: boolean;
|
|
6
|
-
}>;
|
|
7
3
|
export type ClaudeBinding = {
|
|
8
|
-
readonly location: string;
|
|
9
|
-
readonly participant: string;
|
|
10
4
|
readonly session: string;
|
|
11
|
-
readonly generation
|
|
5
|
+
readonly generation: string;
|
|
12
6
|
};
|
|
7
|
+
/** Extract complete frames and hash the exact bytes observed by the native host. */
|
|
8
|
+
export declare function claudeFrames(text: string): readonly {
|
|
9
|
+
token: string;
|
|
10
|
+
payloadHash: string;
|
|
11
|
+
}[];
|
|
12
|
+
/** Guard pre-effect; admit and observe only the exact captured handoff after each native effect. */
|
|
13
|
+
export declare function observeClaudeDelivery(sessionId: string, text: string, operation: 'guard' | 'admitted' | 'stored', env?: NodeJS.ProcessEnv, captured?: readonly DeliveryHandoffRef[]): Promise<{
|
|
14
|
+
recognized: boolean;
|
|
15
|
+
current: boolean;
|
|
16
|
+
refs?: readonly DeliveryHandoffRef[];
|
|
17
|
+
}>;
|
package/dist/claude-delivery.js
CHANGED
|
@@ -1,108 +1,55 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { indexedSquareEntries } from './square-index.js';
|
|
6
|
-
import { parseMessageId } from './message.js';
|
|
1
|
+
import { DeliveryLedger } from './delivery-ledger.js';
|
|
2
|
+
import { harnessOwner } from './owner.js';
|
|
3
|
+
import { renderDeliveryReminder } from './delivery-frame.js';
|
|
4
|
+
import { hashPayload, nativeTokens } from './claude-transport.js';
|
|
7
5
|
export { createClaudeWakeTransport, nativeTokens, nativePayload } from './claude-transport.js';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
/** Extract complete frames and hash the exact bytes observed by the native host. */
|
|
7
|
+
export function claudeFrames(text) {
|
|
8
|
+
const prefix = renderDeliveryReminder([''], { tagged: false });
|
|
9
|
+
return [...text.matchAll(/(?:^|\n)(\[square-inbox:([a-zA-Z0-9-]+)\]\n[\s\S]*?\n\[\/square-inbox:\2\])(?=\n|$)/g)]
|
|
10
|
+
.flatMap(frame => {
|
|
11
|
+
const start = frame.index + frame[0].length - frame[1].length;
|
|
12
|
+
return text.slice(start - prefix.length, start) === prefix
|
|
13
|
+
? [{ token: frame[2], payloadHash: hashPayload(prefix + frame[1]) }] : [];
|
|
14
14
|
});
|
|
15
|
-
const paths = new Set(candidates.map(candidate => candidate.location));
|
|
16
|
-
for (const location of paths) {
|
|
17
|
-
const artifact = createArtifactPort(location);
|
|
18
|
-
try {
|
|
19
|
-
const found = await artifact.read((facts) => tokens.flatMap((token) => {
|
|
20
|
-
const evidence = facts.deliveryByToken(token);
|
|
21
|
-
if (evidence?.kind !== 'wake' || evidence.correlation?.harness !== 'claude' || evidence.correlation.token !== token
|
|
22
|
-
|| evidence.route?.kind !== 'claude-native' || evidence.route.address.sessionId !== sessionId)
|
|
23
|
-
return [];
|
|
24
|
-
const seat = facts.seat(evidence.participant);
|
|
25
|
-
return [{ artifact, evidence, location, index: parseMessageId(evidence.message),
|
|
26
|
-
...(seat === undefined ? {} : { seat: { participant: evidence.participant, owner: seat.owner, generation: evidence.generation } }) }];
|
|
27
|
-
}));
|
|
28
|
-
rows.push(...found.value);
|
|
29
|
-
if (found.value.length === 0)
|
|
30
|
-
await artifact.close();
|
|
31
|
-
}
|
|
32
|
-
catch {
|
|
33
|
-
await artifact.close();
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return rows;
|
|
37
|
-
}
|
|
38
|
-
/** The token, participant, activity and captured generation must still be the exact wake. */
|
|
39
|
-
function sameCapturedEvidence(facts, row) {
|
|
40
|
-
const token = row.evidence.claimToken;
|
|
41
|
-
const evidence = token === undefined ? undefined : facts.deliveryByToken(token);
|
|
42
|
-
return token !== undefined && evidence !== undefined && row.seat !== undefined && row.seat.participant === row.evidence.participant
|
|
43
|
-
&& row.seat.generation === row.evidence.generation && evidence.kind === 'wake' && evidence.claimToken === token
|
|
44
|
-
&& evidence.participant === row.evidence.participant && evidence.message === row.evidence.message
|
|
45
|
-
&& evidence.generation === row.evidence.generation
|
|
46
|
-
&& evidence.correlation?.harness === 'claude' && evidence.correlation.token === token
|
|
47
|
-
&& evidence.correlation.payloadHash === row.evidence.correlation?.payloadHash
|
|
48
|
-
&& ['dispatching', 'unknown', 'accepted'].includes(evidence.outcome);
|
|
49
|
-
}
|
|
50
|
-
function capturedInput(row, now) {
|
|
51
|
-
if (row.seat === undefined || row.index === undefined || row.evidence.route === undefined)
|
|
52
|
-
return undefined;
|
|
53
|
-
return { location: row.location, participant: row.evidence.participant, generation: row.seat.generation,
|
|
54
|
-
owner: row.seat.owner, index: row.index, route: row.evidence.route, now };
|
|
55
15
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return input !== undefined && sameCapturedEvidence(facts, row) && nativePendingHead(facts, input) !== undefined;
|
|
59
|
-
}
|
|
60
|
-
/** Recognize the whole frame first; one inactive token vetoes it before any evidence/receipt write. */
|
|
61
|
-
export async function observeClaudeDelivery(sessionId, text, operation, env = process.env) {
|
|
16
|
+
/** Guard pre-effect; admit and observe only the exact captured handoff after each native effect. */
|
|
17
|
+
export async function observeClaudeDelivery(sessionId, text, operation, env = process.env, captured) {
|
|
62
18
|
const tokens = nativeTokens(text);
|
|
63
|
-
if (tokens.length
|
|
19
|
+
if (!tokens.length)
|
|
64
20
|
return { recognized: false, current: false };
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
const
|
|
21
|
+
const frames = claudeFrames(text);
|
|
22
|
+
const owner = harnessOwner({ channel: 'claude-code', session: sessionId });
|
|
23
|
+
const ledger = await DeliveryLedger.open({ location: env.SQUARE_DELIVERY });
|
|
68
24
|
try {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|| !['dispatching', 'unknown', 'accepted'].includes(row.evidence.outcome))
|
|
75
|
-
return false;
|
|
76
|
-
const matching = frames.filter(frame => frame[2] === token);
|
|
77
|
-
return matching.length === tokens.filter(candidate => candidate === token).length
|
|
78
|
-
&& matching.every(frame => hashPayload(frame[1]) === hash);
|
|
79
|
-
});
|
|
80
|
-
if (!recognized || !contentCurrent)
|
|
25
|
+
const found = await Promise.all(tokens.map(token => ledger.handoff(token)));
|
|
26
|
+
const recognized = found.some(Boolean);
|
|
27
|
+
if (!recognized)
|
|
28
|
+
return { recognized: false, current: false };
|
|
29
|
+
if (frames.length !== tokens.length || new Set(tokens).size !== tokens.length || found.some(ref => !ref))
|
|
81
30
|
return { recognized, current: false };
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
31
|
+
const refs = found;
|
|
32
|
+
if (refs.some((ref, i) => ref.owner !== owner || ref.routeKind !== 'claude-native' || ref.token !== frames[i].token || ref.payloadHash !== frames[i].payloadHash))
|
|
33
|
+
return { recognized, current: false };
|
|
34
|
+
if (operation === 'admitted' && (!Array.isArray(captured) || captured.length !== refs.length || refs.some((ref, i) => {
|
|
35
|
+
const old = captured[i];
|
|
36
|
+
return !old || old.token !== ref.token || old.owner !== ref.owner || old.generation !== ref.generation || old.payloadHash !== ref.payloadHash || old.routeKind !== ref.routeKind;
|
|
37
|
+
})))
|
|
38
|
+
return { recognized, current: false };
|
|
39
|
+
if (operation === 'guard') {
|
|
40
|
+
for (const ref of refs)
|
|
41
|
+
if (!await ledger.guardHandoff(ref))
|
|
42
|
+
return { recognized, current: false };
|
|
43
|
+
return { recognized, current: true, refs };
|
|
86
44
|
}
|
|
87
|
-
for (const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const seat = row.seat;
|
|
91
|
-
const current = (facts) => currentRow(facts, row, Date.now());
|
|
92
|
-
const admitted = await row.artifact.transactAs({ kind: 'participant', seat }, facts => current(facts) && facts.channel.completeDelivery({ seat, message: row.evidence.message,
|
|
93
|
-
claimToken: row.evidence.claimToken, outcome: 'accepted', at: Date.now() })).catch(() => ({ value: false }));
|
|
94
|
-
if (!admitted.value)
|
|
45
|
+
for (const ref of refs) {
|
|
46
|
+
const recorded = operation === 'admitted' ? await ledger.acceptHandoff(ref) : await ledger.observePresentation(ref);
|
|
47
|
+
if (!recorded)
|
|
95
48
|
return { recognized, current: false };
|
|
96
|
-
if (operation === 'stored') {
|
|
97
|
-
await presentPending({ artifact: row.artifact, location: row.location,
|
|
98
|
-
// Persisted coverage decides consumption; the current renderer never guesses.
|
|
99
|
-
participant: row.evidence.participant, message: row.evidence.message, seat, observationOf: row.evidence.claimToken,
|
|
100
|
-
markSeen: row.evidence.coverage !== 'clipped', sink: { present() { } }, current });
|
|
101
|
-
}
|
|
102
49
|
}
|
|
103
50
|
return { recognized, current: true };
|
|
104
51
|
}
|
|
105
52
|
finally {
|
|
106
|
-
await
|
|
53
|
+
await ledger.close();
|
|
107
54
|
}
|
|
108
55
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createHash, randomBytes } from 'node:crypto';
|
|
2
2
|
import { promises as fs } from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import { deliveryLedgerLocation } from './delivery-ledger.js';
|
|
4
5
|
import { z } from 'zod/v4';
|
|
5
6
|
import { withFileLock } from './file-lock.js';
|
|
6
|
-
import { squareIndexPath } from './square-index.js';
|
|
7
7
|
import { SquareError } from './model.js';
|
|
8
8
|
const LEASE_MS = 10 * 60 * 1000;
|
|
9
9
|
const text = z.string().max(4096).refine(value => value.trim().length > 0 && !/[\x00-\x1f\x7f]/u.test(value));
|
|
@@ -12,7 +12,7 @@ const coordinate = z.object({
|
|
|
12
12
|
});
|
|
13
13
|
const record = coordinate.extend({ token: z.string().regex(/^[a-f0-9]{64}$/u), expiresAt: z.number() });
|
|
14
14
|
const unavailable = () => new SquareError('unavailable', 'Claude MCP call context unavailable');
|
|
15
|
-
const storeRoot = (env) => path.join(path.dirname(
|
|
15
|
+
const storeRoot = (env) => path.join(path.dirname(deliveryLedgerLocation(env)), 'claude-mcp-calls');
|
|
16
16
|
const callFile = (root, id) => path.join(root, `${createHash('sha256').update(id).digest('hex')}.json`);
|
|
17
17
|
async function readRecord(file) {
|
|
18
18
|
try {
|