@astrosheep/square 0.3.11 → 0.3.13
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/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/codex-plugin/hooks/hooks.json +9 -0
- package/dist/activity-feed.d.ts +14 -0
- package/dist/activity-feed.js +5 -5
- package/dist/activity.d.ts +9 -0
- package/dist/activity.js +112 -96
- package/dist/artifact.d.ts +20 -0
- package/dist/artifact.js +317 -648
- package/dist/automatic-session.d.ts +5 -0
- package/dist/automatic-session.js +86 -0
- package/dist/boundary-presentation.d.ts +5 -0
- package/dist/boundary-presentation.js +6 -5
- package/dist/claude-hook.d.ts +9 -0
- package/dist/claude-hook.js +35 -3
- package/dist/cli/context.d.ts +35 -0
- package/dist/cli/context.js +26 -31
- package/dist/cli/harness-command.d.ts +28 -0
- package/dist/cli/harness-command.js +30 -15
- package/dist/cli/maintenance-commands.d.ts +7 -0
- package/dist/cli/maintenance-commands.js +12 -69
- package/dist/cli/meta-commands.d.ts +5 -0
- package/dist/cli/observation-commands.d.ts +20 -0
- package/dist/cli/observation-commands.js +159 -139
- package/dist/cli/program.d.ts +2 -0
- package/dist/cli/program.js +3 -12
- package/dist/cli/registry.d.ts +9 -0
- package/dist/cli/registry.js +2 -4
- package/dist/cli/square-commands.d.ts +34 -0
- package/dist/cli/square-commands.js +90 -84
- package/dist/cmd/notify-once.d.ts +2 -0
- package/dist/codex-hook.d.ts +10 -0
- package/dist/codex-hook.js +35 -3
- package/dist/decisions.d.ts +92 -0
- package/dist/decisions.js +93 -81
- package/dist/delivery-health.d.ts +21 -0
- package/dist/delivery-health.js +41 -34
- package/dist/delivery.d.ts +81 -0
- package/dist/delivery.js +71 -69
- package/dist/file-lock.d.ts +6 -0
- package/dist/harness-claude.d.ts +19 -0
- package/dist/harness-claude.js +8 -3
- package/dist/harness-codex.d.ts +29 -0
- package/dist/harness-codex.js +2 -2
- package/dist/harness-links.d.ts +17 -0
- package/dist/harness-links.js +2 -2
- package/dist/harness-pi.d.ts +11 -0
- package/dist/harness-pi.js +2 -2
- package/dist/harness-stage.d.ts +6 -0
- package/dist/harness.d.ts +20 -0
- package/dist/harness.js +5 -3
- package/dist/help.d.ts +6 -0
- package/dist/help.js +12 -13
- package/dist/identity.d.ts +13 -0
- package/dist/inbox.d.ts +2 -0
- package/dist/inbox.js +15 -19
- package/dist/index.d.ts +13 -0
- package/dist/index.js +31 -67
- package/dist/landing.d.ts +10 -0
- package/dist/landing.js +95 -0
- package/dist/list.d.ts +1 -0
- package/dist/list.js +39 -61
- package/dist/model.d.ts +147 -0
- package/dist/model.js +12 -8
- package/dist/notifications.d.ts +30 -0
- package/dist/notifications.js +83 -91
- package/dist/open-square.d.ts +11 -0
- package/dist/open-square.js +4 -0
- package/dist/participant-identity.d.ts +1 -0
- package/dist/participant-identity.js +3 -0
- package/dist/paseo-connection.d.ts +15 -0
- package/dist/paseo-delivery.d.ts +14 -0
- package/dist/paseo-state.d.ts +11 -0
- package/dist/paseo-timeline.d.ts +16 -0
- package/dist/presence.d.ts +3 -0
- package/dist/presence.js +42 -0
- package/dist/presentation.d.ts +91 -0
- package/dist/presentation.js +52 -63
- package/dist/presented.d.ts +11 -0
- package/dist/registry.d.ts +58 -0
- package/dist/registry.js +36 -3
- package/dist/routes.d.ts +42 -0
- package/dist/runtime.d.ts +45 -0
- package/dist/runtime.js +48 -55
- package/dist/search.d.ts +11 -0
- package/dist/square-core.d.ts +111 -0
- package/dist/square-core.js +63 -9
- package/dist/square-facade.d.ts +86 -0
- package/dist/square-facade.js +1 -0
- package/dist/square-file-adapter.d.ts +18 -0
- package/dist/square-file-adapter.js +70 -0
- package/dist/square-storage.d.ts +20 -0
- package/dist/square-storage.js +171 -0
- package/dist/square-wiring.d.ts +19 -0
- package/dist/square-wiring.js +70 -0
- package/dist/square.d.ts +2 -0
- package/dist/state-cell.d.ts +14 -0
- package/dist/state-cell.js +1 -0
- package/dist/stream.d.ts +3 -0
- package/dist/stream.js +19 -23
- package/dist/time.d.ts +6 -0
- package/dist/views.d.ts +95 -0
- package/dist/views.js +81 -0
- package/dist/wake-attempts.d.ts +39 -0
- package/dist/wake-attempts.js +8 -4
- package/dist/wake-evidence.d.ts +12 -0
- package/dist/wake-evidence.js +7 -10
- package/dist/wake-port.d.ts +20 -0
- package/dist/wake-sink.d.ts +12 -0
- package/dist/wakes.d.ts +40 -0
- package/dist/wakes.js +62 -0
- package/dist/watch.d.ts +2 -0
- package/dist/watch.js +89 -210
- package/extensions/square-opencode.js +27 -1
- package/extensions/square-pi.js +13 -0
- package/guides/participant.md +7 -174
- package/package.json +5 -4
- package/skills/brainstorm/SKILL.md +30 -30
- package/skills/square/.claude-plugin/plugin.json +1 -1
- package/skills/square/SKILL.md +56 -29
- package/skills/square/hooks/hooks.json +9 -0
- package/skills/square-feedback/SKILL.md +9 -9
- package/dist/compact.js +0 -26
- package/dist/doctor.js +0 -36
- package/dist/square-application.js +0 -262
- package/template.md +0 -4
- package/templates/architect.md +0 -4
- package/templates/brainstorm.md +0 -4
package/dist/watch.js
CHANGED
|
@@ -1,55 +1,31 @@
|
|
|
1
1
|
import { setTimeout as sleep } from 'node:timers/promises';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
2
|
+
import { isSquareError, } from './model.js';
|
|
3
|
+
import { SLEEP_MS, STALE_MS, WATCH_HEARTBEAT_MS, nowMs, } from './runtime.js';
|
|
4
|
+
import { openSquare } from './square-file-adapter.js';
|
|
5
|
+
import { closeOpenSquare } from './open-square.js';
|
|
6
|
+
import { openParticipant } from './square-wiring.js';
|
|
7
|
+
import { resolveParticipant, watchPresentation } from './views.js';
|
|
8
|
+
import { acquireWatchLease, ownsWatchLease, pulseWatchLease, releaseWatchLease } from './wakes.js';
|
|
7
9
|
import { renderWatchForceTakeover, renderWatchAlreadyActive, renderWatchOutput, renderWatchReplaced, renderWatchStatus, participantCommandPrefix, withPathOutput, } from './presentation.js';
|
|
8
|
-
import { ackPeerDelta, deliveryDelta, filteredPeerActivities, filteredRoomChanges, matchesFeedFilter, peerPublicActs, peerRoomChanges, } from './activity-feed.js';
|
|
9
|
-
import { coreParticipants, resolveKnownName } from './decisions.js';
|
|
10
10
|
import { hasAutomaticDeliveryIdentity, localParticipantOwner } from './registry.js';
|
|
11
|
-
|
|
12
|
-
return deliveryDelta(doc, name);
|
|
13
|
-
}
|
|
11
|
+
import { parseActivityId } from './square-core.js';
|
|
14
12
|
function watchStatusExitCode(status) {
|
|
15
13
|
return status === 'capped' ? 1 : 0;
|
|
16
14
|
}
|
|
17
15
|
function leaseId() {
|
|
18
16
|
return `watch_${process.pid}_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
19
17
|
}
|
|
20
|
-
function
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return Object.keys(filter).length === 0 ? undefined : filter;
|
|
26
|
-
}
|
|
27
|
-
function setLease(doc, name, id, at, opts, ownerId) {
|
|
28
|
-
const filter = leaseFilter(opts);
|
|
29
|
-
writeWatchLease(doc, name, {
|
|
30
|
-
leaseId: id,
|
|
31
|
-
...(ownerId === undefined ? {} : { ownerId }),
|
|
32
|
-
heartbeatAt: at,
|
|
33
|
-
expiresAt: at + WATCH_STALE_MS,
|
|
34
|
-
...(filter ? { filter } : {}),
|
|
18
|
+
function watchOutputResult(squarePath, presentation, name, caught, opts = {}) {
|
|
19
|
+
const delivered = caught.activities.flatMap((activity) => {
|
|
20
|
+
const index = parseActivityId(activity.id);
|
|
21
|
+
const stored = index === undefined ? undefined : presentation.activities.find((item) => item.index === index);
|
|
22
|
+
return stored === undefined ? [] : [stored];
|
|
35
23
|
});
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return freshWatchLease(doc, name, at)?.leaseId === id;
|
|
39
|
-
}
|
|
40
|
-
function consumeDelta(doc, name, delta, delivered, at) {
|
|
41
|
-
const consumed = ackPeerDelta(doc, name, delta);
|
|
42
|
-
const receipts = markDeliveredNotifications(doc, name, delivered, at);
|
|
43
|
-
return consumed || receipts;
|
|
44
|
-
}
|
|
45
|
-
function watchOutputResult(squarePath, doc, name, delta, opts = {}) {
|
|
46
|
-
const publicItems = peerPublicActs(delta, name).filter((item) => matchesFeedFilter(item, opts));
|
|
47
|
-
const roomChanges = filteredRoomChanges(delta, name, opts);
|
|
48
|
-
consumeDelta(doc, name, delta, publicItems, nowMs());
|
|
49
|
-
writeSquareDoc(squarePath, doc);
|
|
24
|
+
const publicItems = delivered.filter((item) => item.kind === 'say' || item.kind === 'done');
|
|
25
|
+
const roomChanges = delivered.filter((item) => item.kind === 'join' || item.kind === 'done' || item.kind === 'hold' || item.kind === 'resume');
|
|
50
26
|
return {
|
|
51
27
|
type: 'output',
|
|
52
|
-
stdout: renderWatchOutput(
|
|
28
|
+
stdout: renderWatchOutput([...presentation.activities], publicItems, roomChanges, {
|
|
53
29
|
...opts,
|
|
54
30
|
squarePath,
|
|
55
31
|
viewer: name,
|
|
@@ -58,30 +34,11 @@ function watchOutputResult(squarePath, doc, name, delta, opts = {}) {
|
|
|
58
34
|
...(opts.status ? { status: opts.status } : {}),
|
|
59
35
|
};
|
|
60
36
|
}
|
|
61
|
-
function
|
|
62
|
-
|
|
63
|
-
const doc = loadSquare(squarePath);
|
|
64
|
-
const now = nowMs();
|
|
65
|
-
return { participants: coreParticipants(doc, now), now };
|
|
66
|
-
}
|
|
67
|
-
catch {
|
|
68
|
-
return undefined;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
function loadHeaderCount(squarePath) {
|
|
72
|
-
try {
|
|
73
|
-
return inSquareCount(loadSquare(squarePath));
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
return undefined;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
function writeWatchOutput(squarePath, name, stdout, status, idleMs) {
|
|
80
|
-
const presence = loadPresence(squarePath);
|
|
81
|
-
const headerOpts = { participantCount: loadHeaderCount(squarePath) };
|
|
37
|
+
function writeWatchOutput(squarePath, name, presentation, stdout, status, idleMs) {
|
|
38
|
+
const headerOpts = { participantCount: presentation.participantCount };
|
|
82
39
|
const showCatchHint = !hasAutomaticDeliveryIdentity();
|
|
83
40
|
if (status) {
|
|
84
|
-
process.stdout.write(withPathOutput(squarePath, [renderWatchStatus({ status, squarePath, name, idleMs, presence, showCatchHint }), stdout.trimEnd()].filter(Boolean).join('\n\n').trimEnd(), headerOpts));
|
|
41
|
+
process.stdout.write(withPathOutput(squarePath, [renderWatchStatus({ status, squarePath, name, idleMs, presence: presentation.presence, showCatchHint }), stdout.trimEnd()].filter(Boolean).join('\n\n').trimEnd(), headerOpts));
|
|
85
42
|
return;
|
|
86
43
|
}
|
|
87
44
|
const fallback = showCatchHint
|
|
@@ -89,67 +46,51 @@ function writeWatchOutput(squarePath, name, stdout, status, idleMs) {
|
|
|
89
46
|
: '';
|
|
90
47
|
process.stdout.write(withPathOutput(squarePath, [stdout.trimEnd(), fallback].filter(Boolean).join('\n\n').trimEnd(), headerOpts));
|
|
91
48
|
}
|
|
92
|
-
function writeWatchTerminal(squarePath, name, status, idleMs) {
|
|
93
|
-
const presence = loadPresence(squarePath);
|
|
49
|
+
function writeWatchTerminal(squarePath, name, presentation, status, idleMs) {
|
|
94
50
|
process.stdout.write(withPathOutput(squarePath, renderWatchStatus({
|
|
95
51
|
status,
|
|
96
52
|
squarePath,
|
|
97
53
|
name,
|
|
98
54
|
...(idleMs === undefined ? {} : { idleMs }),
|
|
99
|
-
presence,
|
|
55
|
+
presence: presentation.presence,
|
|
100
56
|
showCatchHint: !hasAutomaticDeliveryIdentity(),
|
|
101
|
-
}), { participantCount:
|
|
57
|
+
}), { participantCount: presentation.participantCount }));
|
|
102
58
|
}
|
|
103
|
-
function writeWatchReplaced(squarePath, name) {
|
|
104
|
-
process.stdout.write(withPathOutput(squarePath, renderWatchReplaced({ squarePath, name }), { participantCount:
|
|
59
|
+
function writeWatchReplaced(squarePath, name, presentation) {
|
|
60
|
+
process.stdout.write(withPathOutput(squarePath, renderWatchReplaced({ squarePath, name }), { participantCount: presentation.participantCount }));
|
|
105
61
|
}
|
|
106
|
-
async function finishWatchResult(squarePath, name, result, leaseId, idleMs) {
|
|
62
|
+
async function finishWatchResult(square, squarePath, name, result, leaseId, idleMs) {
|
|
107
63
|
if (result.type === 'output') {
|
|
108
|
-
await endWatch(
|
|
109
|
-
writeWatchOutput(squarePath, name, result.stdout, result.status);
|
|
64
|
+
await endWatch(square, name, leaseId);
|
|
65
|
+
writeWatchOutput(squarePath, name, await watchPresentation(square, name), result.stdout, result.status);
|
|
110
66
|
process.exitCode = watchStatusExitCode(result.status);
|
|
111
67
|
return true;
|
|
112
68
|
}
|
|
113
69
|
if (result.type === 'terminal') {
|
|
114
|
-
await endWatch(
|
|
115
|
-
writeWatchTerminal(squarePath, name, result.status, idleMs);
|
|
70
|
+
await endWatch(square, name, leaseId);
|
|
71
|
+
writeWatchTerminal(squarePath, name, await watchPresentation(square, name), result.status, idleMs);
|
|
116
72
|
process.exitCode = watchStatusExitCode(result.status);
|
|
117
73
|
return true;
|
|
118
74
|
}
|
|
119
75
|
if (result.type === 'replaced') {
|
|
120
|
-
writeWatchReplaced(squarePath, name);
|
|
76
|
+
writeWatchReplaced(squarePath, name, await watchPresentation(square, name));
|
|
121
77
|
process.exitCode = 0;
|
|
122
78
|
return true;
|
|
123
79
|
}
|
|
124
80
|
return false;
|
|
125
81
|
}
|
|
126
|
-
async function beginWatch(squarePath, name, opts) {
|
|
127
|
-
const at = nowMs();
|
|
82
|
+
async function beginWatch(square, squarePath, name, opts) {
|
|
128
83
|
const id = leaseId();
|
|
129
84
|
const ownerId = localParticipantOwner(squarePath, name);
|
|
130
|
-
|
|
131
|
-
type: 'lease',
|
|
132
|
-
name,
|
|
133
|
-
leaseId: id,
|
|
134
|
-
...(ownerId === undefined ? {} : { ownerId }),
|
|
135
|
-
at,
|
|
136
|
-
expiresAt: at + WATCH_STALE_MS,
|
|
137
|
-
force: opts.replace,
|
|
138
|
-
filter: leaseFilter(opts),
|
|
139
|
-
});
|
|
140
|
-
if (committed.result.type === 'active')
|
|
141
|
-
return committed.result;
|
|
142
|
-
return { type: 'started', leaseId: id, replaced: committed.result.replaced, heartbeatAt: at };
|
|
85
|
+
return acquireWatchLease(square, name, id, opts, ownerId);
|
|
143
86
|
}
|
|
144
|
-
async function endWatch(
|
|
145
|
-
|
|
146
|
-
return;
|
|
147
|
-
await execute(squarePath, { type: 'release-lease', name, leaseId: id });
|
|
87
|
+
async function endWatch(square, name, id) {
|
|
88
|
+
await releaseWatchLease(square, name, id);
|
|
148
89
|
}
|
|
149
|
-
function installWatchInterruptHandler(squarePath, name, currentLeaseId) {
|
|
90
|
+
function installWatchInterruptHandler(square, squarePath, name, currentLeaseId) {
|
|
150
91
|
const onInterrupt = () => {
|
|
151
92
|
void (async () => {
|
|
152
|
-
await endWatch(
|
|
93
|
+
await endWatch(square, name, currentLeaseId());
|
|
153
94
|
process.stdout.write(withPathOutput(squarePath, '✕ catch stopped'));
|
|
154
95
|
process.exit(130);
|
|
155
96
|
})().catch((error) => {
|
|
@@ -162,157 +103,93 @@ function installWatchInterruptHandler(squarePath, name, currentLeaseId) {
|
|
|
162
103
|
process.off('SIGINT', onInterrupt);
|
|
163
104
|
};
|
|
164
105
|
}
|
|
165
|
-
function terminalStatus(doc, name) {
|
|
166
|
-
const count = countSays(doc.acts, name);
|
|
167
|
-
if (doc.hardCap !== null && count >= doc.hardCap)
|
|
168
|
-
return 'capped';
|
|
169
|
-
const done = doneNames(doc.acts);
|
|
170
|
-
done.delete(nameKey(name));
|
|
171
|
-
if (hasQuorum(doc, name, done))
|
|
172
|
-
return 'quorum';
|
|
173
|
-
return undefined;
|
|
174
|
-
}
|
|
175
106
|
async function cmdWatchNow(squarePath, name, opts) {
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const
|
|
184
|
-
const
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
if (hasDeliverable) {
|
|
196
|
-
const acked = ackPeerDelta(doc, name, delta);
|
|
197
|
-
if (acked || touched)
|
|
198
|
-
writeSquareDoc(squarePath, doc);
|
|
199
|
-
}
|
|
200
|
-
else if (touched) {
|
|
201
|
-
writeSquareDoc(squarePath, doc);
|
|
202
|
-
}
|
|
203
|
-
if (status)
|
|
204
|
-
return { type: 'terminal', status };
|
|
205
|
-
return { type: 'terminal', status: 'empty-now' };
|
|
206
|
-
});
|
|
207
|
-
await finishWatchResult(squarePath, name, result, undefined);
|
|
107
|
+
const square = await openSquare(squarePath, { clock: nowMs });
|
|
108
|
+
const facade = await openParticipant({ path: squarePath, clock: nowMs }, name);
|
|
109
|
+
try {
|
|
110
|
+
const caught = await facade.participant.catch({
|
|
111
|
+
...(opts.participants === undefined ? {} : { from: opts.participants }),
|
|
112
|
+
...(opts.mention === undefined ? {} : { mention: true }),
|
|
113
|
+
});
|
|
114
|
+
const presentation = await watchPresentation(square, name);
|
|
115
|
+
const status = presentation.terminalStatus;
|
|
116
|
+
const result = caught.activities.length > 0
|
|
117
|
+
? watchOutputResult(squarePath, presentation, name, caught, { mention: opts.mention, ...(status ? { status } : {}) })
|
|
118
|
+
: { type: 'terminal', status: status ?? 'empty-now' };
|
|
119
|
+
await finishWatchResult(square, squarePath, name, result, undefined);
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
await facade.close();
|
|
123
|
+
await closeOpenSquare(square);
|
|
124
|
+
}
|
|
208
125
|
}
|
|
209
126
|
export async function cmdWatch(squarePath, name, opts) {
|
|
210
|
-
let
|
|
127
|
+
let square;
|
|
211
128
|
try {
|
|
212
|
-
|
|
213
|
-
name =
|
|
129
|
+
square = await openSquare(squarePath, { clock: nowMs });
|
|
130
|
+
name = (await resolveParticipant(square, name)).name;
|
|
214
131
|
if (opts.mention !== undefined)
|
|
215
|
-
opts = { ...opts, mention:
|
|
132
|
+
opts = { ...opts, mention: (await resolveParticipant(square, opts.mention)).name };
|
|
216
133
|
if (opts.participants !== undefined && opts.participants.length > 0) {
|
|
217
|
-
opts = { ...opts, participants: opts.participants.map((
|
|
134
|
+
opts = { ...opts, participants: await Promise.all(opts.participants.map(async (participant) => (await resolveParticipant(square, participant)).name)) };
|
|
218
135
|
}
|
|
219
136
|
}
|
|
220
137
|
catch (err) {
|
|
221
|
-
if (err
|
|
138
|
+
if (isSquareError(err)) {
|
|
222
139
|
process.stderr.write(err.message + '\n');
|
|
223
140
|
process.exit(err.code === 'not_found' ? 1 : 2);
|
|
224
141
|
}
|
|
225
142
|
throw err;
|
|
226
143
|
}
|
|
227
144
|
if (opts.now) {
|
|
145
|
+
await closeOpenSquare(square);
|
|
228
146
|
await cmdWatchNow(squarePath, name, opts);
|
|
229
147
|
return;
|
|
230
148
|
}
|
|
231
|
-
const start = await beginWatch(squarePath, name, opts);
|
|
149
|
+
const start = await beginWatch(square, squarePath, name, opts);
|
|
232
150
|
if (start.type === 'active') {
|
|
233
|
-
|
|
151
|
+
const presentation = await watchPresentation(square, name);
|
|
152
|
+
process.stdout.write(withPathOutput(squarePath, renderWatchAlreadyActive({ squarePath, name }), { participantCount: presentation.participantCount }));
|
|
153
|
+
await closeOpenSquare(square);
|
|
234
154
|
process.exit(1);
|
|
235
155
|
}
|
|
236
156
|
let staleSince = nowMs();
|
|
237
157
|
let currentLeaseId = start.leaseId;
|
|
238
158
|
let nextHeartbeatAt = start.heartbeatAt + WATCH_HEARTBEAT_MS;
|
|
239
159
|
if (start.replaced) {
|
|
240
|
-
|
|
160
|
+
const presentation = await watchPresentation(square, name);
|
|
161
|
+
process.stdout.write(withPathOutput(squarePath, renderWatchForceTakeover({ squarePath, name }), { participantCount: presentation.participantCount }));
|
|
241
162
|
}
|
|
242
163
|
const idleMs = opts.idleMs ?? STALE_MS;
|
|
243
|
-
const removeInterruptHandler = installWatchInterruptHandler(squarePath, name, () => currentLeaseId);
|
|
164
|
+
const removeInterruptHandler = installWatchInterruptHandler(square, squarePath, name, () => currentLeaseId);
|
|
165
|
+
const facade = await openParticipant({ path: squarePath, clock: nowMs }, name);
|
|
244
166
|
try {
|
|
245
167
|
while (true) {
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
const delta = catchDelta(doc, name);
|
|
260
|
-
const peerPublic = peerPublicActs(delta, name);
|
|
261
|
-
const roomChanges = peerRoomChanges(delta, name);
|
|
262
|
-
const filteredActivities = filteredPeerActivities(delta, name, opts);
|
|
263
|
-
const matchingRoomChanges = filteredRoomChanges(delta, name, opts);
|
|
264
|
-
const hasDeliverable = peerPublic.length > 0 || roomChanges.length > 0;
|
|
265
|
-
const hasFilteredDeliverable = filteredActivities.length > 0 || matchingRoomChanges.length > 0;
|
|
266
|
-
const status = terminalStatus(doc, name);
|
|
267
|
-
if (currentHold(doc.acts).active) {
|
|
268
|
-
if (mutated)
|
|
269
|
-
writeSquareDoc(squarePath, doc);
|
|
270
|
-
return { type: 'held' };
|
|
271
|
-
}
|
|
272
|
-
if (hasDeliverable &&
|
|
273
|
-
hasFilteredDeliverable &&
|
|
274
|
-
(filteredActivities.length > 0 || matchingRoomChanges.length > 0 || status !== undefined)) {
|
|
275
|
-
return watchOutputResult(squarePath, doc, name, delta, {
|
|
276
|
-
participants: opts.participants,
|
|
277
|
-
mention: opts.mention,
|
|
278
|
-
...(status ? { status } : {}),
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
if (hasDeliverable && !hasFilteredDeliverable)
|
|
282
|
-
mutated = ackPeerDelta(doc, name, delta) || mutated;
|
|
283
|
-
if (status) {
|
|
284
|
-
if (mutated)
|
|
285
|
-
writeSquareDoc(squarePath, doc);
|
|
286
|
-
return { type: 'terminal', status };
|
|
168
|
+
const leaseState = await pulseWatchLease(square, name, currentLeaseId, opts, nowMs() >= nextHeartbeatAt);
|
|
169
|
+
if (leaseState.type === 'sleep' && leaseState.heartbeatAt !== undefined) {
|
|
170
|
+
nextHeartbeatAt = leaseState.heartbeatAt + WATCH_HEARTBEAT_MS;
|
|
171
|
+
}
|
|
172
|
+
let result = leaseState;
|
|
173
|
+
if (result.type === 'sleep') {
|
|
174
|
+
const caught = await facade.participant.catch({
|
|
175
|
+
...(opts.participants === undefined ? {} : { from: opts.participants }),
|
|
176
|
+
...(opts.mention === undefined ? {} : { mention: true }),
|
|
177
|
+
});
|
|
178
|
+
if (caught.activities.length > 0) {
|
|
179
|
+
result = watchOutputResult(squarePath, await watchPresentation(square, name), name, caught, { mention: opts.mention });
|
|
287
180
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
return { type: 'sleep' };
|
|
291
|
-
});
|
|
292
|
-
if (await finishWatchResult(squarePath, name, result, currentLeaseId)) {
|
|
181
|
+
}
|
|
182
|
+
if (await finishWatchResult(square, squarePath, name, result, currentLeaseId)) {
|
|
293
183
|
currentLeaseId = undefined;
|
|
294
184
|
return;
|
|
295
185
|
}
|
|
296
186
|
if (result.type === 'held')
|
|
297
187
|
staleSince = nowMs();
|
|
298
188
|
if (nowMs() - staleSince >= idleMs) {
|
|
299
|
-
const result = await
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const delta = catchDelta(doc, name);
|
|
304
|
-
const filteredActivities = filteredPeerActivities(delta, name, opts);
|
|
305
|
-
if (filteredActivities.length > 0) {
|
|
306
|
-
return watchOutputResult(squarePath, doc, name, delta, {
|
|
307
|
-
stalePartial: true,
|
|
308
|
-
participants: opts.participants,
|
|
309
|
-
mention: opts.mention,
|
|
310
|
-
idleMs,
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
|
-
return { type: 'terminal', status: 'stale' };
|
|
314
|
-
});
|
|
315
|
-
if (await finishWatchResult(squarePath, name, result, currentLeaseId, idleMs)) {
|
|
189
|
+
const result = !await ownsWatchLease(square, name, currentLeaseId)
|
|
190
|
+
? { type: 'replaced' }
|
|
191
|
+
: { type: 'terminal', status: 'stale' };
|
|
192
|
+
if (await finishWatchResult(square, squarePath, name, result, currentLeaseId, idleMs)) {
|
|
316
193
|
currentLeaseId = undefined;
|
|
317
194
|
return;
|
|
318
195
|
}
|
|
@@ -321,6 +198,8 @@ export async function cmdWatch(squarePath, name, opts) {
|
|
|
321
198
|
}
|
|
322
199
|
}
|
|
323
200
|
finally {
|
|
201
|
+
await facade.close();
|
|
202
|
+
await closeOpenSquare(square);
|
|
324
203
|
removeInterruptHandler();
|
|
325
204
|
}
|
|
326
205
|
}
|
|
@@ -1,14 +1,40 @@
|
|
|
1
1
|
import { presentPendingAtBoundary } from '../dist/boundary-presentation.js';
|
|
2
|
+
import { automaticSessionEnd, automaticSessionStart } from '../dist/automatic-session.js';
|
|
2
3
|
|
|
3
4
|
export default async function squareOpenCodePlugin() {
|
|
5
|
+
const joining = new Map();
|
|
4
6
|
return {
|
|
7
|
+
event: async ({ event }) => {
|
|
8
|
+
if (event.type === 'session.created' || event.type === 'session.updated') {
|
|
9
|
+
const sessionID = event.properties?.sessionID;
|
|
10
|
+
const cwd = event.properties?.info?.directory || process.cwd();
|
|
11
|
+
if (sessionID) {
|
|
12
|
+
try {
|
|
13
|
+
const context = await automaticSessionStart('opencode', sessionID, cwd);
|
|
14
|
+
if (context) joining.set(sessionID, context);
|
|
15
|
+
} catch { /* startup remains bounded */ }
|
|
16
|
+
}
|
|
17
|
+
} else if (event.type === 'session.deleted') {
|
|
18
|
+
const sessionID = event.properties?.sessionID;
|
|
19
|
+
const cwd = event.properties?.info?.directory || process.cwd();
|
|
20
|
+
if (sessionID) {
|
|
21
|
+
joining.delete(sessionID);
|
|
22
|
+
await automaticSessionEnd('opencode', sessionID, cwd);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
5
26
|
'shell.env': async (input, output) => {
|
|
6
27
|
if (input.sessionID) output.env.OPENCODE_SESSION_ID = input.sessionID;
|
|
7
28
|
},
|
|
8
29
|
|
|
9
30
|
'tool.execute.after': async (input, output) => {
|
|
10
31
|
try {
|
|
11
|
-
|
|
32
|
+
const joined = joining.get(input.sessionID);
|
|
33
|
+
if (joined) {
|
|
34
|
+
joining.delete(input.sessionID);
|
|
35
|
+
output.output = `${output.output}${output.output === '' ? '' : '\n\n'}${joined}`;
|
|
36
|
+
}
|
|
37
|
+
await presentPendingAtBoundary(
|
|
12
38
|
input.sessionID,
|
|
13
39
|
(context) => {
|
|
14
40
|
output.output = `${output.output}${output.output === '' ? '' : '\n\n'}${context}`;
|
package/extensions/square-pi.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { presentPendingAtBoundary, renderPendingAtBoundary } from '../dist/boundary-presentation.js';
|
|
2
|
+
import { automaticSessionEnd, automaticSessionStart } from '../dist/automatic-session.js';
|
|
2
3
|
|
|
3
4
|
export function pendingInbox(inbox) {
|
|
4
5
|
return inbox.filter((item) => item.notifications?.length > 0);
|
|
@@ -16,17 +17,26 @@ export function renderPiInbox(inbox) {
|
|
|
16
17
|
|
|
17
18
|
export default function squarePiExtension(pi) {
|
|
18
19
|
let sessionId;
|
|
20
|
+
let sessionCwd;
|
|
21
|
+
let joiningContext;
|
|
19
22
|
let previousSessionId;
|
|
20
23
|
const present = (deliver) => sessionId === undefined ? undefined : presentPendingAtBoundary(sessionId, deliver);
|
|
21
24
|
|
|
22
25
|
pi.on('session_start', async (_event, ctx) => {
|
|
23
26
|
sessionId = ctx.sessionManager.getSessionId();
|
|
27
|
+
sessionCwd = ctx.cwd || process.cwd();
|
|
24
28
|
previousSessionId = process.env.SQUARE_PI_SESSION_ID;
|
|
25
29
|
process.env.SQUARE_PI_SESSION_ID = sessionId;
|
|
30
|
+
try { joiningContext = await automaticSessionStart('pi', sessionId, sessionCwd); } catch { joiningContext = undefined; }
|
|
26
31
|
});
|
|
27
32
|
|
|
28
33
|
pi.on('before_agent_start', async () => {
|
|
29
34
|
try {
|
|
35
|
+
if (joiningContext) {
|
|
36
|
+
const context = joiningContext;
|
|
37
|
+
joiningContext = undefined;
|
|
38
|
+
return { message: { customType: 'square', content: context, display: true } };
|
|
39
|
+
}
|
|
30
40
|
return present((context) => ({ message: { customType: 'square', content: context, display: true } }));
|
|
31
41
|
} catch {
|
|
32
42
|
return undefined;
|
|
@@ -34,10 +44,13 @@ export default function squarePiExtension(pi) {
|
|
|
34
44
|
});
|
|
35
45
|
|
|
36
46
|
pi.on('session_shutdown', async () => {
|
|
47
|
+
if (sessionId && sessionCwd) await automaticSessionEnd('pi', sessionId, sessionCwd);
|
|
37
48
|
if (process.env.SQUARE_PI_SESSION_ID === sessionId) {
|
|
38
49
|
if (previousSessionId === undefined) delete process.env.SQUARE_PI_SESSION_ID;
|
|
39
50
|
else process.env.SQUARE_PI_SESSION_ID = previousSessionId;
|
|
40
51
|
}
|
|
41
52
|
sessionId = undefined;
|
|
53
|
+
sessionCwd = undefined;
|
|
54
|
+
joiningContext = undefined;
|
|
42
55
|
});
|
|
43
56
|
}
|