@astrosheep/square 0.4.0 → 0.4.2
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-boundary-state.d.ts +20 -0
- package/dist/claude-boundary-state.js +84 -0
- package/dist/claude-hook.d.ts +2 -0
- package/dist/claude-hook.js +231 -0
- package/dist/claude-mcp-context.d.ts +1 -1
- package/dist/claude-mcp-context.js +12 -3
- package/dist/cli/context.js +7 -9
- package/dist/cli/observation-commands.d.ts +4 -1
- package/dist/cli/observation-commands.js +37 -4
- package/dist/cli/registry.js +5 -2
- 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-claude.js +3 -4
- package/dist/harness-links.js +63 -7
- package/dist/harness.js +12 -17
- package/dist/help.js +4 -1
- 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 +2 -3
- 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 +7 -39
- package/examples/symmetric-domains.mjs +29 -249
- package/guides/change-waits.md +13 -53
- package/guides/claude-native.md +7 -171
- package/guides/codex-delivery.md +55 -80
- package/guides/delivery-primitives.md +48 -207
- package/guides/mcp.md +1 -1
- 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 +2 -3
- package/plugins/claude/hooks/hooks.json +11 -1
- 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/claude-delivery.d.ts +0 -12
- package/dist/claude-delivery.js +0 -108
- package/dist/claude-mod.d.ts +0 -8
- package/dist/claude-mod.js +0 -172
- package/dist/claude-transport.d.ts +0 -5
- package/dist/claude-transport.js +0 -58
- 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/claude/hooks/register.js +0 -132
- package/plugins/claude/types.d.ts +0 -7
- 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) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
declare const stateSchema: z.ZodObject<{
|
|
3
|
+
generation: z.ZodString;
|
|
4
|
+
promptId: z.ZodOptional<z.ZodString>;
|
|
5
|
+
stopEpoch: z.ZodString;
|
|
6
|
+
consumed: z.ZodBoolean;
|
|
7
|
+
ended: z.ZodBoolean;
|
|
8
|
+
}, z.core.$strip>;
|
|
9
|
+
export type ClaudeBoundary = z.infer<typeof stateSchema>;
|
|
10
|
+
/** Receiver operational facts only: no cards, claims, or payload copies. */
|
|
11
|
+
export declare function claudeBoundaryStore(session: string, env: NodeJS.ProcessEnv): {
|
|
12
|
+
start(attach: () => Promise<string | false>): Promise<void>;
|
|
13
|
+
end(): Promise<ClaudeBoundary | undefined>;
|
|
14
|
+
prompt(promptId: string | undefined): Promise<void>;
|
|
15
|
+
capture(promptId: string): Promise<ClaudeBoundary | undefined>;
|
|
16
|
+
current(captured: ClaudeBoundary): Promise<boolean>;
|
|
17
|
+
consume(captured: ClaudeBoundary): Promise<void>;
|
|
18
|
+
watch<T>(signal: AbortSignal, run: () => Promise<T>): Promise<T>;
|
|
19
|
+
};
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { promises as fs } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { z } from 'zod/v4';
|
|
5
|
+
import { deliveryLedgerLocation } from './delivery-ledger.js';
|
|
6
|
+
import { withFileLock } from './file-lock.js';
|
|
7
|
+
const stateSchema = z.object({
|
|
8
|
+
generation: z.string().min(1), promptId: z.string().min(1).optional(),
|
|
9
|
+
stopEpoch: z.string().min(1), consumed: z.boolean(), ended: z.boolean(),
|
|
10
|
+
});
|
|
11
|
+
/** Receiver operational facts only: no cards, claims, or payload copies. */
|
|
12
|
+
export function claudeBoundaryStore(session, env) {
|
|
13
|
+
const root = path.join(path.dirname(deliveryLedgerLocation(env)), 'claude-hooks');
|
|
14
|
+
const key = createHash('sha256').update(session).digest('hex');
|
|
15
|
+
const file = path.join(root, `${key}.json`);
|
|
16
|
+
async function mutate(change) {
|
|
17
|
+
await fs.mkdir(root, { recursive: true, mode: 0o700 });
|
|
18
|
+
await fs.chmod(root, 0o700);
|
|
19
|
+
return withFileLock(path.join(root, `${key}.lock`), { retryMs: 10, signal: AbortSignal.timeout(2500) }, async () => {
|
|
20
|
+
let state;
|
|
21
|
+
try {
|
|
22
|
+
state = stateSchema.parse(JSON.parse(await fs.readFile(file, 'utf8')));
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
if (error.code !== 'ENOENT')
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
return change(state, async (next) => {
|
|
29
|
+
const temporary = `${file}.${randomUUID()}.tmp`;
|
|
30
|
+
try {
|
|
31
|
+
await fs.writeFile(temporary, JSON.stringify(next), { mode: 0o600, flag: 'wx' });
|
|
32
|
+
await fs.rename(temporary, file);
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
await fs.rm(temporary, { force: true });
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
const matches = (state, captured) => !!state && !state.ended && !state.consumed
|
|
41
|
+
&& state.generation === captured.generation && state.promptId === captured.promptId && state.stopEpoch === captured.stopEpoch;
|
|
42
|
+
return {
|
|
43
|
+
async start(attach) {
|
|
44
|
+
await mutate(async (state, save) => {
|
|
45
|
+
if (state)
|
|
46
|
+
await save({ ...state, promptId: undefined, ended: true, stopEpoch: randomUUID() });
|
|
47
|
+
const generation = await attach();
|
|
48
|
+
if (generation)
|
|
49
|
+
await save({ generation, stopEpoch: randomUUID(), consumed: false, ended: false });
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
async end() {
|
|
53
|
+
return mutate(async (state, save) => {
|
|
54
|
+
if (state)
|
|
55
|
+
await save({ ...state, ended: true, stopEpoch: randomUUID() });
|
|
56
|
+
return state;
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
async prompt(promptId) {
|
|
60
|
+
await mutate(async (state, save) => {
|
|
61
|
+
if (state && !state.ended)
|
|
62
|
+
await save({ ...state, promptId, stopEpoch: randomUUID(), consumed: false });
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
capture(promptId) {
|
|
66
|
+
return mutate(async (state) => state && !state.ended && !state.consumed && state.promptId === promptId ? state : undefined);
|
|
67
|
+
},
|
|
68
|
+
current(captured) { return mutate(async (state) => matches(state, captured)); },
|
|
69
|
+
async consume(captured) {
|
|
70
|
+
await mutate(async (state, save) => {
|
|
71
|
+
if (matches(state, captured))
|
|
72
|
+
await save({ ...state, consumed: true });
|
|
73
|
+
});
|
|
74
|
+
},
|
|
75
|
+
async watch(signal, run) {
|
|
76
|
+
await fs.mkdir(root, { recursive: true, mode: 0o700 });
|
|
77
|
+
const lock = path.join(root, `${key}.watch.lock`);
|
|
78
|
+
const handle = await fs.open(lock, 'a', 0o600);
|
|
79
|
+
await handle.close();
|
|
80
|
+
// A newer Stop waits through bounded shutdown; it must not skip a busy lock.
|
|
81
|
+
return withFileLock(lock, { retryMs: 10, signal }, run);
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare function runClaudeHook(text: string, env?: NodeJS.ProcessEnv, sink?: (value: string) => void | Promise<void>): Promise<string>;
|
|
2
|
+
export declare function runClaudeWatch(text: string, env?: NodeJS.ProcessEnv, signal?: AbortSignal, output?: (frame: string) => Promise<void>): Promise<string>;
|