@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/square-index.js
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from 'node:fs';
|
|
2
|
-
import os from 'node:os';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { randomUUID } from 'node:crypto';
|
|
5
|
-
import { withFileLock } from './file-lock.js';
|
|
6
|
-
import { canonicalPath } from './canonical-path.js';
|
|
7
|
-
import { observeFileMetadata } from './file-changes.js';
|
|
8
|
-
import { validateOwner } from './neutral.js';
|
|
9
|
-
export function squareIndexPath(env = process.env) {
|
|
10
|
-
const configured = env.SQUARE_INDEX?.trim();
|
|
11
|
-
return path.resolve(configured ?? path.join(os.homedir(), '.square', 'index.ndjsonl'));
|
|
12
|
-
}
|
|
13
|
-
function lockPath(index) {
|
|
14
|
-
return `${index}.lock`;
|
|
15
|
-
}
|
|
16
|
-
function validEntry(value) {
|
|
17
|
-
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
18
|
-
return undefined;
|
|
19
|
-
const row = value;
|
|
20
|
-
if (typeof row.location !== 'string' || !path.isAbsolute(row.location) || typeof row.owner !== 'string')
|
|
21
|
-
return undefined;
|
|
22
|
-
try {
|
|
23
|
-
validateOwner(row.owner);
|
|
24
|
-
}
|
|
25
|
-
catch {
|
|
26
|
-
return undefined;
|
|
27
|
-
}
|
|
28
|
-
if (Object.keys(row).some((key) => !['location', 'owner'].includes(key)))
|
|
29
|
-
return undefined;
|
|
30
|
-
return { location: row.location, owner: row.owner };
|
|
31
|
-
}
|
|
32
|
-
async function readEntries(index) {
|
|
33
|
-
let content;
|
|
34
|
-
try {
|
|
35
|
-
content = await fs.readFile(index, 'utf8');
|
|
36
|
-
}
|
|
37
|
-
catch (error) {
|
|
38
|
-
if (error.code === 'ENOENT')
|
|
39
|
-
return [];
|
|
40
|
-
throw error;
|
|
41
|
-
}
|
|
42
|
-
const entries = [];
|
|
43
|
-
for (const line of content.split('\n')) {
|
|
44
|
-
if (line.trim() === '')
|
|
45
|
-
continue;
|
|
46
|
-
try {
|
|
47
|
-
const entry = validEntry(JSON.parse(line));
|
|
48
|
-
if (entry !== undefined)
|
|
49
|
-
entries.push(entry);
|
|
50
|
-
}
|
|
51
|
-
catch { /* Disposable discovery input. */ }
|
|
52
|
-
}
|
|
53
|
-
return entries;
|
|
54
|
-
}
|
|
55
|
-
function matches(entry, query) {
|
|
56
|
-
return query.owner === undefined || entry.owner === query.owner;
|
|
57
|
-
}
|
|
58
|
-
export async function indexedSquareEntries(query = {}, env = process.env) {
|
|
59
|
-
const entries = await readEntries(squareIndexPath(env));
|
|
60
|
-
const result = [];
|
|
61
|
-
const seen = new Set();
|
|
62
|
-
for (const entry of entries) {
|
|
63
|
-
if (!matches(entry, query))
|
|
64
|
-
continue;
|
|
65
|
-
let location;
|
|
66
|
-
try {
|
|
67
|
-
location = await canonicalPath(entry.location);
|
|
68
|
-
}
|
|
69
|
-
catch {
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
const key = JSON.stringify([location, entry.owner]);
|
|
73
|
-
if (seen.has(key))
|
|
74
|
-
continue;
|
|
75
|
-
seen.add(key);
|
|
76
|
-
result.push({ location, owner: entry.owner });
|
|
77
|
-
}
|
|
78
|
-
return result;
|
|
79
|
-
}
|
|
80
|
-
export async function indexedSquarePaths(query = {}, env = process.env) {
|
|
81
|
-
return [...new Set((await indexedSquareEntries(query, env)).map((entry) => entry.location))];
|
|
82
|
-
}
|
|
83
|
-
/** Best-effort discovery only: a busy index skips publication, never queues or delays artifact work. */
|
|
84
|
-
export async function rememberSquarePath(location, owner, index) {
|
|
85
|
-
validateOwner(owner);
|
|
86
|
-
const canonical = await canonicalPath(location);
|
|
87
|
-
await fs.mkdir(path.dirname(index), { recursive: true });
|
|
88
|
-
return await withFileLock(lockPath(index), { retryMs: 10, skipIfBusy: true }, async () => {
|
|
89
|
-
const entries = await readEntries(index);
|
|
90
|
-
if (entries.some((entry) => entry.location === canonical && entry.owner === owner))
|
|
91
|
-
return false;
|
|
92
|
-
const next = [...entries, { location: canonical, owner }];
|
|
93
|
-
const temporary = `${index}.${process.pid}.${randomUUID()}.tmp`;
|
|
94
|
-
try {
|
|
95
|
-
await fs.writeFile(temporary, next.map((entry) => JSON.stringify(entry)).join('\n') + '\n', { flag: 'wx', mode: 0o600 });
|
|
96
|
-
await fs.rename(temporary, index);
|
|
97
|
-
}
|
|
98
|
-
finally {
|
|
99
|
-
await fs.rm(temporary, { force: true });
|
|
100
|
-
}
|
|
101
|
-
return true;
|
|
102
|
-
}) ?? false;
|
|
103
|
-
}
|
|
104
|
-
export function observeSquareIndex(env = process.env) {
|
|
105
|
-
return observeFileMetadata(squareIndexPath(env));
|
|
106
|
-
}
|
package/dist/wake-preview.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { WakeProjectionContext, WakeProjection, SquareArtifactPort } from './ports.js';
|
|
2
|
-
export declare function squareWakeProjection(artifact: SquareArtifactPort): WakeProjection;
|
|
3
|
-
/** Agora-framed projection over the narrow captured delivery context. */
|
|
4
|
-
export declare function wakePreview(facts: import('./ports.js').SquareRead, request: WakeProjectionContext): {
|
|
5
|
-
payload: string;
|
|
6
|
-
coverage: "full" | "clipped";
|
|
7
|
-
} | undefined;
|
package/dist/wake-preview.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { parseMessageId } from './message.js';
|
|
2
|
-
import { nativePendingPreview } from './inbox.js';
|
|
3
|
-
import { canonicalPathSync } from './canonical-path.js';
|
|
4
|
-
export function squareWakeProjection(artifact) {
|
|
5
|
-
return async (context) => (await artifact.read(facts => wakePreview(facts, context))).value;
|
|
6
|
-
}
|
|
7
|
-
/** Agora-framed projection over the narrow captured delivery context. */
|
|
8
|
-
export function wakePreview(facts, request) {
|
|
9
|
-
const index = parseMessageId(request.message);
|
|
10
|
-
if (index === undefined)
|
|
11
|
-
return undefined;
|
|
12
|
-
const preview = nativePendingPreview(facts, { location: canonicalPathSync(request.location), participant: request.participant,
|
|
13
|
-
generation: request.seat.generation, owner: request.seat.owner, index, route: request.route, now: Date.now() });
|
|
14
|
-
return preview === undefined ? undefined : { payload: preview.payload, coverage: preview.clipped ? 'clipped' : 'full' };
|
|
15
|
-
}
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
import { squareWakeProjection } from '../../wake-preview.js';
|
|
3
|
-
import { channelArtifact, createArtifactPort } from '../../artifact.js';
|
|
4
|
-
import { deliverPending } from '../../delivery-operations.js';
|
|
5
|
-
import { nativePendingSource, observeSessionPending, projectSessionSeats } from '../../inbox.js';
|
|
6
|
-
import { automaticWakeEligible, nativePendingHead, presentationBlocksWake } from '../../delivery.js';
|
|
7
|
-
import { type Seat } from '../../model.js';
|
|
8
|
-
import { harnessOwner, decodeHarnessOwner } from '../../owner.js';
|
|
9
|
-
import type { WakeRequest, WakeTransportPort } from '../../ports.js';
|
|
10
|
-
import { formatMessageId, parseMessageId } from '../../message.js';
|
|
11
|
-
import { indexedSquareEntries } from '../../square-index.js';
|
|
12
|
-
import { asSeatRef } from '../../seats.js';
|
|
13
|
-
import { restoreSessionSeats } from '../../session-lifecycle.js';
|
|
14
|
-
import { presentPending } from '../../presentation-operations.js';
|
|
15
|
-
import { createNativeInputId, sendNativeText, type OpenCodeNativeSession } from '../../packages/agent-delivery/src/opencode-native.js';
|
|
16
|
-
|
|
17
|
-
const routeKind = 'opencode-server' as const;
|
|
18
|
-
const ownerFor = (sessionId: string) => harnessOwner({ channel: 'opencode', session: sessionId });
|
|
19
|
-
const payloadHash = (payload: string) => createHash('sha256').update(payload).digest('hex');
|
|
20
|
-
export interface OpenCodeContextMessage { readonly id?: string; readonly role: string; readonly content: readonly { readonly type: string; readonly text?: string }[] }
|
|
21
|
-
|
|
22
|
-
export async function indexedOpenCodeSessions(env: NodeJS.ProcessEnv): Promise<readonly string[]> {
|
|
23
|
-
return [...new Set((await indexedSquareEntries({ }, env)).flatMap((entry) => {
|
|
24
|
-
const coordinate = decodeHarnessOwner(entry.owner);
|
|
25
|
-
return coordinate?.channel === 'opencode' ? [coordinate.session] : [];
|
|
26
|
-
}))];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export async function discoverOpenCodeSeats(sessionId: string, env: NodeJS.ProcessEnv, signal?: AbortSignal) {
|
|
30
|
-
return projectSessionSeats({ owner: ownerFor(sessionId), env }, signal);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function sameSeat(left: Seat | undefined, right: Seat): boolean {
|
|
34
|
-
return left !== undefined && left.attachment === 'attached' && left.generation === right.generation && left.owner === right.owner;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function createOpenCodeWakeTransport(target: OpenCodeNativeSession, lifetime: AbortSignal): WakeTransportPort {
|
|
38
|
-
return {
|
|
39
|
-
probe: async (route) => !lifetime.aborted && route.kind === routeKind
|
|
40
|
-
&& route.address.sessionId === target.sessionId,
|
|
41
|
-
async attempt(request: WakeRequest, timeoutMs: number, beforeSend) {
|
|
42
|
-
const presentation = request.presentation;
|
|
43
|
-
if (request.claimToken === undefined || parseMessageId(request.message) === undefined || presentation === undefined
|
|
44
|
-
|| request.route.kind !== routeKind || request.route.address.sessionId !== target.sessionId) {
|
|
45
|
-
return { outcome: 'not-capable', diagnostic: 'OpenCode attention has no matching artifact claim or session.' };
|
|
46
|
-
}
|
|
47
|
-
const signal = AbortSignal.any([lifetime, AbortSignal.timeout(timeoutMs)]);
|
|
48
|
-
const started = Date.now();
|
|
49
|
-
if (signal.aborted || !await beforeSend())
|
|
50
|
-
return { outcome: 'not-capable', diagnostic: 'OpenCode attention changed before send.' };
|
|
51
|
-
const inputId = createNativeInputId();
|
|
52
|
-
const correlation = { harness: 'opencode' as const, token: inputId, payloadHash: payloadHash(presentation.payload) };
|
|
53
|
-
if (!await request.channel.prepare({ correlation,
|
|
54
|
-
coverage: presentation.coverage, ...(presentation.description === undefined ? {} : { description: presentation.description }) }, signal))
|
|
55
|
-
return { outcome: 'not-capable', diagnostic: 'OpenCode delivery claim is no longer current.' };
|
|
56
|
-
if (signal.aborted || !await beforeSend())
|
|
57
|
-
return { outcome: 'not-capable', diagnostic: 'OpenCode attention was consumed before send.' };
|
|
58
|
-
const result = await sendNativeText(target, presentation.payload, {
|
|
59
|
-
inputId, delivery: 'steer', timeoutMs: Math.max(1, timeoutMs - (Date.now() - started)), signal,
|
|
60
|
-
});
|
|
61
|
-
if (result.state === 'accepted') return { outcome: 'accepted' };
|
|
62
|
-
if (result.state === 'unavailable') return { outcome: 'failed', signature: result.code };
|
|
63
|
-
return { outcome: 'unknown', signature: result.code };
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function textMessage(message: OpenCodeContextMessage): string | undefined {
|
|
69
|
-
if (message.role !== 'user' || message.id === undefined || !message.content.every((part) => part.type === 'text' && typeof part.text === 'string')) return undefined;
|
|
70
|
-
return message.content.map((part) => part.text!).join('');
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/** The exact prepared native message can authorize artifact-backed presentation once. */
|
|
74
|
-
export async function observeOpenCodeContext(sessionId: string, messages: readonly OpenCodeContextMessage[], env: NodeJS.ProcessEnv, signal: AbortSignal): Promise<void> {
|
|
75
|
-
const observed = new Map(messages.flatMap((message) => {
|
|
76
|
-
const text = textMessage(message);
|
|
77
|
-
return text === undefined ? [] : [[message.id!, payloadHash(text)] as const];
|
|
78
|
-
}));
|
|
79
|
-
if (observed.size === 0 || signal.aborted) return;
|
|
80
|
-
for (const { location, seat } of await discoverOpenCodeSeats(sessionId, env, signal)) {
|
|
81
|
-
if (signal.aborted) return;
|
|
82
|
-
const artifact = createArtifactPort(location, undefined, signal);
|
|
83
|
-
try {
|
|
84
|
-
const { value: matches } = await artifact.read((facts) => {
|
|
85
|
-
if (!sameSeat(facts.seat(seat.participant), seat)) return [];
|
|
86
|
-
return facts.deliveryEvidence(seat.participant, seat.generation).flatMap((evidence) => {
|
|
87
|
-
const token = evidence.correlation?.harness === 'opencode' ? evidence.correlation.token : undefined;
|
|
88
|
-
const hash = token === undefined ? undefined : observed.get(token);
|
|
89
|
-
const index = parseMessageId(evidence.message), now = Date.now();
|
|
90
|
-
if (token === undefined || hash === undefined || evidence.claimToken === undefined || index === undefined
|
|
91
|
-
|| evidence.route === undefined || !['dispatching', 'unknown', 'accepted'].includes(evidence.outcome)
|
|
92
|
-
|| evidence.correlation?.payloadHash !== hash
|
|
93
|
-
|| evidence.route.kind !== routeKind
|
|
94
|
-
|| presentationBlocksWake(facts.deliveryEvidence(seat.participant, seat.generation, evidence.message), now)) return [];
|
|
95
|
-
const source = nativePendingSource(facts, { location, participant: seat.participant, generation: seat.generation,
|
|
96
|
-
owner: seat.owner, index, route: evidence.route, now });
|
|
97
|
-
if (source === undefined) return [];
|
|
98
|
-
return [{ evidence, index, hash, clipped: evidence.coverage === 'clipped' }];
|
|
99
|
-
});
|
|
100
|
-
}, signal);
|
|
101
|
-
for (const { evidence, index, hash, clipped } of matches) {
|
|
102
|
-
if (signal.aborted || evidence.claimToken === undefined) return;
|
|
103
|
-
await presentPending({ artifact, location, participant: seat.participant,
|
|
104
|
-
message: formatMessageId(index), seat: asSeatRef(seat), observationOf: evidence.claimToken, sink: { present() {} }, markSeen: !clipped, signal,
|
|
105
|
-
current: (facts) => {
|
|
106
|
-
const correlation = facts.deliveryByToken(evidence.claimToken!);
|
|
107
|
-
return nativePendingHead(facts, { location, participant: seat.participant, generation: seat.generation,
|
|
108
|
-
owner: seat.owner, index, route: evidence.route!, now: Date.now() }) !== undefined
|
|
109
|
-
&& ['dispatching', 'unknown', 'accepted'].includes(correlation?.outcome ?? '')
|
|
110
|
-
&& correlation?.correlation?.harness === 'opencode'
|
|
111
|
-
&& correlation.correlation.token !== undefined
|
|
112
|
-
&& correlation.correlation.payloadHash === hash;
|
|
113
|
-
},
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
} finally { await artifact.close(); }
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/** The native receiver owns no claims outside the artifact; HOME is used only for discovery. */
|
|
121
|
-
export async function receiveOpenCodePending(target: OpenCodeNativeSession, env: NodeJS.ProcessEnv, signal: AbortSignal): Promise<void> {
|
|
122
|
-
const observer = await observeSessionPending(ownerFor(target.sessionId), env);
|
|
123
|
-
const transport = createOpenCodeWakeTransport(target, signal);
|
|
124
|
-
let waiting: ReturnType<typeof observer.wait> | undefined;
|
|
125
|
-
try {
|
|
126
|
-
while (!signal.aborted) {
|
|
127
|
-
const armed = new Promise<boolean>((resolve) => {
|
|
128
|
-
waiting = observer.wait(Infinity, { signal, skipImmediate: true, onChangeArmed: resolve });
|
|
129
|
-
void waiting.catch(() => resolve(false));
|
|
130
|
-
});
|
|
131
|
-
if (!await armed) { await waiting; break; }
|
|
132
|
-
for (const membership of await observer.read({ signal })) {
|
|
133
|
-
const { location, seat } = membership;
|
|
134
|
-
if (signal.aborted) return;
|
|
135
|
-
const artifact = createArtifactPort(location, undefined, signal);
|
|
136
|
-
try {
|
|
137
|
-
const { value: notifications } = await artifact.read((facts) => {
|
|
138
|
-
if (!sameSeat(facts.seat(seat.participant), seat)) return [];
|
|
139
|
-
return membership.notifications.filter((note) => automaticWakeEligible(
|
|
140
|
-
facts.deliveryEvidence(seat.participant, seat.generation, formatMessageId(note.messageIndex)), Date.now()));
|
|
141
|
-
}, signal);
|
|
142
|
-
for (const note of notifications) {
|
|
143
|
-
if (signal.aborted) return;
|
|
144
|
-
await deliverPending({ artifact: channelArtifact(artifact), preparePresentation: squareWakeProjection(artifact), transport, location, message: formatMessageId(note.messageIndex), now: Date.now() });
|
|
145
|
-
}
|
|
146
|
-
} catch (error) {
|
|
147
|
-
if (signal.aborted) throw signal.reason ?? error;
|
|
148
|
-
// A candidate that became unreadable cannot authorize sending; the shared observer
|
|
149
|
-
// keeps its subscription and will reconsider the artifact after repair.
|
|
150
|
-
} finally { await artifact.close(); }
|
|
151
|
-
}
|
|
152
|
-
await waiting;
|
|
153
|
-
waiting = undefined;
|
|
154
|
-
}
|
|
155
|
-
} catch (error) {
|
|
156
|
-
if (!signal.aborted) throw error;
|
|
157
|
-
} finally {
|
|
158
|
-
observer.close();
|
|
159
|
-
await waiting?.catch(() => {});
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/** Publish the native address into current artifact seats; the legacy route projection is not read. */
|
|
164
|
-
export async function publishOpenCodeAddress(sessionId: string, env: NodeJS.ProcessEnv, signal: AbortSignal): Promise<boolean> {
|
|
165
|
-
await restoreSessionSeats(ownerFor(sessionId), env, { kind: routeKind, address: { sessionId } }, signal);
|
|
166
|
-
const seats = (await discoverOpenCodeSeats(sessionId, env, signal)).filter(({ seat }) => seat.attachment === 'attached');
|
|
167
|
-
for (const { location, seat } of seats) {
|
|
168
|
-
if (signal.aborted) return false;
|
|
169
|
-
const artifact = createArtifactPort(location, undefined, signal);
|
|
170
|
-
try {
|
|
171
|
-
await artifact.transactAs({ kind: 'participant', seat: asSeatRef(seat) }, (facts) =>
|
|
172
|
-
facts.updateWakeAddress(asSeatRef(seat), { kind: routeKind, address: { sessionId } }));
|
|
173
|
-
} finally { await artifact.close(); }
|
|
174
|
-
}
|
|
175
|
-
return seats.length > 0;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export async function cancelOpenCodeDeliveries(sessionId: string, env: NodeJS.ProcessEnv, signal: AbortSignal): Promise<void> {
|
|
179
|
-
for (const { location, seat } of await discoverOpenCodeSeats(sessionId, env, signal)) {
|
|
180
|
-
if (signal.aborted) return;
|
|
181
|
-
if (seat.attachment !== 'attached') continue;
|
|
182
|
-
const artifact = createArtifactPort(location, undefined, signal);
|
|
183
|
-
try {
|
|
184
|
-
const { value: through } = await artifact.read((facts) => facts.channel.header().lastMessageIndex, signal);
|
|
185
|
-
if (through >= 0) await artifact.cancelDeliveries(asSeatRef(seat), through, signal);
|
|
186
|
-
} finally { await artifact.close(); }
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export async function endOpenCodeSession(sessionId: string, env: NodeJS.ProcessEnv, signal: AbortSignal): Promise<void> {
|
|
191
|
-
for (const { location, seat } of await discoverOpenCodeSeats(sessionId, env, signal)) {
|
|
192
|
-
if (signal.aborted) return;
|
|
193
|
-
const { Square } = await import('../../square-api.js');
|
|
194
|
-
const square = await Square.at({ path: location, env });
|
|
195
|
-
try { await square.endHeld(seat.participant, { owner: seat.owner, generation: seat.generation }); }
|
|
196
|
-
finally { await square.close(); }
|
|
197
|
-
}
|
|
198
|
-
}
|
|
File without changes
|
|
File without changes
|