@astrosheep/square 0.3.95 → 0.3.97
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 -1
- package/dist/artifact.js +11 -17
- package/dist/attention-presentation.d.ts +5 -4
- package/dist/attention-presentation.js +18 -8
- package/dist/boundary-presentation.js +31 -26
- package/dist/channel.d.ts +74 -0
- package/dist/channel.js +92 -0
- package/dist/claude-delivery.js +3 -9
- package/dist/cli/observation-commands.js +2 -2
- package/dist/codex-hook.d.ts +0 -2
- package/dist/codex-hook.js +0 -1
- package/dist/decisions.d.ts +2 -11
- package/dist/decisions.js +3 -8
- package/dist/delivery-health.js +3 -13
- package/dist/delivery.d.ts +1 -11
- package/dist/delivery.js +2 -9
- package/dist/identity.d.ts +0 -1
- package/dist/identity.js +0 -1
- package/dist/inbox.js +11 -7
- package/dist/mcp.js +65 -63
- package/dist/model.d.ts +6 -0
- package/dist/model.js +19 -0
- package/dist/notifications.d.ts +1 -1
- package/dist/ports.d.ts +5 -5
- package/dist/presentation-operations.d.ts +1 -0
- package/dist/sqlite-square.d.ts +3 -2
- package/dist/sqlite-square.js +91 -9
- package/dist/square-core.d.ts +1 -0
- package/dist/square-domain.js +22 -7
- package/dist/square-facade.d.ts +36 -10
- package/dist/square-sqlite.js +33 -4
- package/dist/square-tools.d.ts +1 -0
- package/dist/square-tools.js +9 -1
- package/dist/views.d.ts +1 -2
- package/dist/views.js +1 -9
- package/guides/operation-cancellation.md +5 -1
- package/guides/primitive-status-consolidation.md +1 -1
- package/guides/receive-primitive.md +4 -2
- package/guides/row-runtime-delivery-evidence.md +3 -3
- package/guides/row-storage-design.md +5 -1
- package/guides/square-api.md +63 -7
- package/package.json +5 -2
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/codex/hooks/hooks.json +6 -6
- package/plugins/paseo/client/marble.ts +7 -0
- package/plugins/paseo/client/markdown.tsx +65 -9
- package/plugins/paseo/client/palette.ts +43 -20
- package/plugins/paseo/client/square-message.tsx +57 -48
- package/plugins/paseo/shared/message.ts +1 -2
- package/plugins/pi/square-pi.js +6 -4
package/dist/artifact.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ export declare function writeSquareFile(location: string, state: SquareState, si
|
|
|
34
34
|
/** Explicit test/export projection. Hot callers must use the typed port. */
|
|
35
35
|
export declare function readSquareSnapshot(location: string, signal?: AbortSignal): Promise<SquareSnapshot>;
|
|
36
36
|
export declare function readSquareRevision(location: string, signal?: AbortSignal): Promise<number>;
|
|
37
|
-
export declare function observeSquareRevision(location: string): Promise<import('./file-changes.js').VersionObserver<number | null>>;
|
|
38
37
|
export declare function readSquareDeliveryRevision(location: string, signal?: AbortSignal): Promise<number>;
|
|
38
|
+
export declare function observeSquareRevision(location: string): Promise<import('./file-changes.js').VersionObserver<number | null>>;
|
|
39
39
|
export declare function observeSquareDeliveryRevision(location: string): Promise<import('./file-changes.js').VersionObserver<number | null>>;
|
|
40
40
|
export declare function diagnoseSquareFile(location: string): Promise<DiagnoseResult>;
|
package/dist/artifact.js
CHANGED
|
@@ -120,7 +120,7 @@ const readMethods = [
|
|
|
120
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', '
|
|
123
|
+
'receipt', 'receiptFacts', 'lease', 'participantCounts', 'seat', 'seatsForOwner',
|
|
124
124
|
'deliveryEvidence', 'deliveryByToken', 'cancelledThrough', 'deliveryDeferrals',
|
|
125
125
|
];
|
|
126
126
|
const writeMethods = ['applyLanding', 'markSeen', 'putLease', 'removeLease', 'storeSeat', 'removeSeat', 'updateWakeAddress', 'reattachSeat', 'detachSeat', 'cancelThrough', 'claimDelivery', 'prepareDelivery', 'completeDelivery', 'observeNativeDelivery', 'putDeliveryDeferral', 'renewDeliveryDeferral', 'releaseDeliveryDeferral'];
|
|
@@ -404,26 +404,14 @@ export function readSquareSnapshot(location, signal) {
|
|
|
404
404
|
export function readSquareRevision(location, signal) {
|
|
405
405
|
return inspectFile(location, db => createSqliteDriver(db).header().revision, signal);
|
|
406
406
|
}
|
|
407
|
-
export function observeSquareRevision(location) {
|
|
408
|
-
return observeFileVersion(location, 'square-revision', async (file, signal) => {
|
|
409
|
-
try {
|
|
410
|
-
return await readSquareRevision(file, signal);
|
|
411
|
-
}
|
|
412
|
-
catch (error) {
|
|
413
|
-
abort(signal);
|
|
414
|
-
if (error instanceof InternalSquareError && error.code === 'not_found')
|
|
415
|
-
return null;
|
|
416
|
-
throw error;
|
|
417
|
-
}
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
407
|
export function readSquareDeliveryRevision(location, signal) {
|
|
421
408
|
return inspectFile(location, db => createSqliteDriver(db).header().deliveryRevision, signal);
|
|
422
409
|
}
|
|
423
|
-
|
|
424
|
-
|
|
410
|
+
function observeRevision(location, revision) {
|
|
411
|
+
const read = revision === 'square-revision' ? readSquareRevision : readSquareDeliveryRevision;
|
|
412
|
+
return observeFileVersion(location, revision, async (file, signal) => {
|
|
425
413
|
try {
|
|
426
|
-
return await
|
|
414
|
+
return await read(file, signal);
|
|
427
415
|
}
|
|
428
416
|
catch (error) {
|
|
429
417
|
abort(signal);
|
|
@@ -433,6 +421,12 @@ export function observeSquareDeliveryRevision(location) {
|
|
|
433
421
|
}
|
|
434
422
|
});
|
|
435
423
|
}
|
|
424
|
+
export function observeSquareRevision(location) {
|
|
425
|
+
return observeRevision(location, 'square-revision');
|
|
426
|
+
}
|
|
427
|
+
export function observeSquareDeliveryRevision(location) {
|
|
428
|
+
return observeRevision(location, 'square-delivery-revision');
|
|
429
|
+
}
|
|
436
430
|
export async function diagnoseSquareFile(location) {
|
|
437
431
|
try {
|
|
438
432
|
return { problems: [], ...await inspectFile(await canonicalPath(location), inspectSqlite) };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DirectedNotificationRoute } from './model.js';
|
|
1
|
+
import type { ActivityDelivery, DirectedNotificationRoute } from './model.js';
|
|
2
2
|
export declare const ATTENTION_BODY_MAX = 200;
|
|
3
3
|
export interface AttentionPreview {
|
|
4
4
|
squarePath: string;
|
|
@@ -7,13 +7,14 @@ export interface AttentionPreview {
|
|
|
7
7
|
actor: string;
|
|
8
8
|
route: DirectedNotificationRoute;
|
|
9
9
|
body: string;
|
|
10
|
+
delivery?: ActivityDelivery;
|
|
10
11
|
}
|
|
11
|
-
export declare function previewAttentionBody(body: string): string;
|
|
12
|
-
export declare function attentionBodyIsClipped(body: string): boolean;
|
|
12
|
+
export declare function previewAttentionBody(body: string, delivery?: ActivityDelivery): string;
|
|
13
|
+
export declare function attentionBodyIsClipped(body: string, delivery?: ActivityDelivery): boolean;
|
|
13
14
|
export declare function displayAttentionPath(squarePath: string): string;
|
|
14
15
|
export declare function renderAttentionDescription({ actor, route }: Pick<AttentionPreview, 'actor' | 'route'>): string;
|
|
15
16
|
/** Full notification text for human-facing expansion, never the bounded model preview. */
|
|
16
17
|
export declare function renderAttentionFull(attention: AttentionPreview): string;
|
|
17
18
|
/** Shared model-facing envelope; transport correlation and human cards stay outside it. */
|
|
18
|
-
export declare function renderSquareReminder(previews: readonly string[]): string;
|
|
19
|
+
export declare function renderSquareReminder(previews: readonly string[], deliveries?: readonly (ActivityDelivery | undefined)[]): string;
|
|
19
20
|
export declare function renderAttentionPreview(attention: AttentionPreview): string;
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { homedir } from 'node:os';
|
|
2
2
|
import { formatActivityId } from './square-core.js';
|
|
3
3
|
export const ATTENTION_BODY_MAX = 200;
|
|
4
|
-
export function previewAttentionBody(body) {
|
|
5
|
-
const compact = body.replace(/\r\n/g, '\n');
|
|
6
|
-
if (compact.length <= ATTENTION_BODY_MAX)
|
|
4
|
+
export function previewAttentionBody(body, delivery) {
|
|
5
|
+
const compact = (delivery?.mode === 'direct' ? delivery.preview ?? body : body).replace(/\r\n/g, '\n');
|
|
6
|
+
if (delivery?.mode === 'direct' || compact.length <= ATTENTION_BODY_MAX)
|
|
7
7
|
return compact;
|
|
8
8
|
return `${compact.slice(0, ATTENTION_BODY_MAX).trimEnd()}…`;
|
|
9
9
|
}
|
|
10
|
-
export function attentionBodyIsClipped(body) {
|
|
11
|
-
return
|
|
10
|
+
export function attentionBodyIsClipped(body, delivery) {
|
|
11
|
+
return delivery?.mode === 'direct'
|
|
12
|
+
? delivery.preview !== undefined
|
|
13
|
+
: body.replace(/\r\n/g, '\n').length > ATTENTION_BODY_MAX;
|
|
12
14
|
}
|
|
13
15
|
export function displayAttentionPath(squarePath) {
|
|
14
16
|
return squarePath.startsWith(homedir())
|
|
@@ -25,7 +27,7 @@ export function renderAttentionDescription({ actor, route }) {
|
|
|
25
27
|
}
|
|
26
28
|
/** Full notification text for human-facing expansion, never the bounded model preview. */
|
|
27
29
|
export function renderAttentionFull(attention) {
|
|
28
|
-
return renderAttentionBlock(attention, attention.body);
|
|
30
|
+
return attention.delivery?.mode === 'direct' ? renderDirectBlock(attention) : renderAttentionBlock(attention, attention.body);
|
|
29
31
|
}
|
|
30
32
|
/** Attribute values keep their quotes; the stored body and the human card stay unescaped. */
|
|
31
33
|
function escapeAttribute(value) {
|
|
@@ -49,14 +51,20 @@ function renderAttentionBlock(attention, body) {
|
|
|
49
51
|
'</square-activity>',
|
|
50
52
|
].join('\n');
|
|
51
53
|
}
|
|
54
|
+
function renderDirectBlock(attention) {
|
|
55
|
+
return escapeSquareTags(previewAttentionBody(attention.body, attention.delivery));
|
|
56
|
+
}
|
|
52
57
|
/** Shared model-facing envelope; transport correlation and human cards stay outside it. */
|
|
53
|
-
export function renderSquareReminder(previews) {
|
|
58
|
+
export function renderSquareReminder(previews, deliveries = []) {
|
|
54
59
|
if (previews.length === 0)
|
|
55
60
|
return '';
|
|
61
|
+
const header = deliveries.some(item => item?.mode === 'direct')
|
|
62
|
+
? 'This is an external notification, not a direct message from the user.'
|
|
63
|
+
: 'This is an external reminder from Square, not a direct message from the user.';
|
|
56
64
|
return [
|
|
57
65
|
'',
|
|
58
66
|
'<system-reminder>',
|
|
59
|
-
|
|
67
|
+
header,
|
|
60
68
|
'',
|
|
61
69
|
previews.join('\n\n'),
|
|
62
70
|
'</system-reminder>',
|
|
@@ -64,6 +72,8 @@ export function renderSquareReminder(previews) {
|
|
|
64
72
|
].join('\n');
|
|
65
73
|
}
|
|
66
74
|
export function renderAttentionPreview(attention) {
|
|
75
|
+
if (attention.delivery?.mode === 'direct')
|
|
76
|
+
return renderDirectBlock(attention);
|
|
67
77
|
const remaining = attention.body.replace(/\r\n/g, '\n').length - ATTENTION_BODY_MAX;
|
|
68
78
|
const attributes = [
|
|
69
79
|
`square="${escapeAttribute(displayAttentionPath(attention.squarePath))}"`,
|
|
@@ -14,36 +14,43 @@ export function renderPendingAtBoundary(inbox) {
|
|
|
14
14
|
function renderBoundary(inbox) {
|
|
15
15
|
const queued = inbox.flatMap((membership) => membership.notifications.map((notification) => ({ membership, notification })));
|
|
16
16
|
const blocks = [];
|
|
17
|
+
const agoraBlocks = [];
|
|
18
|
+
const deliveries = [];
|
|
19
|
+
let remainingAgora = queued.filter(({ notification }) => notification.delivery?.mode !== 'direct').length;
|
|
17
20
|
const complete = [];
|
|
18
21
|
let omitted = 0;
|
|
19
22
|
const omission = (count) => `${count} more ${count === 1 ? 'activity' : 'activities'} not shown.`;
|
|
20
|
-
for (const
|
|
23
|
+
for (const entry of queued) {
|
|
21
24
|
const { membership, notification } = entry;
|
|
22
|
-
const block =
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
25
|
+
const block = renderAttentionPreview({
|
|
26
|
+
squarePath: membership.location,
|
|
27
|
+
actIndex: notification.actIndex,
|
|
28
|
+
recipient: membership.seat.participant,
|
|
29
|
+
actor: notification.actor,
|
|
30
|
+
route: notification.route,
|
|
31
|
+
body: notification.body,
|
|
32
|
+
delivery: notification.delivery,
|
|
33
|
+
});
|
|
34
|
+
if (notification.delivery?.mode !== 'direct') {
|
|
35
|
+
remainingAgora -= 1;
|
|
36
|
+
const omittedAfter = omitted + remainingAgora;
|
|
37
|
+
const prospective = renderSquareReminder([...agoraBlocks, block, ...(omittedAfter > 0 ? [omission(omittedAfter)] : [])]);
|
|
38
|
+
if (prospective.length > CONTEXT_MAX) {
|
|
39
|
+
omitted += 1;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
agoraBlocks.push(block);
|
|
37
43
|
}
|
|
38
44
|
blocks.push(block);
|
|
45
|
+
deliveries.push(notification.delivery);
|
|
39
46
|
complete.push({
|
|
40
47
|
membership,
|
|
41
|
-
|
|
42
|
-
markSeen: !attentionBodyIsClipped(notification.body),
|
|
48
|
+
actIndex: notification.actIndex,
|
|
49
|
+
markSeen: !attentionBodyIsClipped(notification.body, notification.delivery),
|
|
43
50
|
});
|
|
44
51
|
}
|
|
45
52
|
return {
|
|
46
|
-
context: renderSquareReminder([...blocks, ...(omitted > 0 ? [omission(omitted)] : [])]),
|
|
53
|
+
context: renderSquareReminder([...blocks, ...(omitted > 0 ? [omission(omitted)] : [])], deliveries),
|
|
47
54
|
complete,
|
|
48
55
|
};
|
|
49
56
|
}
|
|
@@ -67,12 +74,10 @@ async function presentPendingAtBoundaryUnlocked(identity, present, lookup = sess
|
|
|
67
74
|
artifact = createArtifactPort(location);
|
|
68
75
|
artifacts.set(location, artifact);
|
|
69
76
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
claims.push({ key: pendingKey(entry.membership, index), claim });
|
|
75
|
-
}
|
|
77
|
+
const claim = await preparePendingPresentation({ artifact, location, participant: seat.participant,
|
|
78
|
+
activity: entry.actIndex, seat, markSeen: entry.markSeen, signal });
|
|
79
|
+
if (claim !== undefined)
|
|
80
|
+
claims.push({ key: pendingKey(entry.membership, entry.actIndex), claim });
|
|
76
81
|
}
|
|
77
82
|
}
|
|
78
83
|
catch (error) {
|
|
@@ -85,7 +90,7 @@ async function presentPendingAtBoundaryUnlocked(identity, present, lookup = sess
|
|
|
85
90
|
const fresh = renderBoundary(pending.map(membership => ({ ...membership,
|
|
86
91
|
notifications: membership.notifications.filter(note => eligible.has(pendingKey(membership, note.actIndex))),
|
|
87
92
|
})));
|
|
88
|
-
const included = new Set(fresh.complete.
|
|
93
|
+
const included = new Set(fresh.complete.map(entry => pendingKey(entry.membership, entry.actIndex)));
|
|
89
94
|
return { payload: fresh.context, included: claims.filter(({ key }) => included.has(key)).map(({ claim }) => claim) };
|
|
90
95
|
}, present, { signal, classifyError: () => 'failed' });
|
|
91
96
|
if (report.observation.kind === 'threw')
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type HarnessSessionChannel } from './participant-identity.js';
|
|
2
|
+
import type { SquareResourceOwner, WakeTransportPort } from './ports.js';
|
|
3
|
+
import type { ActivityId, ChannelSendInput, ChannelSendResult, DeliveryDeferral, HeldSeat, MetadataMatch, OperationControl, Owner, Participant, WakeAddress, PendingOptions, PendingResult, AckResult } from './square-facade.js';
|
|
4
|
+
export { harnessOwner, decodeHarnessOwner, localUserOwner } from './owner.js';
|
|
5
|
+
export type { ActivityDelivery, ActivityMetadata, ChannelTruncate, PerceivedActivity, PendingOptions, PendingResult, AckResult } from './square-facade.js';
|
|
6
|
+
export type { ActivityId, ChannelSendInput, ChannelSendResult, DeliveryDeferral, HeldSeat, MetadataMatch, OperationControl, Owner, WakeAddress, SquareResourceOwner, WakeTransportPort, HarnessSessionChannel };
|
|
7
|
+
export interface ResolvedHarnessSession {
|
|
8
|
+
readonly channel: HarnessSessionChannel;
|
|
9
|
+
readonly sessionId: string;
|
|
10
|
+
readonly agentId?: string;
|
|
11
|
+
readonly owner: Owner;
|
|
12
|
+
readonly wakeAddress?: WakeAddress;
|
|
13
|
+
}
|
|
14
|
+
export declare function resolveHarnessSession(env?: NodeJS.ProcessEnv): ResolvedHarnessSession | undefined;
|
|
15
|
+
export interface ChannelOpenInput {
|
|
16
|
+
readonly path: string;
|
|
17
|
+
readonly cwd?: string;
|
|
18
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
19
|
+
readonly clock?: () => number;
|
|
20
|
+
readonly wakeTransport?: WakeTransportPort | false;
|
|
21
|
+
readonly resources?: SquareResourceOwner;
|
|
22
|
+
}
|
|
23
|
+
export interface ChannelCreateInput extends ChannelOpenInput {
|
|
24
|
+
readonly force?: boolean;
|
|
25
|
+
readonly description?: string;
|
|
26
|
+
}
|
|
27
|
+
type EndpointOptions = {
|
|
28
|
+
readonly owner: Owner;
|
|
29
|
+
readonly wakeAddress?: WakeAddress;
|
|
30
|
+
};
|
|
31
|
+
type EndpointIdentity = {
|
|
32
|
+
readonly owner: Owner;
|
|
33
|
+
readonly generation: ActivityId;
|
|
34
|
+
};
|
|
35
|
+
/** A scoped direct-messaging vocabulary over the shared Square resource. */
|
|
36
|
+
export declare class Channel {
|
|
37
|
+
private readonly square;
|
|
38
|
+
private constructor();
|
|
39
|
+
get location(): string;
|
|
40
|
+
static open(input: ChannelOpenInput, control?: OperationControl): Promise<Channel>;
|
|
41
|
+
static create(input: ChannelCreateInput, control?: OperationControl): Promise<Channel>;
|
|
42
|
+
static inMemory(input?: Omit<ChannelCreateInput, 'path' | 'resources' | 'force'>): Channel;
|
|
43
|
+
attach(name: string, options: EndpointOptions & {
|
|
44
|
+
readonly takeover?: boolean;
|
|
45
|
+
}, control?: OperationControl): Promise<ChannelEndpoint>;
|
|
46
|
+
reattach(name: string, options: EndpointOptions, control?: OperationControl): Promise<ChannelEndpoint | null>;
|
|
47
|
+
endpoint(name: string, options: {
|
|
48
|
+
readonly owner: Owner;
|
|
49
|
+
}): Promise<ChannelEndpoint>;
|
|
50
|
+
endpointsOf(owner: Owner): Promise<readonly HeldSeat[]>;
|
|
51
|
+
detach(name: string, identity: EndpointIdentity): Promise<true | null>;
|
|
52
|
+
remove(name: string, identity: EndpointIdentity): Promise<boolean>;
|
|
53
|
+
cancelDeliveries(name: string, identity: EndpointIdentity): Promise<boolean>;
|
|
54
|
+
send(input: ChannelSendInput, control?: OperationControl): Promise<ChannelSendResult>;
|
|
55
|
+
close(): Promise<void>;
|
|
56
|
+
}
|
|
57
|
+
/** Captures one recipient generation through its existing Participant handle. */
|
|
58
|
+
export declare class ChannelEndpoint {
|
|
59
|
+
private readonly channel;
|
|
60
|
+
private readonly participant;
|
|
61
|
+
constructor(channel: Channel, participant: Participant);
|
|
62
|
+
get name(): string;
|
|
63
|
+
get generation(): ActivityId;
|
|
64
|
+
pending(options?: PendingOptions, control?: OperationControl): Promise<PendingResult>;
|
|
65
|
+
ack(ids: readonly ActivityId[], control?: OperationControl): Promise<AckResult>;
|
|
66
|
+
leave(control?: OperationControl): Promise<void>;
|
|
67
|
+
deferDelivery(input: Readonly<{
|
|
68
|
+
match: MetadataMatch;
|
|
69
|
+
durationMs: number;
|
|
70
|
+
}>, control?: OperationControl): Promise<DeliveryDeferral>;
|
|
71
|
+
renewDeliveryDeferral(id: string, durationMs: number, control?: OperationControl): Promise<DeliveryDeferral | null>;
|
|
72
|
+
releaseDeliveryDeferral(id: string, control?: OperationControl): Promise<boolean>;
|
|
73
|
+
send(input: Omit<ChannelSendInput, 'from'>, control?: OperationControl): Promise<ChannelSendResult>;
|
|
74
|
+
}
|
package/dist/channel.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { codexWakeAddress } from './codex-address.js';
|
|
2
|
+
import { SquareError } from './model.js';
|
|
3
|
+
import { harnessOwner } from './owner.js';
|
|
4
|
+
import { harnessSessionSources } from './participant-identity.js';
|
|
5
|
+
import { Square } from './square-api.js';
|
|
6
|
+
import { squareLocation } from './square-location.js';
|
|
7
|
+
export { harnessOwner, decodeHarnessOwner, localUserOwner } from './owner.js';
|
|
8
|
+
export function resolveHarnessSession(env = process.env) {
|
|
9
|
+
for (const { variable, channel } of harnessSessionSources) {
|
|
10
|
+
const sessionId = env[variable]?.trim();
|
|
11
|
+
if (!sessionId)
|
|
12
|
+
continue;
|
|
13
|
+
const agentId = channel === 'claude-code' && env.CLAUDE_CODE_CHILD_SESSION?.trim() === '1'
|
|
14
|
+
? env.CLAUDE_CODE_AGENT_ID?.trim() || undefined : undefined;
|
|
15
|
+
const wakeAddress = channel === 'codex' ? codexWakeAddress(sessionId, env)
|
|
16
|
+
: channel === 'paseo' ? { kind: 'paseo', address: { agentId: sessionId } } : undefined;
|
|
17
|
+
return { channel, sessionId, ...(agentId === undefined ? {} : { agentId }),
|
|
18
|
+
owner: harnessOwner({ channel, session: sessionId, ...(agentId === undefined ? {} : { agent: agentId }) }),
|
|
19
|
+
...(wakeAddress === undefined ? {} : { wakeAddress }) };
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
function explicitLocation(input) {
|
|
24
|
+
if (typeof input?.path !== 'string' || input.path.trim() === '')
|
|
25
|
+
throw new SquareError('invalid_args', 'Channel path is required.');
|
|
26
|
+
return squareLocation(input);
|
|
27
|
+
}
|
|
28
|
+
/** A scoped direct-messaging vocabulary over the shared Square resource. */
|
|
29
|
+
export class Channel {
|
|
30
|
+
square;
|
|
31
|
+
constructor(square) {
|
|
32
|
+
this.square = square;
|
|
33
|
+
}
|
|
34
|
+
get location() { return this.square.location; }
|
|
35
|
+
static async open(input, control) {
|
|
36
|
+
const path = explicitLocation(input);
|
|
37
|
+
return new Channel(await Square.at({ ...input, path }, control));
|
|
38
|
+
}
|
|
39
|
+
static async create(input, control) {
|
|
40
|
+
const path = explicitLocation(input);
|
|
41
|
+
return new Channel(await Square.build({ ...input, path, hardCap: null, markdown: input.description ?? '# Channel\n' }, control));
|
|
42
|
+
}
|
|
43
|
+
static inMemory(input = {}) {
|
|
44
|
+
return new Channel(Square.inMemory({ ...input, hardCap: null, markdown: input.description ?? '# Channel\n' }));
|
|
45
|
+
}
|
|
46
|
+
async attach(name, options, control) {
|
|
47
|
+
return new ChannelEndpoint(this, (await this.square.join(name, options, control)).participant);
|
|
48
|
+
}
|
|
49
|
+
async reattach(name, options, control) {
|
|
50
|
+
const participant = await this.square.reattach(name, options, control);
|
|
51
|
+
return participant === null ? null : new ChannelEndpoint(this, participant);
|
|
52
|
+
}
|
|
53
|
+
async endpoint(name, options) {
|
|
54
|
+
return new ChannelEndpoint(this, await this.square.participant(name, options));
|
|
55
|
+
}
|
|
56
|
+
endpointsOf(owner) { return this.square.seatsOf(owner); }
|
|
57
|
+
detach(name, identity) { return this.square.detach(name, identity); }
|
|
58
|
+
async remove(name, identity) { return await this.square.endHeld(name, identity) !== null; }
|
|
59
|
+
cancelDeliveries(name, identity) { return this.square.cancelDeliveries(name, identity); }
|
|
60
|
+
send(input, control) { return this.square.send(input, control); }
|
|
61
|
+
close() { return this.square.close(); }
|
|
62
|
+
}
|
|
63
|
+
/** Captures one recipient generation through its existing Participant handle. */
|
|
64
|
+
export class ChannelEndpoint {
|
|
65
|
+
channel;
|
|
66
|
+
participant;
|
|
67
|
+
constructor(channel, participant) {
|
|
68
|
+
this.channel = channel;
|
|
69
|
+
this.participant = participant;
|
|
70
|
+
}
|
|
71
|
+
get name() { return this.participant.name; }
|
|
72
|
+
get generation() { return this.participant.generation; }
|
|
73
|
+
pending(options, control) {
|
|
74
|
+
return this.participant.pending(options, control);
|
|
75
|
+
}
|
|
76
|
+
ack(ids, control) {
|
|
77
|
+
return this.participant.ack(ids, control);
|
|
78
|
+
}
|
|
79
|
+
async leave(control) { await this.participant.done(undefined, {}, control); }
|
|
80
|
+
deferDelivery(input, control) {
|
|
81
|
+
return this.participant.deferDelivery(input, control);
|
|
82
|
+
}
|
|
83
|
+
renewDeliveryDeferral(id, durationMs, control) {
|
|
84
|
+
return this.participant.renewDeliveryDeferral(id, durationMs, control);
|
|
85
|
+
}
|
|
86
|
+
releaseDeliveryDeferral(id, control) {
|
|
87
|
+
return this.participant.releaseDeliveryDeferral(id, control);
|
|
88
|
+
}
|
|
89
|
+
send(input, control) {
|
|
90
|
+
return this.channel.send({ ...input, from: this.name }, control);
|
|
91
|
+
}
|
|
92
|
+
}
|
package/dist/claude-delivery.js
CHANGED
|
@@ -12,12 +12,6 @@ const marker = /(?:^|\n)\[square-inbox:([a-zA-Z0-9-]+)\](?:\n|$)/g;
|
|
|
12
12
|
export function nativeTokens(text) { return [...text.matchAll(marker)].map((match) => match[1]); }
|
|
13
13
|
export function nativePayload(token, preview) { return `[square-inbox:${token}]\n${preview}\n[/square-inbox:${token}]`; }
|
|
14
14
|
function nativeSupported(route) { return route.address.loaded === 'true' && !!route.address.endpoint; }
|
|
15
|
-
function sameAddress(left, right) {
|
|
16
|
-
if (left === undefined)
|
|
17
|
-
return false;
|
|
18
|
-
const keys = Object.keys(left);
|
|
19
|
-
return keys.length === Object.keys(right).length && keys.every((key) => left[key] === right[key]);
|
|
20
|
-
}
|
|
21
15
|
function hashPayload(payload) { return createHash('sha256').update(payload).digest('hex'); }
|
|
22
16
|
function previewFacts(facts, request) {
|
|
23
17
|
const index = parseActivityId(request.activity);
|
|
@@ -80,8 +74,7 @@ function currentPayload(facts, request) {
|
|
|
80
74
|
const seat = facts.seat(request.participant), evidence = facts.deliveryByToken(request.claimToken);
|
|
81
75
|
if (seat === undefined || seat.attachment !== 'attached' || evidence === undefined || seat.generation !== evidence.generation
|
|
82
76
|
|| seat.generation !== request.seat.generation || seat.owner !== request.seat.owner
|
|
83
|
-
|| evidence.activity !== request.activity || !['dispatching', 'unknown', 'accepted'].includes(evidence.outcome)
|
|
84
|
-
|| seat.wakeAddress?.kind !== 'claude-native' || !sameAddress(seat.wakeAddress.address, request.route.address))
|
|
77
|
+
|| evidence.activity !== request.activity || !['dispatching', 'unknown', 'accepted'].includes(evidence.outcome))
|
|
85
78
|
return undefined;
|
|
86
79
|
const native = previewFacts(facts, request);
|
|
87
80
|
if (native === undefined)
|
|
@@ -150,7 +143,8 @@ export async function observeClaudeDelivery(sessionId, text, operation, env = pr
|
|
|
150
143
|
return { recognized, current: false };
|
|
151
144
|
if (operation === 'stored')
|
|
152
145
|
await presentPending({ artifact: row.artifact, location: request.location,
|
|
153
|
-
|
|
146
|
+
// Claude's stored event consumes even Agora previews; retain that contract for both modes.
|
|
147
|
+
participant: request.participant, activity: request.activity, seat: request.seat, markSeen: true, sink: { present() { } },
|
|
154
148
|
current: facts => currentPayload(facts, request) === payload });
|
|
155
149
|
}
|
|
156
150
|
return { recognized, current: true };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { once } from 'node:events';
|
|
3
3
|
import { runClaudeMod } from '../claude-mod.js';
|
|
4
|
-
import {
|
|
4
|
+
import { runCodexHook } from '../codex-hook.js';
|
|
5
5
|
import { isSquareError } from '../model.js';
|
|
6
6
|
import { commandPrefix, participantIdentity, truncateChars, withPathOutput, quoteShell, } from '../presentation.js';
|
|
7
7
|
import { PARTICIPANTS_DEFAULT_LIMIT, PARTICIPANTS_MAX_LIMIT, renderHistoryResult, renderParticipantsResult, renderStatusResult, } from '../operation-presentation.js';
|
|
@@ -523,4 +523,4 @@ function hookCommand(runHook) {
|
|
|
523
523
|
};
|
|
524
524
|
}
|
|
525
525
|
export const claudeModCommand = hookCommand(runClaudeMod);
|
|
526
|
-
export const codexHookCommand = hookCommand(
|
|
526
|
+
export const codexHookCommand = hookCommand(runCodexHook);
|
package/dist/codex-hook.d.ts
CHANGED
|
@@ -5,10 +5,8 @@ export interface CodexHookInput {
|
|
|
5
5
|
session_id?: unknown;
|
|
6
6
|
hook_event_name?: unknown;
|
|
7
7
|
cwd?: unknown;
|
|
8
|
-
source?: unknown;
|
|
9
8
|
}
|
|
10
9
|
export type HookTrace = (line: string) => void;
|
|
11
10
|
export declare const CODEX_HOOK_BUDGET_MS = 4500;
|
|
12
11
|
export declare function codexHookResponse(input: CodexHookInput, lookup?: (identity: Owner, env?: NodeJS.ProcessEnv, signal?: AbortSignal) => Promise<PendingMembership[]> | PendingMembership[], env?: NodeJS.ProcessEnv, deliveryTransports?: WakeTransports, trace?: HookTrace): Promise<object | undefined>;
|
|
13
12
|
export declare function runCodexHook(inputText: string, env?: NodeJS.ProcessEnv): Promise<string>;
|
|
14
|
-
export declare const runCodexHookAsync: typeof runCodexHook;
|
package/dist/codex-hook.js
CHANGED
package/dist/decisions.d.ts
CHANGED
|
@@ -7,12 +7,7 @@ type TimedAct<K extends Act['kind'] = Act['kind']> = Extract<Act, {
|
|
|
7
7
|
};
|
|
8
8
|
import type { ExpressResult, ParticipantSummary, UnreadActivitySummary } from './square-facade.js';
|
|
9
9
|
export declare function resolveKnownName(facts: SquareRead, name: string): string;
|
|
10
|
-
export
|
|
11
|
-
joinedName: string;
|
|
12
|
-
addParticipant: boolean;
|
|
13
|
-
joinAct?: TimedAct<'join'>;
|
|
14
|
-
}
|
|
15
|
-
export declare function decideJoin(facts: SquareRead, name: string, now: number): DecideJoinResult;
|
|
10
|
+
export declare function decideJoin(facts: SquareRead, name: string, now: number): TimedAct<'join'> | undefined;
|
|
16
11
|
export type ActDecision = ({
|
|
17
12
|
type: 'sent';
|
|
18
13
|
act: TimedAct;
|
|
@@ -21,8 +16,6 @@ export type ActDecision = ({
|
|
|
21
16
|
activitySummaries: UnreadActivitySummary[];
|
|
22
17
|
} | {
|
|
23
18
|
type: 'capped';
|
|
24
|
-
count: number;
|
|
25
|
-
hardCap: number;
|
|
26
19
|
} | {
|
|
27
20
|
type: 'throttled';
|
|
28
21
|
delayMs: number;
|
|
@@ -45,9 +38,7 @@ export declare function decideAct(facts: SquareRead, input: {
|
|
|
45
38
|
}): ActDecision;
|
|
46
39
|
export declare function validateDoneBody(body: unknown): void;
|
|
47
40
|
export declare function coreDone(facts: SquareRead, name: string, body: string, now: number): TimedAct<'done'>;
|
|
48
|
-
export declare function
|
|
49
|
-
export declare function coreIgnore(facts: SquareRead, actor: string, target: string, now: number): TimedAct<"listen" | "ignore" | "unlisten"> | undefined;
|
|
50
|
-
export declare function coreUnlisten(facts: SquareRead, actor: string, target: string, now: number): TimedAct<"listen" | "ignore" | "unlisten"> | undefined;
|
|
41
|
+
export declare function attentionChange(facts: SquareRead, kind: 'listen' | 'ignore' | 'unlisten', actor: string, target: string, now: number): TimedAct<'listen' | 'ignore' | 'unlisten'> | undefined;
|
|
51
42
|
export declare function coreListening(facts: SquareRead, actor: string): readonly string[];
|
|
52
43
|
export declare function compareParticipantActivity(a: Pick<ParticipantSummary, 'name' | 'lastActiveAt'>, b: Pick<ParticipantSummary, 'name' | 'lastActiveAt'>): number;
|
|
53
44
|
export declare function coreParticipants(facts: SquareRead, now: number): ParticipantSummary[];
|
package/dist/decisions.js
CHANGED
|
@@ -13,9 +13,7 @@ export function resolveKnownName(facts, name) {
|
|
|
13
13
|
export function decideJoin(facts, name, now) {
|
|
14
14
|
validateName(name);
|
|
15
15
|
const current = facts.participant(name);
|
|
16
|
-
|
|
17
|
-
return current?.joined ? { joinedName, addParticipant: false }
|
|
18
|
-
: { joinedName, addParticipant: current === undefined, joinAct: { kind: 'join', actor: joinedName, at: now } };
|
|
16
|
+
return current?.joined ? undefined : { kind: 'join', actor: current?.name ?? name, at: now };
|
|
19
17
|
}
|
|
20
18
|
function validateActivityName(name) {
|
|
21
19
|
try {
|
|
@@ -131,7 +129,7 @@ export function decideAct(facts, input) {
|
|
|
131
129
|
if (!current.joined)
|
|
132
130
|
throw activityRefusal('not_joined', `${name} has not joined this square`);
|
|
133
131
|
if (header.hardCap !== null && current.sayCount >= header.hardCap)
|
|
134
|
-
return { type: 'capped'
|
|
132
|
+
return { type: 'capped' };
|
|
135
133
|
if (reach === 'bell') {
|
|
136
134
|
const recent = facts.recentSayTimes(now - 3600000, 1, name, true);
|
|
137
135
|
if (recent.length > 0)
|
|
@@ -162,7 +160,7 @@ export function coreDone(facts, name, body, now) {
|
|
|
162
160
|
requireStanding(current);
|
|
163
161
|
return { kind: 'done', actor: current.name, at: now };
|
|
164
162
|
}
|
|
165
|
-
function attentionChange(facts, kind, actor, target, now) {
|
|
163
|
+
export function attentionChange(facts, kind, actor, target, now) {
|
|
166
164
|
const current = resolveStanding(facts, actor);
|
|
167
165
|
validateName(target);
|
|
168
166
|
requireStanding(current);
|
|
@@ -175,9 +173,6 @@ function attentionChange(facts, kind, actor, target, now) {
|
|
|
175
173
|
}
|
|
176
174
|
return existing?.kind === kind ? undefined : { kind, actor: current.name, target, at: now };
|
|
177
175
|
}
|
|
178
|
-
export function coreListen(facts, actor, target, now) { return attentionChange(facts, 'listen', actor, target, now); }
|
|
179
|
-
export function coreIgnore(facts, actor, target, now) { return attentionChange(facts, 'ignore', actor, target, now); }
|
|
180
|
-
export function coreUnlisten(facts, actor, target, now) { return attentionChange(facts, 'unlisten', actor, target, now); }
|
|
181
176
|
export function coreListening(facts, actor) {
|
|
182
177
|
const current = resolveStanding(facts, actor);
|
|
183
178
|
return facts.attention(current.name).filter((item) => item.kind === 'listen').map((item) => item.target);
|
package/dist/delivery-health.js
CHANGED
|
@@ -32,17 +32,7 @@ export async function classifyDeliveryHealth(squarePath, opts) {
|
|
|
32
32
|
|| row.outcome === 'dispatching' && (row.expiresAt === undefined || row.expiresAt > now)));
|
|
33
33
|
const terminal = evidence.findLast((row) => row.kind === 'wake' && (row.outcome === 'accepted' || row.outcome === 'unknown'));
|
|
34
34
|
const attemptRow = evidence.findLast((row) => row.kind === 'wake' && (row.outcome === 'accepted' || row.outcome === 'unknown' || row.outcome === 'failed'));
|
|
35
|
-
const attempt = attemptRow === undefined || seat === undefined
|
|
36
|
-
at: attemptRow.at,
|
|
37
|
-
attention: { squarePath, recipient: seat.participant, actIndex: note.index },
|
|
38
|
-
routeKind: attemptRow.route.kind,
|
|
39
|
-
outcome: attemptRow.outcome,
|
|
40
|
-
attemptN: attemptRow.attemptN,
|
|
41
|
-
...(attemptRow.signature === undefined ? {} : { signature: attemptRow.signature }),
|
|
42
|
-
...(attemptRow.message === undefined ? {} : { message: attemptRow.message }),
|
|
43
|
-
...(attemptRow.diagnostic === undefined ? {} : { diagnostic: attemptRow.diagnostic }),
|
|
44
|
-
...(attemptRow.nativeObservations === undefined ? {} : { nativeObservations: attemptRow.nativeObservations }),
|
|
45
|
-
};
|
|
35
|
+
const attempt = attemptRow === undefined || seat === undefined ? undefined : deliveryAttempt(squarePath, attemptRow, { squarePath, recipient: seat.participant, actIndex: note.index });
|
|
46
36
|
const kind = presentation
|
|
47
37
|
? 'presented-not-delivered'
|
|
48
38
|
: terminal?.outcome === 'accepted'
|
|
@@ -127,12 +117,12 @@ function formatRelease(release, now) {
|
|
|
127
117
|
const diagnostic = release.diagnostic === undefined ? '' : ` · ${displayField(JSON.stringify(release.diagnostic))}`;
|
|
128
118
|
return ` · ${formatActivityId(release.attention.actIndex)} → ${displayField(participantIdentity(release.attention.recipient))} · ${route} · ${reason} · ${age}${message}${diagnostic}`;
|
|
129
119
|
}
|
|
130
|
-
function deliveryAttempt(squarePath, row) {
|
|
120
|
+
function deliveryAttempt(squarePath, row, attention = { squarePath, recipient: row.participant, actIndex: Number(row.activity.slice(4)) }) {
|
|
131
121
|
if (row.route?.kind === undefined || row.outcome === 'released' || row.outcome === 'dispatching')
|
|
132
122
|
return undefined;
|
|
133
123
|
return {
|
|
134
124
|
at: row.at,
|
|
135
|
-
attention
|
|
125
|
+
attention,
|
|
136
126
|
routeKind: row.route.kind,
|
|
137
127
|
outcome: row.outcome,
|
|
138
128
|
attemptN: row.attemptN,
|
package/dist/delivery.d.ts
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
|
-
import type { DirectedNotificationRoute,
|
|
1
|
+
import type { DirectedNotificationRoute, WatchLease } from './model.js';
|
|
2
2
|
import type { DeliveryEvidence, DirectedHead, ParticipantFact, SquareArtifactPort, SquareRead, WakeRequest } from './ports.js';
|
|
3
3
|
export type { DirectedNotificationRoute } from './model.js';
|
|
4
|
-
export type SayItem = StoredAct & {
|
|
5
|
-
kind: 'say';
|
|
6
|
-
};
|
|
7
|
-
export interface PlannedNotification {
|
|
8
|
-
item: SayItem;
|
|
9
|
-
recipient: string;
|
|
10
|
-
route: DirectedNotificationRoute;
|
|
11
|
-
}
|
|
12
4
|
/** Exact stored activity projection for transports that send an awareness reminder. */
|
|
13
5
|
export declare function wakePreview(facts: SquareRead, request: WakeRequest): string | undefined;
|
|
14
6
|
export interface RoutedNotification {
|
|
@@ -26,8 +18,6 @@ export declare function pendingHead(facts: SquareRead, name: string, index: numb
|
|
|
26
18
|
export declare function pendingRecipients(facts: SquareRead, index: number): DirectedHead[];
|
|
27
19
|
/** Still-pending directed heads for one recipient, oldest first. Bodies belong to presentation. */
|
|
28
20
|
export declare function pendingHeads(facts: SquareRead, name: string): DirectedHead[];
|
|
29
|
-
/** Complete recipient projection, assembled inside its one transaction. */
|
|
30
|
-
export declare function pendingNotifications(facts: SquareRead, name: string): PlannedNotification[];
|
|
31
21
|
export interface PendingScan {
|
|
32
22
|
readonly roster: readonly ParticipantFact[];
|
|
33
23
|
readonly throughIndex: number;
|
package/dist/delivery.js
CHANGED
|
@@ -14,8 +14,8 @@ export function wakePreview(facts, request) {
|
|
|
14
14
|
return undefined;
|
|
15
15
|
return renderSquareReminder([renderAttentionPreview({
|
|
16
16
|
squarePath: request.location, recipient: request.participant, actIndex: item.index,
|
|
17
|
-
actor: item.actor, route: head.route, body: item.body ?? '(structured activity)',
|
|
18
|
-
})]);
|
|
17
|
+
actor: item.actor, route: head.route, body: item.body ?? '(structured activity)', delivery: item.delivery,
|
|
18
|
+
})], [item.delivery]);
|
|
19
19
|
}
|
|
20
20
|
function custody(evidence) {
|
|
21
21
|
return evidence.map(row => ({ purpose: row.kind === 'wake' ? 'dispatch' : 'presentation', outcome: row.outcome, expiresAt: row.expiresAt }));
|
|
@@ -58,13 +58,6 @@ export function pendingHeads(facts, name) {
|
|
|
58
58
|
}
|
|
59
59
|
return heads;
|
|
60
60
|
}
|
|
61
|
-
/** Complete recipient projection, assembled inside its one transaction. */
|
|
62
|
-
export function pendingNotifications(facts, name) {
|
|
63
|
-
const heads = pendingHeads(facts, name), recipient = heads[0]?.recipient ?? name;
|
|
64
|
-
const routes = new Map(heads.map((head) => [head.index, head.route]));
|
|
65
|
-
return facts.activities(heads.map((head) => head.index)).flatMap((item) => item.kind === 'say'
|
|
66
|
-
? [{ item, recipient, route: routes.get(item.index) }] : []);
|
|
67
|
-
}
|
|
68
61
|
export function capturePendingScan(facts) {
|
|
69
62
|
return { roster: facts.roster().filter((participant) => participant.joined), throughIndex: facts.header().lastActIndex };
|
|
70
63
|
}
|