@astrosheep/square 0.3.93 → 0.3.95
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/activity-feed.d.ts +3 -5
- package/dist/activity-feed.js +16 -14
- package/dist/artifact.js +3 -3
- package/dist/attention.d.ts +19 -0
- package/dist/attention.js +30 -0
- package/dist/boundary-presentation.d.ts +2 -2
- package/dist/boundary-presentation.js +33 -40
- package/dist/catch-decisions.d.ts +5 -8
- package/dist/catch-decisions.js +45 -25
- package/dist/claude-delivery-status.js +10 -4
- package/dist/claude-delivery.js +19 -11
- package/dist/claude-mod.js +24 -53
- package/dist/cli/context.d.ts +9 -4
- package/dist/cli/context.js +30 -27
- package/dist/cli/express-presentation.d.ts +3 -1
- package/dist/cli/express-presentation.js +28 -16
- package/dist/cli/json.d.ts +3 -0
- package/dist/cli/json.js +14 -0
- package/dist/cli/mcp-server-command.js +2 -2
- package/dist/cli/observation-commands.d.ts +1 -3
- package/dist/cli/observation-commands.js +43 -56
- package/dist/cli/program.js +3 -3
- package/dist/cli/registry.js +7 -6
- package/dist/cli/square-commands.d.ts +0 -2
- package/dist/cli/square-commands.js +30 -44
- package/dist/codex-hook.d.ts +3 -2
- package/dist/codex-hook.js +13 -4
- package/dist/codex-queue.js +4 -4
- package/dist/decisions.d.ts +5 -16
- package/dist/decisions.js +9 -33
- package/dist/delivery-attempts.d.ts +52 -0
- package/dist/delivery-attempts.js +73 -0
- package/dist/delivery-deferral.js +2 -2
- package/dist/delivery-effects.d.ts +139 -0
- package/dist/delivery-effects.js +163 -0
- package/dist/delivery-health.d.ts +0 -1
- package/dist/delivery-health.js +1 -3
- package/dist/delivery-operations.d.ts +2 -2
- package/dist/delivery-operations.js +76 -101
- package/dist/delivery-primitives.d.ts +5 -0
- package/dist/delivery-primitives.js +3 -0
- package/dist/delivery.d.ts +1 -1
- package/dist/delivery.js +7 -6
- package/dist/help.js +14 -14
- package/dist/http-delivery.d.ts +16 -0
- package/dist/http-delivery.js +101 -0
- package/dist/inbox.d.ts +9 -22
- package/dist/inbox.js +23 -27
- package/dist/index.d.ts +1 -3
- package/dist/index.js +1 -1
- package/dist/mcp.d.ts +3 -0
- package/dist/mcp.js +23 -24
- package/dist/model.d.ts +12 -21
- package/dist/model.js +5 -8
- package/dist/notifications.d.ts +1 -1
- package/dist/notifications.js +8 -5
- package/dist/operation-presentation.d.ts +6 -6
- package/dist/operation-presentation.js +55 -58
- package/dist/owner.d.ts +11 -0
- package/dist/owner.js +32 -0
- package/dist/paseo-delivery.d.ts +4 -1
- package/dist/paseo-delivery.js +7 -12
- package/dist/paseo-state.d.ts +2 -2
- package/dist/paseo-state.js +6 -4
- package/dist/plugins/opencode/delivery.js +27 -15
- package/dist/ports.d.ts +18 -47
- package/dist/presentation-operations.d.ts +14 -19
- package/dist/presentation-operations.js +51 -61
- package/dist/presentation.d.ts +7 -17
- package/dist/presentation.js +46 -91
- package/dist/receive.d.ts +66 -0
- package/dist/receive.js +54 -0
- package/dist/runtime.d.ts +1 -8
- package/dist/runtime.js +2 -17
- package/dist/seats.d.ts +2 -3
- package/dist/seats.js +7 -15
- package/dist/session-lifecycle.d.ts +6 -2
- package/dist/session-lifecycle.js +39 -11
- package/dist/sqlite-square.d.ts +36 -23
- package/dist/sqlite-square.js +162 -211
- package/dist/square-api.d.ts +16 -11
- package/dist/square-api.js +27 -23
- package/dist/square-core.d.ts +0 -14
- package/dist/square-core.js +0 -9
- package/dist/square-domain.d.ts +4 -2
- package/dist/square-domain.js +65 -52
- package/dist/square-facade.d.ts +47 -105
- package/dist/square-index.d.ts +5 -13
- package/dist/square-index.js +13 -38
- package/dist/square-legacy-framing.d.ts +2 -0
- package/dist/square-legacy-framing.js +8 -0
- package/dist/square-location.d.ts +1 -14
- package/dist/square-location.js +4 -70
- package/dist/square-migration-host-ledger.d.ts +2 -2
- package/dist/square-migration-host-ledger.js +6 -5
- package/dist/square-migration.d.ts +4 -1
- package/dist/square-migration.js +93 -38
- package/dist/square-receive.d.ts +6 -0
- package/dist/square-receive.js +18 -0
- package/dist/square-sqlite.d.ts +9 -3
- package/dist/square-sqlite.js +139 -114
- package/dist/square-tools.d.ts +7 -10
- package/dist/square-tools.js +21 -61
- package/dist/stream.js +4 -5
- package/dist/views.d.ts +23 -13
- package/dist/views.js +47 -53
- package/dist/wakes.d.ts +1 -5
- package/dist/wakes.js +4 -6
- package/examples/symmetric-domains.mjs +250 -0
- package/guides/application-facade-delivery-evidence.md +6 -6
- package/guides/attention-primitive.md +32 -0
- package/guides/change-waits.md +2 -2
- package/guides/claude-native.md +18 -9
- package/guides/codex-delivery.md +17 -6
- package/guides/delivery-primitives.md +180 -0
- package/guides/mcp-connection-lifetime.md +1 -1
- package/guides/mcp.md +3 -3
- package/guides/migration.md +148 -151
- package/guides/operation-cancellation.md +1 -1
- package/guides/participant-ownership.md +78 -47
- package/guides/pi-tools.md +34 -5
- package/guides/primitive-catch-consolidation.md +5 -5
- package/guides/primitive-history-consolidation.md +2 -2
- package/guides/primitive-status-consolidation.md +3 -3
- package/guides/primitive-storage-consolidation.md +1 -1
- package/guides/receive-primitive.md +90 -0
- package/guides/row-runtime-delivery-evidence.md +1 -1
- package/guides/row-storage-design.md +22 -28
- package/guides/square-api.md +314 -91
- package/guides/symmetric-domains.md +92 -0
- package/package.json +14 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/skills/square/SKILL.md +2 -6
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/opencode/delivery.ts +27 -17
- package/plugins/paseo/client/palette.ts +18 -18
- package/plugins/paseo/client/square-message.tsx +17 -23
- package/plugins/pi/square-pi.js +17 -17
- package/skills/square/SKILL.md +2 -6
- package/dist/square-backend.d.ts +0 -13
- package/dist/square-backend.js +0 -1
- package/dist/square-stdio.d.ts +0 -22
- package/dist/square-stdio.js +0 -235
- package/dist/stdio-server.d.ts +0 -3
- package/dist/stdio-server.js +0 -212
- package/guides/backends.md +0 -193
package/dist/activity-feed.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import type { SquareRead } from './ports.js';
|
|
2
|
-
import type {
|
|
3
|
-
/**
|
|
4
|
-
export declare function
|
|
5
|
-
kind: 'say';
|
|
6
|
-
}>[];
|
|
2
|
+
import type { ExpressContext } from './square-facade.js';
|
|
3
|
+
/** A bounded, nonconsuming glimpse; seen rows beyond a cursor hole must not reappear. */
|
|
4
|
+
export declare function expressionContext(facts: SquareRead, name: string): ExpressContext;
|
package/dist/activity-feed.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { formatActivityId } from './square-core.js';
|
|
2
|
+
const CONTEXT_LIMIT = 5;
|
|
3
|
+
const PREVIEW_CHARS = 400;
|
|
4
|
+
/** A bounded, nonconsuming glimpse; seen rows beyond a cursor hole must not reappear. */
|
|
5
|
+
export function expressionContext(facts, name) {
|
|
6
|
+
const filter = { afterIndex: facts.participant(name)?.lastJoinIndex ?? -1, unseenOnly: true };
|
|
7
|
+
const page = facts.directed(name, filter, { edge: 'newest', limit: CONTEXT_LIMIT });
|
|
8
|
+
const activities = facts.activities(page.items.map(head => head.index)).filter(act => act.kind === 'say')
|
|
9
|
+
.sort((a, b) => a.index - b.index).map(act => {
|
|
10
|
+
const body = act.body ?? '(structured activity)';
|
|
11
|
+
// Keep the boundary between Unicode code points, including non-BMP characters.
|
|
12
|
+
const chars = Array.from(body.slice(0, PREVIEW_CHARS * 2));
|
|
13
|
+
const preview = chars.slice(0, PREVIEW_CHARS).join('');
|
|
14
|
+
return { id: formatActivityId(act.index), actor: act.actor, preview, truncated: preview.length < body.length };
|
|
15
|
+
});
|
|
16
|
+
return { activities, remaining: Math.max(0, facts.directedCount(name, filter) - activities.length) };
|
|
15
17
|
}
|
package/dist/artifact.js
CHANGED
|
@@ -117,13 +117,13 @@ function synchronous(value) {
|
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
const readMethods = [
|
|
120
|
-
'header', 'participant', 'roster', 'attention', 'incomingAttention',
|
|
120
|
+
'header', 'participant', 'roster', 'attention', 'incomingAttention',
|
|
121
121
|
'activity', 'activityHeads', 'activities', 'activityCount', 'contextIndexes', 'sayOrdinals', 'recentSayTimes',
|
|
122
122
|
'audience', 'directed', 'directedAt', 'directedCount', 'pendingAudience',
|
|
123
|
-
'receipt', 'receiptFacts', 'receiptAssignments', 'lease', 'participantCounts', 'seat', '
|
|
123
|
+
'receipt', 'receiptFacts', 'receiptAssignments', 'lease', 'participantCounts', 'seat', 'seatsForOwner',
|
|
124
124
|
'deliveryEvidence', 'deliveryByToken', 'cancelledThrough', 'deliveryDeferrals',
|
|
125
125
|
];
|
|
126
|
-
const writeMethods = ['applyLanding', 'markSeen', 'putLease', 'removeLease', 'storeSeat', 'removeSeat', 'updateWakeAddress', 'cancelThrough', 'claimDelivery', 'prepareDelivery', 'completeDelivery', 'observeNativeDelivery', 'putDeliveryDeferral', 'renewDeliveryDeferral', 'releaseDeliveryDeferral'];
|
|
126
|
+
const writeMethods = ['applyLanding', 'markSeen', 'putLease', 'removeLease', 'storeSeat', 'removeSeat', 'updateWakeAddress', 'reattachSeat', 'detachSeat', 'cancelThrough', 'claimDelivery', 'prepareDelivery', 'completeDelivery', 'observeNativeDelivery', 'putDeliveryDeferral', 'renewDeliveryDeferral', 'releaseDeliveryDeferral'];
|
|
127
127
|
/** One connection, ordered operations, guarded synchronous transaction capabilities. */
|
|
128
128
|
export function createArtifactPort(location, initial, externalSignal) {
|
|
129
129
|
let database;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type AudienceRoute = 'mention' | 'reply' | 'attention' | 'bell';
|
|
2
|
+
export interface AudienceRecipient {
|
|
3
|
+
readonly recipient: string;
|
|
4
|
+
readonly route: AudienceRoute;
|
|
5
|
+
}
|
|
6
|
+
export interface AudienceInput {
|
|
7
|
+
readonly actor: string;
|
|
8
|
+
readonly recipients: readonly string[];
|
|
9
|
+
readonly mentions?: readonly string[];
|
|
10
|
+
readonly replyAuthor?: string;
|
|
11
|
+
readonly reach?: 'bell';
|
|
12
|
+
readonly incoming?: readonly {
|
|
13
|
+
readonly listener: string;
|
|
14
|
+
readonly kind: 'listen' | 'ignore';
|
|
15
|
+
}[];
|
|
16
|
+
readonly sameIdentity?: (a: string, b: string) => boolean;
|
|
17
|
+
}
|
|
18
|
+
/** Resolve attention against the caller's eligible recipient snapshot. */
|
|
19
|
+
export declare function resolveAudience(input: AudienceInput): readonly AudienceRecipient[];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Resolve attention against the caller's eligible recipient snapshot. */
|
|
2
|
+
export function resolveAudience(input) {
|
|
3
|
+
const same = input.sameIdentity ?? ((a, b) => a === b);
|
|
4
|
+
const incoming = input.incoming ?? [];
|
|
5
|
+
const audience = [];
|
|
6
|
+
const add = (requested, route) => {
|
|
7
|
+
const recipient = input.recipients.find((item) => same(item, requested));
|
|
8
|
+
if (recipient === undefined || same(recipient, input.actor)
|
|
9
|
+
|| audience.some((item) => same(item.recipient, recipient)))
|
|
10
|
+
return;
|
|
11
|
+
if (input.reach !== 'bell' && incoming.some((item) => item.kind === 'ignore' && same(item.listener, recipient)))
|
|
12
|
+
return;
|
|
13
|
+
audience.push({ recipient, route });
|
|
14
|
+
};
|
|
15
|
+
if (input.reach === 'bell') {
|
|
16
|
+
for (const recipient of input.recipients)
|
|
17
|
+
add(recipient, 'bell');
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
for (const mention of input.mentions ?? [])
|
|
21
|
+
add(mention, 'mention');
|
|
22
|
+
if (input.replyAuthor !== undefined)
|
|
23
|
+
add(input.replyAuthor, 'reply');
|
|
24
|
+
for (const recipient of input.recipients) {
|
|
25
|
+
if (incoming.some((item) => item.kind === 'listen' && same(item.listener, recipient)))
|
|
26
|
+
add(recipient, 'attention');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return audience;
|
|
30
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { pendingAtBoundary } from './inbox.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Owner, PendingMembership } from './model.js';
|
|
3
3
|
export { pendingAtBoundary };
|
|
4
4
|
export declare function renderPendingAtBoundary(inbox: PendingMembership[]): string;
|
|
5
5
|
/**
|
|
@@ -11,4 +11,4 @@ export declare function renderPendingAtBoundary(inbox: PendingMembership[]): str
|
|
|
11
11
|
* after success, captured-seat commits record only its included items without an eligibility veto.
|
|
12
12
|
* A source that cannot verify its reuse must read the inbox again.
|
|
13
13
|
*/
|
|
14
|
-
export declare function presentPendingAtBoundary<T>(identity:
|
|
14
|
+
export declare function presentPendingAtBoundary<T>(identity: Owner, present: (context: string) => T | Promise<T>, lookup?: (identity: Owner, env?: NodeJS.ProcessEnv, signal?: AbortSignal) => Promise<PendingMembership[]> | PendingMembership[], env?: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<T | undefined>;
|
|
@@ -3,6 +3,7 @@ import { createHash } from 'node:crypto';
|
|
|
3
3
|
import { withFileLock } from './file-lock.js';
|
|
4
4
|
import { createArtifactPort } from './artifact.js';
|
|
5
5
|
import { preparePendingPresentation } from './presentation-operations.js';
|
|
6
|
+
import { presentPreparedBatch } from './delivery-effects.js';
|
|
6
7
|
import { sessionInbox, projectBoundaryEligibility, pendingAtBoundary, pendingKey } from './inbox.js';
|
|
7
8
|
import { attentionBodyIsClipped, renderAttentionPreview, renderSquareReminder } from './attention-presentation.js';
|
|
8
9
|
export { pendingAtBoundary };
|
|
@@ -56,53 +57,45 @@ async function presentPendingAtBoundaryUnlocked(identity, present, lookup = sess
|
|
|
56
57
|
const artifacts = new Map();
|
|
57
58
|
const claims = [];
|
|
58
59
|
try {
|
|
59
|
-
// Claim the entire candidate batch before its one external effect. No later item needs
|
|
60
|
-
// a new claim (and new eligibility decision) after its bytes have already been rendered.
|
|
61
|
-
for (const entry of renderBoundary(pending).complete) {
|
|
62
|
-
const { location, seat } = entry.membership;
|
|
63
|
-
let artifact = artifacts.get(location);
|
|
64
|
-
if (artifact === undefined) {
|
|
65
|
-
// This boundary owns the connection until all effect evidence/cleanup has settled.
|
|
66
|
-
// Request cancellation travels with pre-effect operations, not that lifetime.
|
|
67
|
-
artifact = createArtifactPort(location);
|
|
68
|
-
artifacts.set(location, artifact);
|
|
69
|
-
}
|
|
70
|
-
for (const index of entry.actIndexes) {
|
|
71
|
-
const claim = await preparePendingPresentation({ artifact, location, participant: seat.participant,
|
|
72
|
-
activity: index, seat, markSeen: entry.markSeen, signal });
|
|
73
|
-
if (claim !== undefined)
|
|
74
|
-
claims.push({ key: pendingKey(entry.membership, index), claim });
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
const eligible = new Set();
|
|
78
|
-
for (const { key, claim } of claims)
|
|
79
|
-
if (await claim.eligible())
|
|
80
|
-
eligible.add(key);
|
|
81
|
-
const fresh = renderBoundary(pending.map(membership => ({ ...membership,
|
|
82
|
-
notifications: membership.notifications.filter(note => eligible.has(pendingKey(membership, note.actIndex))),
|
|
83
|
-
})));
|
|
84
|
-
const included = new Set(fresh.complete.flatMap(entry => entry.actIndexes.map(index => pendingKey(entry.membership, index))));
|
|
85
|
-
if (included.size === 0 || signal?.aborted)
|
|
86
|
-
return undefined;
|
|
87
|
-
const rendered = claims.filter(({ key }) => included.has(key));
|
|
88
|
-
let result;
|
|
89
60
|
try {
|
|
90
|
-
|
|
61
|
+
// Enumeration owns discovery/connections only; returned handles belong to the common runner.
|
|
62
|
+
for (const entry of renderBoundary(pending).complete) {
|
|
63
|
+
const { location, seat } = entry.membership;
|
|
64
|
+
let artifact = artifacts.get(location);
|
|
65
|
+
if (artifact === undefined) {
|
|
66
|
+
// Keep connections alive through all signal-independent settlement/cleanup.
|
|
67
|
+
artifact = createArtifactPort(location);
|
|
68
|
+
artifacts.set(location, artifact);
|
|
69
|
+
}
|
|
70
|
+
for (const index of entry.actIndexes) {
|
|
71
|
+
const claim = await preparePendingPresentation({ artifact, location, participant: seat.participant,
|
|
72
|
+
activity: index, seat, markSeen: entry.markSeen, signal });
|
|
73
|
+
if (claim !== undefined)
|
|
74
|
+
claims.push({ key: pendingKey(entry.membership, index), claim });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
91
77
|
}
|
|
92
78
|
catch (error) {
|
|
93
|
-
|
|
79
|
+
// Transfer has not happened: enumeration must abandon any custody already acquired.
|
|
80
|
+
await Promise.all(claims.map(({ claim }) => claim.release()));
|
|
94
81
|
throw error;
|
|
95
82
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
83
|
+
const report = await presentPreparedBatch(claims.map(({ claim }) => claim), eligibleClaims => {
|
|
84
|
+
const eligible = new Set(claims.filter(({ claim }) => eligibleClaims.includes(claim)).map(({ key }) => key));
|
|
85
|
+
const fresh = renderBoundary(pending.map(membership => ({ ...membership,
|
|
86
|
+
notifications: membership.notifications.filter(note => eligible.has(pendingKey(membership, note.actIndex))),
|
|
87
|
+
})));
|
|
88
|
+
const included = new Set(fresh.complete.flatMap(entry => entry.actIndexes.map(index => pendingKey(entry.membership, index))));
|
|
89
|
+
return { payload: fresh.context, included: claims.filter(({ key }) => included.has(key)).map(({ claim }) => claim) };
|
|
90
|
+
}, present, { signal, classifyError: () => 'failed' });
|
|
91
|
+
if (report.observation.kind === 'threw')
|
|
92
|
+
throw report.observation.error;
|
|
93
|
+
for (const { result } of report.settlements)
|
|
94
|
+
if (result.persistence.status === 'error')
|
|
95
|
+
throw result.persistence.error;
|
|
96
|
+
return report.observation.kind === 'returned' ? report.observation.value : undefined;
|
|
103
97
|
}
|
|
104
98
|
finally {
|
|
105
|
-
await Promise.all(claims.map(({ claim }) => claim.release()));
|
|
106
99
|
await Promise.all([...artifacts.values()].map(artifact => artifact.close()));
|
|
107
100
|
}
|
|
108
101
|
}
|
|
@@ -1,23 +1,20 @@
|
|
|
1
|
-
import { type ActivityId } from './square-core.js';
|
|
2
1
|
import { type StoredAct } from './model.js';
|
|
3
2
|
import type { ActivityIndex, DirectedFilter, SquareRead, SquareWrite } from './ports.js';
|
|
4
|
-
import type { CatchOptions } from './square-facade.js';
|
|
3
|
+
import type { CatchOptions, CatchReason, PerceivedActivity } from './square-facade.js';
|
|
5
4
|
export interface CatchSelection {
|
|
6
5
|
readonly participant: string;
|
|
7
|
-
readonly activities: readonly
|
|
8
|
-
readonly consumedThrough: ActivityId | null;
|
|
6
|
+
readonly activities: readonly PerceivedActivity[];
|
|
9
7
|
readonly remaining: number;
|
|
10
|
-
readonly sayNumbers: Readonly<Record<number, number>>;
|
|
11
8
|
readonly standingCount: number;
|
|
12
|
-
readonly
|
|
9
|
+
readonly deliveryAvailable: boolean;
|
|
13
10
|
readonly now: number;
|
|
14
11
|
readonly ownActivityCount: number;
|
|
15
12
|
readonly hardCap: number | null;
|
|
16
|
-
readonly terminalStatus: 'capped' | 'quorum' | undefined;
|
|
13
|
+
readonly terminalStatus: Extract<CatchReason, 'capped' | 'quorum'> | undefined;
|
|
17
14
|
readonly hint?: string;
|
|
18
15
|
}
|
|
19
16
|
/** Capture a selected page (or an empty completion) without opening another resource. */
|
|
20
|
-
export declare function observeCatchSelection(facts: SquareRead, participant: string, activities: readonly StoredAct[], filter: DirectedFilter, at: number): CatchSelection;
|
|
17
|
+
export declare function observeCatchSelection(facts: SquareRead, participant: string, activities: readonly StoredAct[], filter: DirectedFilter, at: number, remaining?: number): CatchSelection;
|
|
21
18
|
export declare const CATCH_DEFAULT_LIMIT = 10;
|
|
22
19
|
export declare const CATCH_MAX_LIMIT = 100;
|
|
23
20
|
export declare function catchLimit(value: number | undefined): number;
|
package/dist/catch-decisions.js
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import { formatActivityId, parseActivityId } from './square-core.js';
|
|
2
|
-
import {
|
|
2
|
+
import { terminalStatusFor } from './runtime.js';
|
|
3
3
|
import { SquareError } from './model.js';
|
|
4
4
|
import { resolveKnownName } from './decisions.js';
|
|
5
|
+
import { toPerceivedActivity } from './views.js';
|
|
5
6
|
import { catchHint } from './hints.js';
|
|
7
|
+
import { consumeReceived, ReceiveError, selectReceivedHeads } from './receive.js';
|
|
8
|
+
import { squareReceiveRead } from './square-receive.js';
|
|
6
9
|
/** Capture a selected page (or an empty completion) without opening another resource. */
|
|
7
|
-
export function observeCatchSelection(facts, participant, activities, filter, at) {
|
|
10
|
+
export function observeCatchSelection(facts, participant, activities, filter, at, remaining) {
|
|
8
11
|
if (typeof at !== 'number' || !Number.isFinite(at))
|
|
9
12
|
throw new SquareError('invalid_args', 'Catch time must be a finite number.');
|
|
10
|
-
const cursor = readCursor(facts, participant);
|
|
11
13
|
const counts = facts.participantCounts(), header = facts.header(), current = facts.participant(participant);
|
|
12
14
|
return {
|
|
13
|
-
participant, activities
|
|
14
|
-
remaining: facts.directedCount(participant, filter),
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
participant, activities: activities.map(activity => toPerceivedActivity(facts, activity, participant)),
|
|
16
|
+
remaining: remaining ?? facts.directedCount(participant, filter),
|
|
17
|
+
standingCount: counts.joined, now: at,
|
|
18
|
+
deliveryAvailable: facts.seat(participant)?.wakeAddress !== undefined,
|
|
17
19
|
ownActivityCount: current.sayCount, hardCap: header.hardCap,
|
|
18
20
|
terminalStatus: terminalStatusFor(current, header.hardCap, counts),
|
|
19
21
|
};
|
|
@@ -27,19 +29,23 @@ export function catchLimit(value) {
|
|
|
27
29
|
}
|
|
28
30
|
return limit;
|
|
29
31
|
}
|
|
30
|
-
|
|
31
|
-
export function selectCatchPage(facts, requestedName, options) {
|
|
32
|
+
function catchPageSelection(facts, requestedName, options) {
|
|
32
33
|
const participant = resolveKnownName(facts, requestedName);
|
|
33
34
|
const limit = catchLimit(options.limit);
|
|
34
|
-
const senders = options.from?.map(
|
|
35
|
+
const senders = options.from?.map(name => resolveKnownName(facts, name));
|
|
35
36
|
const filter = { unseenOnly: true, ...(senders === undefined ? {} : { senders }), ...(options.mention === true ? { attentionOnly: true } : {}) };
|
|
36
|
-
return { participant,
|
|
37
|
+
return { participant, filter, selection: { kind: 'page', query: filter, limit } };
|
|
38
|
+
}
|
|
39
|
+
/** Select non-ID catch coordinates without hydrating bodies or recording receipts. */
|
|
40
|
+
export function selectCatchPage(facts, requestedName, options) {
|
|
41
|
+
const { participant, filter, selection } = catchPageSelection(facts, requestedName, options);
|
|
42
|
+
return { participant, indexes: selectReceivedHeads(squareReceiveRead(facts), participant, selection).map(item => item.key), filter };
|
|
37
43
|
}
|
|
38
44
|
/** Selection, receipts and resulting cursor belong to one artifact write transaction. */
|
|
39
45
|
export function decideCatch(facts, requestedName, options, at) {
|
|
40
46
|
let viewer;
|
|
41
|
-
let delivered;
|
|
42
47
|
let filter = { unseenOnly: true };
|
|
48
|
+
let selection;
|
|
43
49
|
if (options.id !== undefined) {
|
|
44
50
|
viewer = resolveKnownName(facts, requestedName);
|
|
45
51
|
catchLimit(options.limit);
|
|
@@ -48,25 +54,39 @@ export function decideCatch(facts, requestedName, options, at) {
|
|
|
48
54
|
const index = parseActivityId(options.id);
|
|
49
55
|
if (index === undefined)
|
|
50
56
|
throw new SquareError('invalid_args', 'Invalid catch id: expected an activity id like act/12.');
|
|
51
|
-
|
|
52
|
-
if (activity === undefined || activity.kind !== 'say' || facts.directedAt(viewer, index) === undefined) {
|
|
53
|
-
throw new SquareError('invalid_args', `✕ ${formatActivityId(index)} isn't addressed to you`, {
|
|
54
|
-
reason: 'not_addressed', activityId: formatActivityId(index), activityExists: activity !== undefined,
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
delivered = [activity];
|
|
57
|
+
selection = { kind: 'exact', key: index, remainingQuery: filter };
|
|
58
58
|
}
|
|
59
59
|
else {
|
|
60
|
-
const page =
|
|
60
|
+
const page = catchPageSelection(facts, requestedName, options);
|
|
61
61
|
viewer = page.participant;
|
|
62
62
|
filter = page.filter;
|
|
63
|
-
|
|
63
|
+
selection = page.selection;
|
|
64
|
+
}
|
|
65
|
+
let consumed;
|
|
66
|
+
try {
|
|
67
|
+
consumed = consumeReceived({
|
|
68
|
+
...squareReceiveRead(facts),
|
|
69
|
+
insertReceipt: (ref, time) => facts.markSeen(ref.recipient, [ref.item], time) > 0,
|
|
70
|
+
}, viewer, selection, at);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
if ((error instanceof TypeError || error instanceof RangeError) && (typeof at !== 'number' || !Number.isFinite(at))) {
|
|
74
|
+
throw new SquareError('invalid_args', 'Invalid square artifact: row is malformed.');
|
|
75
|
+
}
|
|
76
|
+
if (!(error instanceof ReceiveError))
|
|
77
|
+
throw error;
|
|
78
|
+
if (error.code === 'missing_item')
|
|
79
|
+
throw new SquareError('invalid_args', 'Invalid square artifact: addressed activity is missing.');
|
|
80
|
+
const index = error.key;
|
|
81
|
+
throw new SquareError('invalid_args', `✕ ${formatActivityId(index)} isn't addressed to you`, {
|
|
82
|
+
reason: 'not_addressed', activityId: formatActivityId(index), activityExists: facts.activity(index) !== undefined,
|
|
83
|
+
});
|
|
64
84
|
}
|
|
65
|
-
const
|
|
66
|
-
const
|
|
85
|
+
const delivered = consumed.items.map(item => item.data);
|
|
86
|
+
const observed = observeCatchSelection(facts, viewer, delivered, filter, at, consumed.remaining);
|
|
67
87
|
// Receipts are the durable catch count: everything directed here minus what is still unseen.
|
|
68
88
|
const caught = facts.directedCount(viewer, {}) - facts.directedCount(viewer, { unseenOnly: true });
|
|
69
89
|
// An exact id catch is a targeted fetch and stays repeatable, so it carries no nudge.
|
|
70
|
-
const hint = options.id === undefined ? catchHint(caught -
|
|
71
|
-
return hint === undefined ?
|
|
90
|
+
const hint = options.id === undefined ? catchHint(caught - consumed.newlyConsumed, caught) : undefined;
|
|
91
|
+
return hint === undefined ? observed : { ...observed, hint };
|
|
72
92
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createArtifactPort } from './artifact.js';
|
|
2
|
+
import { decodeHarnessOwner } from './owner.js';
|
|
2
3
|
import { indexedSquareEntries } from './square-index.js';
|
|
3
4
|
const holdReasons = new Set(['explicit-setting', 'managed-setting', 'repo-setting', 'invalid-setting', 'bypass-default', 'mode-unknown', 'mode-mismatch', 'no-mode-asserted']);
|
|
4
5
|
const dropReasons = new Set(['expired', 'refused', 'dropped', 'discarded']);
|
|
@@ -13,12 +14,17 @@ export async function recordClaudeDeliveryStatus(input, env = process.env) {
|
|
|
13
14
|
|| (input.state === 'held' ? !holdReasons.has(input.reason ?? '')
|
|
14
15
|
: input.state === 'dropped' ? !dropReasons.has(input.reason ?? '') : input.reason !== undefined))
|
|
15
16
|
return false;
|
|
16
|
-
|
|
17
|
-
const
|
|
17
|
+
const paths = new Set((await indexedSquareEntries({}, env)).filter(entry => {
|
|
18
|
+
const coordinate = decodeHarnessOwner(entry.owner);
|
|
19
|
+
return coordinate?.channel === 'claude-code' && coordinate.session === input.sessionId;
|
|
20
|
+
}).map(entry => entry.location));
|
|
21
|
+
for (const location of paths) {
|
|
22
|
+
const artifact = createArtifactPort(location);
|
|
18
23
|
try {
|
|
19
24
|
const { value: match } = await artifact.read((facts) => facts.roster().flatMap((participant) => {
|
|
20
25
|
const seat = facts.seat(participant.name);
|
|
21
|
-
if (seat === undefined || seat.
|
|
26
|
+
if (seat === undefined || seat.attachment !== 'attached' || seat.wakeAddress?.kind !== 'claude-native'
|
|
27
|
+
|| seat.wakeAddress.address.sessionId !== input.sessionId)
|
|
22
28
|
return [];
|
|
23
29
|
const evidence = facts.deliveryEvidence(seat.participant, seat.generation).find((row) => row.correlation?.harness === 'claude' && row.correlation.messageId === input.messageId && row.claimToken !== undefined);
|
|
24
30
|
return evidence === undefined ? [] : [{ seat, evidence }];
|
|
@@ -26,7 +32,7 @@ export async function recordClaudeDeliveryStatus(input, env = process.env) {
|
|
|
26
32
|
if (match.length === 0)
|
|
27
33
|
continue;
|
|
28
34
|
const { seat, evidence } = match[0];
|
|
29
|
-
const ref = { participant: seat.participant,
|
|
35
|
+
const ref = { participant: seat.participant, owner: seat.owner, generation: seat.generation };
|
|
30
36
|
return (await artifact.transactAs({ kind: 'participant', seat: ref }, (facts) => facts.observeNativeDelivery({ seat: ref, claimToken: evidence.claimToken, observation: {
|
|
31
37
|
eventId: input.eventId, messageId: input.messageId, state: input.state,
|
|
32
38
|
...(input.reason === undefined ? {} : { reason: input.reason }), observedAt: input.observedAt,
|
package/dist/claude-delivery.js
CHANGED
|
@@ -2,9 +2,10 @@ import { stat } from 'node:fs/promises';
|
|
|
2
2
|
import { createHash, randomUUID } from 'node:crypto';
|
|
3
3
|
import { createArtifactPort } from './artifact.js';
|
|
4
4
|
import { presentPending } from './presentation-operations.js';
|
|
5
|
-
import {
|
|
6
|
-
import { sameBody } from './model.js';
|
|
5
|
+
import { nativePendingPreview } from './inbox.js';
|
|
7
6
|
import { parseActivityId } from './square-core.js';
|
|
7
|
+
import { decodeHarnessOwner } from './owner.js';
|
|
8
|
+
import { indexedSquareEntries } from './square-index.js';
|
|
8
9
|
import { asSeatRef } from './seats.js';
|
|
9
10
|
import { writeClaudeNative } from './packages/agent-delivery/src/claude-native.js';
|
|
10
11
|
const marker = /(?:^|\n)\[square-inbox:([a-zA-Z0-9-]+)\](?:\n|$)/g;
|
|
@@ -22,8 +23,9 @@ function previewFacts(facts, request) {
|
|
|
22
23
|
const index = parseActivityId(request.activity);
|
|
23
24
|
if (index === undefined || request.route.kind !== 'claude-native' || request.route.address.endpoint === undefined)
|
|
24
25
|
return undefined;
|
|
25
|
-
return nativePendingPreview(facts, { location: request.location, routeKind: 'claude-native',
|
|
26
|
-
|
|
26
|
+
return nativePendingPreview(facts, { location: request.location, routeKind: 'claude-native', generation: request.seat.generation,
|
|
27
|
+
address: request.route.address,
|
|
28
|
+
participant: request.participant, actIndex: index, now: Date.now() });
|
|
27
29
|
}
|
|
28
30
|
async function preview(artifact, request) {
|
|
29
31
|
return (await artifact.read((facts) => previewFacts(facts, request))).value;
|
|
@@ -31,7 +33,7 @@ async function preview(artifact, request) {
|
|
|
31
33
|
async function writeClaudeWake(sessionId, endpoint, messageId, payload, timeoutMs, signal) {
|
|
32
34
|
const result = await writeClaudeNative({ sessionId, endpoint, messageId }, payload, { deadline: Date.now() + timeoutMs, signal });
|
|
33
35
|
if (result.outcome === 'unavailable')
|
|
34
|
-
return { outcome: 'failed',
|
|
36
|
+
return { outcome: 'failed', signature: 'claude_endpoint_unavailable', message: result.message };
|
|
35
37
|
return { outcome: 'unknown', signature: 'native_admission_unconfirmed', message: result.outcome === 'written' ? 'Claude accepted the local write; native admission is unconfirmed.' : result.message };
|
|
36
38
|
}
|
|
37
39
|
export function createClaudeWakeTransport(env = process.env) {
|
|
@@ -69,13 +71,15 @@ async function dispatchClaude(request, timeoutMs, beforeSend, env = process.env)
|
|
|
69
71
|
return { outcome: 'not-capable', diagnostic: 'Claude activity changed before native write.' };
|
|
70
72
|
if (signal.aborted || !await beforeSend())
|
|
71
73
|
return { outcome: 'not-capable', diagnostic: 'Claude activity was consumed before native write.' };
|
|
72
|
-
|
|
74
|
+
const sessionId = request.route.address.sessionId;
|
|
75
|
+
if (sessionId === undefined)
|
|
76
|
+
return { outcome: 'not-capable', diagnostic: 'Claude native route has no session address.' };
|
|
77
|
+
return writeClaudeWake(sessionId, request.route.address.endpoint, messageId, payload, timeoutMs, signal);
|
|
73
78
|
}
|
|
74
79
|
function currentPayload(facts, request) {
|
|
75
80
|
const seat = facts.seat(request.participant), evidence = facts.deliveryByToken(request.claimToken);
|
|
76
|
-
if (seat === undefined || evidence === undefined || seat.generation !== evidence.generation
|
|
77
|
-
|| seat.generation !== request.seat.generation ||
|
|
78
|
-
|| seat.body.channel !== 'claude-code' || seat.body.session !== request.route.sessionId
|
|
81
|
+
if (seat === undefined || seat.attachment !== 'attached' || evidence === undefined || seat.generation !== evidence.generation
|
|
82
|
+
|| seat.generation !== request.seat.generation || seat.owner !== request.seat.owner
|
|
79
83
|
|| evidence.activity !== request.activity || !['dispatching', 'unknown', 'accepted'].includes(evidence.outcome)
|
|
80
84
|
|| seat.wakeAddress?.kind !== 'claude-native' || !sameAddress(seat.wakeAddress.address, request.route.address))
|
|
81
85
|
return undefined;
|
|
@@ -87,7 +91,11 @@ function currentPayload(facts, request) {
|
|
|
87
91
|
}
|
|
88
92
|
async function rowsForSession(sessionId, env, tokens) {
|
|
89
93
|
const rows = [];
|
|
90
|
-
const
|
|
94
|
+
const candidates = (await indexedSquareEntries({}, env)).filter(entry => {
|
|
95
|
+
const coordinate = decodeHarnessOwner(entry.owner);
|
|
96
|
+
return coordinate?.channel === 'claude-code' && coordinate.session === sessionId;
|
|
97
|
+
});
|
|
98
|
+
const paths = new Set(candidates.map(candidate => candidate.location));
|
|
91
99
|
for (const location of paths) {
|
|
92
100
|
const artifact = createArtifactPort(location);
|
|
93
101
|
try {
|
|
@@ -101,7 +109,7 @@ async function rowsForSession(sessionId, env, tokens) {
|
|
|
101
109
|
const item = index === undefined ? undefined : facts.activity(index);
|
|
102
110
|
if (seat === undefined || item?.kind !== 'say')
|
|
103
111
|
return [row];
|
|
104
|
-
const route = { location, participant: seat.participant,
|
|
112
|
+
const route = { location, participant: seat.participant,
|
|
105
113
|
...evidence.route, updatedAt: evidence.at };
|
|
106
114
|
const request = { location, participant: seat.participant, activity: evidence.activity,
|
|
107
115
|
actor: item.actor, seat: asSeatRef(seat), claimToken: token, attemptN: evidence.attemptN, route, artifact };
|
package/dist/claude-mod.js
CHANGED
|
@@ -7,11 +7,13 @@ import { processActNotificationsOnce } from './notifications.js';
|
|
|
7
7
|
import { sweepPending } from './delivery-operations.js';
|
|
8
8
|
import { beginClaudeMcpCall, endClaudeMcpCall } from './claude-mcp-context.js';
|
|
9
9
|
import { asSeatRef } from './seats.js';
|
|
10
|
-
import { projectSessionSeats
|
|
11
|
-
import {
|
|
10
|
+
import { projectSessionSeats } from './inbox.js';
|
|
11
|
+
import { restoreSessionSeats } from './session-lifecycle.js';
|
|
12
|
+
import { harnessOwner } from './owner.js';
|
|
13
|
+
import { InternalSquareError, SquareError } from './model.js';
|
|
12
14
|
/** Persisted hook state keeps its wire coordinate; internal consumers keep the located seat. */
|
|
13
|
-
function claudeBinding({ location, seat }) {
|
|
14
|
-
return { location, participant: seat.participant, session:
|
|
15
|
+
function claudeBinding({ location, seat }, sessionId) {
|
|
16
|
+
return { location, participant: seat.participant, session: sessionId, generation: seat.generation };
|
|
15
17
|
}
|
|
16
18
|
/** JSON stdin bridge. Identity is explicit per operation; process.env is never rewritten. */
|
|
17
19
|
export async function runClaudeMod(inputText, env = process.env) {
|
|
@@ -33,29 +35,29 @@ export async function runClaudeMod(inputText, env = process.env) {
|
|
|
33
35
|
}
|
|
34
36
|
const scoped = operationEnv('claude', input.sessionId, env);
|
|
35
37
|
const signal = AbortSignal.timeout(2500);
|
|
36
|
-
const
|
|
38
|
+
const owner = harnessOwner({ channel: 'claude-code', session: input.sessionId, ...(input.agentId === undefined ? {} : { agent: input.agentId }) });
|
|
37
39
|
const supported = process.platform === 'darwin' && typeof input.endpoint === 'string' && input.endpoint.startsWith('/');
|
|
38
|
-
const sessionSeats = () => projectSessionSeats({
|
|
40
|
+
const sessionSeats = async () => (await projectSessionSeats({ owner, env }, signal)).filter(member => member.seat.attachment === 'attached');
|
|
39
41
|
if (input.operation === 'end') {
|
|
40
42
|
const retired = [];
|
|
41
43
|
const captured = [...input.bindings ?? []];
|
|
42
44
|
// Discover committed late joins at the pinned end boundary, never later owners.
|
|
43
45
|
if (input.endedAt !== undefined)
|
|
44
|
-
for (const member of await
|
|
46
|
+
for (const member of await projectSessionSeats({ owner, env }, signal)) {
|
|
45
47
|
if (member.joinedAt > input.endedAt || captured.some((item) => item.location === member.location
|
|
46
48
|
&& item.participant === member.seat.participant && item.generation === member.seat.generation))
|
|
47
49
|
continue;
|
|
48
|
-
captured.push(claudeBinding(member));
|
|
50
|
+
captured.push(claudeBinding(member, input.sessionId));
|
|
49
51
|
}
|
|
50
52
|
for (const binding of captured) {
|
|
51
53
|
if (binding.session !== input.sessionId)
|
|
52
54
|
continue;
|
|
53
|
-
const square = await Square.at({ path: binding.location, env: scoped
|
|
54
|
-
participant: binding.participant });
|
|
55
|
+
const square = await Square.at({ path: binding.location, env: scoped });
|
|
55
56
|
try {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
const detached = binding.generation === undefined ? null
|
|
58
|
+
: await square.detach(binding.participant, { owner, generation: binding.generation });
|
|
59
|
+
if (detached)
|
|
60
|
+
retired.push(binding);
|
|
59
61
|
}
|
|
60
62
|
finally {
|
|
61
63
|
await square.close();
|
|
@@ -67,41 +69,10 @@ export async function runClaudeMod(inputText, env = process.env) {
|
|
|
67
69
|
}
|
|
68
70
|
const recovery = new Set();
|
|
69
71
|
const cancelledBindings = [...input.cancelledBindings ?? []];
|
|
70
|
-
if (input.operation === 'start'
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const seen = new Set();
|
|
75
|
-
for (const candidate of candidates) {
|
|
76
|
-
if (signal.aborted)
|
|
77
|
-
break;
|
|
78
|
-
const probe = createArtifactPort(candidate.location, undefined, signal);
|
|
79
|
-
let names;
|
|
80
|
-
try {
|
|
81
|
-
names = (await probe.read(facts => facts.completedSeatsForBody(body).map(seat => seat.participant), signal)).value;
|
|
82
|
-
}
|
|
83
|
-
catch (error) {
|
|
84
|
-
if (error instanceof InternalSquareError && error.code === 'not_found')
|
|
85
|
-
throw new SquareError('unavailable', `Square is unavailable at ${candidate.location}`);
|
|
86
|
-
throw error;
|
|
87
|
-
}
|
|
88
|
-
finally {
|
|
89
|
-
await probe.close();
|
|
90
|
-
}
|
|
91
|
-
for (const participant of names) {
|
|
92
|
-
const key = `${candidate.location}\0${participant}`;
|
|
93
|
-
if (seen.has(key))
|
|
94
|
-
continue;
|
|
95
|
-
seen.add(key);
|
|
96
|
-
const square = await Square.at({ path: candidate.location, env: scoped, body, participant });
|
|
97
|
-
try {
|
|
98
|
-
await square.join(participant);
|
|
99
|
-
}
|
|
100
|
-
finally {
|
|
101
|
-
await square.close();
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
72
|
+
if (input.operation === 'start') {
|
|
73
|
+
await restoreSessionSeats(owner, scoped, supported ? {
|
|
74
|
+
kind: 'claude-native', address: { sessionId: input.sessionId, endpoint: input.endpoint, version: input.version, platform: 'darwin', loaded: 'true' },
|
|
75
|
+
} : undefined, signal);
|
|
105
76
|
}
|
|
106
77
|
if (input.operation === 'start' || input.operation === 'reconcile' || input.operation === 'cancel' || input.cancelAt !== undefined) {
|
|
107
78
|
for (const member of await sessionSeats()) {
|
|
@@ -109,21 +80,21 @@ export async function runClaudeMod(inputText, env = process.env) {
|
|
|
109
80
|
break;
|
|
110
81
|
const { location, seat: capturedSeat, joinedAt } = member;
|
|
111
82
|
const matches = (item) => item !== undefined
|
|
112
|
-
&& item.location === location && item.participant === capturedSeat.participant && item.session ===
|
|
83
|
+
&& item.location === location && item.participant === capturedSeat.participant && item.session === input.sessionId
|
|
113
84
|
&& (item.generation === undefined || item.generation === capturedSeat.generation);
|
|
114
85
|
const expected = input.bindings?.find(matches);
|
|
115
86
|
const cancelled = input.cancelledBindings?.find(matches) ?? (input.operation === 'cancel' ? expected : undefined);
|
|
116
87
|
const lateCancelledJoin = input.operation === 'cancel' && input.cancelAt !== undefined
|
|
117
88
|
&& joinedAt <= input.cancelAt && expected === undefined;
|
|
118
89
|
if (lateCancelledJoin && !cancelledBindings.some(matches))
|
|
119
|
-
cancelledBindings.push(claudeBinding(member));
|
|
90
|
+
cancelledBindings.push(claudeBinding(member, input.sessionId));
|
|
120
91
|
if (input.operation !== 'start' && input.operation !== 'reconcile' && !lateCancelledJoin && expected === undefined)
|
|
121
92
|
continue;
|
|
122
93
|
const artifact = createArtifactPort(location, undefined, signal);
|
|
123
94
|
try {
|
|
124
95
|
const { value: currentSeat } = await artifact.read((facts) => facts.seat(capturedSeat.participant), signal);
|
|
125
|
-
if (currentSeat === undefined || currentSeat.generation !== capturedSeat.generation
|
|
126
|
-
||
|
|
96
|
+
if (currentSeat === undefined || currentSeat.attachment !== 'attached' || currentSeat.generation !== capturedSeat.generation
|
|
97
|
+
|| currentSeat.owner !== capturedSeat.owner)
|
|
127
98
|
continue;
|
|
128
99
|
const seat = asSeatRef(currentSeat);
|
|
129
100
|
const existing = currentSeat.wakeAddress?.kind === 'claude-native' ? currentSeat.wakeAddress : undefined;
|
|
@@ -193,7 +164,7 @@ export async function runClaudeMod(inputText, env = process.env) {
|
|
|
193
164
|
await processActNotificationsOnce(location, index, { env: { ...env, SQUARE_NOTIFY_DELIVERY_WAIT_MS: '200' } });
|
|
194
165
|
}
|
|
195
166
|
}
|
|
196
|
-
return `${JSON.stringify({ available: supported, diagnostic: supported ? undefined : 'Native inbox unavailable: requires macOS Claude with an interactive loaded mod and its real endpoint.', bindings: members.map(claudeBinding), cancelledBindings })}\n`;
|
|
167
|
+
return `${JSON.stringify({ available: supported, diagnostic: supported ? undefined : 'Native inbox unavailable: requires macOS Claude with an interactive loaded mod and its real endpoint.', bindings: members.map(member => claudeBinding(member, input.sessionId)), cancelledBindings })}\n`;
|
|
197
168
|
}
|
|
198
169
|
if (!['guard', 'admitted', 'stored'].includes(input.operation) || typeof input.text !== 'string')
|
|
199
170
|
throw new Error('Unknown Claude bridge operation.');
|