@north-light/crouter 0.3.192 → 0.3.194
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/api/client.d.ts +31 -10
- package/dist/api/client.js +83 -24
- package/dist/api/dto/bash-jobs.d.ts +24 -0
- package/dist/api/dto/bash-jobs.js +9 -0
- package/dist/api/dto/human.d.ts +3 -4
- package/dist/api/dto/inbox.d.ts +106 -65
- package/dist/api/dto/inbox.js +2 -8
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +1 -0
- package/dist/api/routes.d.ts +7 -0
- package/dist/api/routes.js +7 -0
- package/dist/builtin-memory/02-lifecycle/01-resident.md +1 -1
- package/dist/builtin-memory/internal/examples/imessage-assistant.md +1 -1
- package/dist/builtin-memory/internal/nodes-and-canvas.md +2 -2
- package/dist/builtin-memory/internal/storage-tiers.md +1 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/serial/provider-rotation.test.ts +101 -3
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +6 -2
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +68 -14
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +87 -13
- package/dist/clients/attach/__tests__/chat-view-snapshot-ordering.test.js +2 -2
- package/dist/clients/attach/__tests__/group-activity.test.js +2 -2
- package/dist/clients/attach/chrome/canvas-panels.d.ts +1 -1
- package/dist/clients/attach/chrome/canvas-panels.js +1 -1
- package/dist/clients/attach/input/overlay-owner.js +1 -1
- package/dist/clients/attach/overlays/auth.js +34 -8
- package/dist/clients/attach/overlays/pickers.d.ts +5 -8
- package/dist/clients/attach/overlays/pickers.js +37 -43
- package/dist/clients/attach/viewer.js +643 -620
- package/dist/clients/inbox/__tests__/serial/inbox-controller.test.js +38 -128
- package/dist/clients/inbox/__tests__/serial/mount-panel.test.js +72 -148
- package/dist/clients/inbox/controller.d.ts +2 -3
- package/dist/clients/inbox/controller.js +41 -42
- package/dist/clients/inbox/page-adapter.d.ts +33 -0
- package/dist/clients/inbox/page-adapter.js +54 -0
- package/dist/clients/inbox/resolve.d.ts +2 -2
- package/dist/clients/inbox/resolve.js +1 -1
- package/dist/clients/inbox/review-visibility.d.ts +1 -1
- package/dist/clients/inbox/review-visibility.js +1 -1
- package/dist/clients/inbox/tui/ansi.d.ts +1 -1
- package/dist/clients/inbox/tui/ansi.js +2 -2
- package/dist/clients/inbox/tui/input.d.ts +1 -3
- package/dist/clients/inbox/tui/input.js +242 -553
- package/dist/clients/inbox/tui/panel.d.ts +3 -1
- package/dist/clients/inbox/tui/panel.js +59 -187
- package/dist/clients/inbox/tui/render.d.ts +0 -25
- package/dist/clients/inbox/tui/render.js +165 -416
- package/dist/clients/inbox/tui/slots.d.ts +32 -0
- package/dist/clients/inbox/tui/slots.js +156 -0
- package/dist/clients/inbox/tui/types.d.ts +25 -13
- package/dist/clients/inbox/tui.js +5 -3
- package/dist/commands/attention.js +2 -2
- package/dist/commands/human/doc.js +66 -20
- package/dist/commands/human/prompts.d.ts +3 -3
- package/dist/commands/human/prompts.js +158 -112
- package/dist/commands/human/queue.d.ts +0 -1
- package/dist/commands/human/queue.js +34 -94
- package/dist/commands/human/shared.d.ts +10 -4
- package/dist/commands/human/shared.js +62 -13
- package/dist/commands/human.js +8 -4
- package/dist/commands/memory/read.js +13 -5
- package/dist/commands/node/bash.js +18 -30
- package/dist/commands/node/lifecycle.js +2 -2
- package/dist/commands/surface/node/focus.js +1 -1
- package/dist/core/__tests__/human-deliver.test.js +40 -46
- package/dist/core/__tests__/seam/broker-attach-stream.test.js +7 -1
- package/dist/core/__tests__/seam/dormancy-release.test.js +64 -10
- package/dist/core/__tests__/serial/human-deliver-e2e.test.js +40 -31
- package/dist/core/__tests__/serial/spawn-root.test.js +3 -1
- package/dist/core/__tests__/spawn-worktree-id-conflict.test.js +3 -1
- package/dist/core/auth-interaction.d.ts +2 -2
- package/dist/core/bash-jobs.d.ts +43 -0
- package/dist/core/bash-jobs.js +96 -2
- package/dist/core/canvas/__tests__/attention.test.js +16 -10
- package/dist/core/canvas/__tests__/render-remote.test.js +9 -3
- package/dist/core/canvas/attention.js +1 -1
- package/dist/core/canvas/browse/app.js +1 -1
- package/dist/core/canvas/nav-model.d.ts +1 -1
- package/dist/core/canvas/nav-model.js +1 -1
- package/dist/core/canvas/remote-canvas-source.js +1 -1
- package/dist/core/canvas/types.d.ts +2 -1
- package/dist/core/config.js +4 -1
- package/dist/core/human/__tests__/page-render.test.d.ts +1 -0
- package/dist/core/human/__tests__/page-render.test.js +49 -0
- package/dist/core/human/__tests__/page-tickets.test.d.ts +1 -0
- package/dist/core/human/__tests__/page-tickets.test.js +112 -0
- package/dist/core/human/__tests__/page.test.d.ts +1 -0
- package/dist/core/human/__tests__/page.test.js +139 -0
- package/dist/core/human/__tests__/serial/inbox-core.test.js +42 -32
- package/dist/core/human/claim.js +2 -2
- package/dist/core/human/convention.d.ts +4 -4
- package/dist/core/human/convention.js +6 -13
- package/dist/core/human/markdown-html.d.ts +8 -0
- package/dist/core/human/markdown-html.js +581 -0
- package/dist/core/human/page-catalog.d.ts +6 -0
- package/dist/core/human/page-catalog.js +43 -0
- package/dist/core/human/page-render.d.ts +3 -0
- package/dist/core/human/page-render.js +131 -0
- package/dist/core/human/page-schema.d.ts +312 -0
- package/dist/core/human/page-schema.js +291 -0
- package/dist/core/human/page-synth.d.ts +9 -0
- package/dist/core/human/page-synth.js +17 -0
- package/dist/core/human/page.d.ts +22 -0
- package/dist/core/human/page.js +427 -0
- package/dist/core/human/review-schema.d.ts +19 -0
- package/dist/core/human/review-schema.js +34 -0
- package/dist/core/human/scan.d.ts +12 -3
- package/dist/core/human/scan.js +69 -26
- package/dist/core/human/summary.d.ts +5 -6
- package/dist/core/human/summary.js +54 -37
- package/dist/core/human/tickets.d.ts +61 -11
- package/dist/core/human/tickets.js +100 -57
- package/dist/core/human/types.d.ts +21 -66
- package/dist/core/human/types.js +1 -5
- package/dist/core/inspector/model.d.ts +5 -0
- package/dist/core/inspector/model.js +5 -0
- package/dist/core/inspector/text.js +2 -2
- package/dist/core/inspector/tui.js +10 -3
- package/dist/core/keybindings/catalog.d.ts +1 -1
- package/dist/core/keybindings/catalog.js +4 -2
- package/dist/core/preview-registry.js +0 -1
- package/dist/core/review/ticket-filter.d.ts +1 -1
- package/dist/core/review/ticket-filter.js +2 -8
- package/dist/core/runtime/broker/read-ops.js +3 -0
- package/dist/core/runtime/broker.js +19 -10
- package/dist/core/runtime/package-health.js +1 -1
- package/dist/core/runtime/persona.js +2 -1
- package/dist/core/runtime/tmux-bindings.js +8 -1
- package/dist/core/termrender/termrender.d.ts +1 -1
- package/dist/core/termrender/termrender.js +2 -2
- package/dist/core/termrender/version.d.ts +1 -1
- package/dist/core/termrender/version.js +1 -1
- package/dist/core/tui/page-host.d.ts +1 -1
- package/dist/core/tui/page-host.js +2 -2
- package/dist/core/user-settings.d.ts +19 -0
- package/dist/core/user-settings.js +22 -0
- package/dist/daemon/api/handlers/bash-jobs.d.ts +2 -0
- package/dist/daemon/api/handlers/bash-jobs.js +90 -0
- package/dist/daemon/api/handlers/human.js +16 -14
- package/dist/daemon/api/handlers/inbox.js +311 -227
- package/dist/daemon/api/router.d.ts +5 -1
- package/dist/daemon/api/router.js +5 -0
- package/dist/daemon/api/server.js +2 -0
- package/dist/daemon/cron-run.js +23 -10
- package/dist/daemon/human/finish.d.ts +8 -3
- package/dist/daemon/human/finish.js +35 -15
- package/dist/daemon/reconcilers/broker-supervision.js +7 -1
- package/dist/daemon/reconcilers/dormant-inbox.js +8 -0
- package/dist/daemon/reconcilers/live-obligation.d.ts +2 -0
- package/dist/daemon/reconcilers/live-obligation.js +11 -0
- package/dist/pages/bundle.css +1 -0
- package/dist/pages/bundle.js +1347 -0
- package/dist/pages/comments.d.ts +34 -0
- package/dist/pages/comments.js +72 -0
- package/dist/pages/elements/cards.d.ts +17 -0
- package/dist/pages/elements/cards.js +785 -0
- package/dist/pages/elements/chart.d.ts +1 -0
- package/dist/pages/elements/chart.js +648 -0
- package/dist/pages/elements/options.d.ts +21 -0
- package/dist/pages/elements/options.js +604 -0
- package/dist/pages/elements/pages.d.ts +24 -0
- package/dist/pages/elements/pages.js +355 -0
- package/dist/pages/elements/slot.d.ts +21 -0
- package/dist/pages/elements/slot.js +105 -0
- package/dist/pages/elements/table.d.ts +19 -0
- package/dist/pages/elements/table.js +906 -0
- package/dist/pages/elements/text.d.ts +25 -0
- package/dist/pages/elements/text.js +951 -0
- package/dist/pages/entry.d.ts +8 -0
- package/dist/pages/entry.js +8 -0
- package/dist/pages/host.d.ts +132 -0
- package/dist/pages/host.js +149 -0
- package/dist/pages/register.d.ts +2 -0
- package/dist/pages/register.js +3 -0
- package/dist/pages/slot-config.d.ts +36 -0
- package/dist/pages/slot-config.js +50 -0
- package/dist/pages/types.d.ts +98 -0
- package/dist/pages/types.js +23 -0
- package/dist/pi-extensions/canvas-bash-valve.d.ts +4 -2
- package/dist/pi-extensions/canvas-bash-valve.js +25 -7
- package/dist/types.d.ts +4 -0
- package/dist/types.js +2 -0
- package/package.json +8 -8
- package/runtime.lock.json +156 -101
- package/scripts/install-runtime.mjs +172 -5
- package/dist/clients/inbox/deck-adapter.d.ts +0 -29
- package/dist/clients/inbox/deck-adapter.js +0 -63
- package/dist/core/human/deck-factories.d.ts +0 -8
- package/dist/core/human/deck-factories.js +0 -15
- package/dist/core/human/deck-schema.d.ts +0 -70
- package/dist/core/human/deck-schema.js +0 -92
|
@@ -18,10 +18,14 @@ export interface RequestContext {
|
|
|
18
18
|
res: ServerResponse;
|
|
19
19
|
}
|
|
20
20
|
/** What a handler returns. `body` is JSON-serialized; omit it (or use 204) for
|
|
21
|
-
* an empty response. `headers` are merged over the default `content-type`.
|
|
21
|
+
* an empty response. `headers` are merged over the default `content-type`.
|
|
22
|
+
* `rawBody` is the escape hatch for the routes that serve bytes rather than
|
|
23
|
+
* JSON (the page runtime bundle): it is written verbatim and the handler owns
|
|
24
|
+
* the `content-type`. Setting both is a handler bug — `rawBody` wins. */
|
|
22
25
|
export interface HandlerResult {
|
|
23
26
|
status: number;
|
|
24
27
|
body?: unknown;
|
|
28
|
+
rawBody?: string | Buffer;
|
|
25
29
|
headers?: Record<string, string>;
|
|
26
30
|
}
|
|
27
31
|
/** A route handler — sync or async. A throw is caught and mapped by the router
|
|
@@ -164,6 +164,11 @@ function writeResult(res, result) {
|
|
|
164
164
|
if (res.headersSent)
|
|
165
165
|
return;
|
|
166
166
|
const headers = { ...(result.headers ?? {}) };
|
|
167
|
+
if (result.rawBody !== undefined) {
|
|
168
|
+
res.writeHead(result.status, headers);
|
|
169
|
+
res.end(result.rawBody);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
167
172
|
if (result.status === 204 || result.body === undefined) {
|
|
168
173
|
res.writeHead(result.status, headers);
|
|
169
174
|
res.end();
|
|
@@ -19,6 +19,7 @@ import { emitEvent } from '../../core/events/emit.js';
|
|
|
19
19
|
import { Router } from './router.js';
|
|
20
20
|
import { handleAttachUpgrade } from './bridge.js';
|
|
21
21
|
import { attachRoutes } from './handlers/attach.js';
|
|
22
|
+
import { bashJobRoutes } from './handlers/bash-jobs.js';
|
|
22
23
|
import { brokerOperationRoutes } from './handlers/broker-ops.js';
|
|
23
24
|
import { canvasRoutes } from './handlers/canvas.js';
|
|
24
25
|
import { daemonRoutes } from './handlers/daemon.js';
|
|
@@ -46,6 +47,7 @@ function buildRouter() {
|
|
|
46
47
|
.registerAll(healthRoutes)
|
|
47
48
|
.registerAll(daemonRoutes)
|
|
48
49
|
.registerAll(nodeRoutes)
|
|
50
|
+
.registerAll(bashJobRoutes)
|
|
49
51
|
.registerAll(brokerOperationRoutes)
|
|
50
52
|
.registerAll(worktreeRoutes)
|
|
51
53
|
.registerAll(messageRoutes)
|
package/dist/daemon/cron-run.js
CHANGED
|
@@ -32,9 +32,11 @@ import { spawn } from 'node:child_process';
|
|
|
32
32
|
import { createHash, randomBytes, randomUUID } from 'node:crypto';
|
|
33
33
|
import { join } from 'node:path';
|
|
34
34
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
35
|
-
import {
|
|
35
|
+
import { rmSync } from 'node:fs';
|
|
36
|
+
import { synthesizedPageDocument, writeSynthesizedPage } from '../core/human/page-synth.js';
|
|
36
37
|
import { ticketDir } from '../core/human/root.js';
|
|
37
|
-
import {
|
|
38
|
+
import { preparePage, publishPage } from '../core/human/tickets.js';
|
|
39
|
+
import { readConfig } from '../core/config.js';
|
|
38
40
|
import { dueClockCrons, dueOverlapCrons, expiredIdleCrons, runningCrons, acquireCronRunLease, releaseCronRunLease, recordCronRun, listCronRuns, advanceCron, consumeCron, cancelCron, killLeasedCronRun, verifyLeasedCronRun, getCron, setCronState, setCronLastOutputHash, } from '../core/canvas/crons.js';
|
|
39
41
|
import { getNode } from '../core/canvas/canvas.js';
|
|
40
42
|
import { fullName } from '../core/canvas/labels.js';
|
|
@@ -211,19 +213,30 @@ async function deliverSpawnSink(c, kind, stdout) {
|
|
|
211
213
|
});
|
|
212
214
|
return res.node.node_id;
|
|
213
215
|
}
|
|
214
|
-
/** Deliver stdout through crouter as a durable notify
|
|
216
|
+
/** Deliver stdout through crouter as a durable notify page (fire-and-forget
|
|
215
217
|
* inbox state — jobs whose output is a person's call). Mirrors `crtr human
|
|
216
|
-
* notify` minus the CLI-env source stamp: the
|
|
218
|
+
* notify` minus the CLI-env source stamp: the page names the cron instead. */
|
|
217
219
|
function deliverHumanSink(c, stdout) {
|
|
220
|
+
const synthesized = writeSynthesizedPage(synthesizedPageDocument(`⏰ cron ${c.name}`, `Scheduled cron "${c.name}" delivered output for your attention.`, stdout, false));
|
|
221
|
+
// Cron stdout rides into the page verbatim, so it can carry invalid page
|
|
222
|
+
// markup. Validate before minting the ticket, so a rejected page leaves no
|
|
223
|
+
// half-built `nfy-*` directory behind.
|
|
224
|
+
let prepared;
|
|
225
|
+
try {
|
|
226
|
+
prepared = preparePage({
|
|
227
|
+
sourceFile: synthesized.path,
|
|
228
|
+
dialect: 'md',
|
|
229
|
+
source: { sessionName: `cron ${c.name} (${c.cron_id})` },
|
|
230
|
+
productKinds: readConfig('user').page_components,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
finally {
|
|
234
|
+
rmSync(synthesized.dir, { recursive: true, force: true });
|
|
235
|
+
}
|
|
218
236
|
const id = `nfy-${randomBytes(4).toString('hex')}`;
|
|
219
237
|
const rc = { mode: 'notify' };
|
|
220
238
|
atomicWriteJson(join(ticketDir(id), 'run.json'), rc);
|
|
221
|
-
|
|
222
|
-
subtitle: `Scheduled cron "${c.name}" delivered output for your attention.`,
|
|
223
|
-
body: stdout,
|
|
224
|
-
});
|
|
225
|
-
deck = { ...deck, source: { sessionName: `cron ${c.name} (${c.cron_id})` } };
|
|
226
|
-
submitDeck({ dir: ticketDir(id), deck });
|
|
239
|
+
publishPage(ticketDir(id), prepared);
|
|
227
240
|
}
|
|
228
241
|
/** Route stdout to the cron's armed sink. Returns the delivered string on
|
|
229
242
|
* success; throws on an undeliverable sink (the caller's failure path). */
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
/**
|
|
3
|
-
|
|
1
|
+
import type { TicketResult } from '../../core/human/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* The one completion path for a page ticket: claim takeover, exclusive result
|
|
4
|
+
* publication, then the asking node's wake. Every addressing surface — the
|
|
5
|
+
* node-addressed resolve route and the ticket-id-addressed inbox respond route —
|
|
6
|
+
* finishes a page here, so an answer always reaches the node that asked.
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolvePageTicket(nodeId: string, responses: unknown, productKinds?: string[]): Promise<TicketResult>;
|
|
4
9
|
/** Cancel a ticket with the store's first-writer-wins publication. */
|
|
5
10
|
export declare function cancelHumanTicket(nodeId: string, opts: {
|
|
6
11
|
reason?: string;
|
|
@@ -2,19 +2,35 @@ import { join } from 'node:path';
|
|
|
2
2
|
import { ApiError } from '../../api/index.js';
|
|
3
3
|
import { getNode, subscribersOf } from '../../core/canvas/canvas.js';
|
|
4
4
|
import { ticketDir } from '../../core/human/root.js';
|
|
5
|
-
import { cancelTicket, projectReviewOutput, readTicketResult,
|
|
5
|
+
import { cancelTicket, projectReviewOutput, readTicketResult, takeoverAndCompletePage, } from '../../core/human/tickets.js';
|
|
6
6
|
import { responsePath } from '../../core/human/convention.js';
|
|
7
7
|
import { readJsonOrNull } from '../../core/fs-utils.js';
|
|
8
8
|
import { appendInbox } from '../../core/feed/inbox.js';
|
|
9
9
|
import { FinalDeliveryError, pushFinal } from '../../core/feed/feed.js';
|
|
10
10
|
import { transition } from '../../core/runtime/lifecycle.js';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
/**
|
|
12
|
+
* The answer a page's asking node wakes with. Slot responses are small and
|
|
13
|
+
* typed, so they are inlined as compact JSON rather than left behind the path:
|
|
14
|
+
* the node reads its answers out of its own report. An acknowledgement carries
|
|
15
|
+
* no responses at all and reads as one line saying the human saw the page.
|
|
16
|
+
* Interim rendering — the designed answer view is phase 5.
|
|
17
|
+
*/
|
|
18
|
+
function renderPageAnswer(result, resultPath) {
|
|
19
|
+
const entries = Object.entries(result.responses);
|
|
20
|
+
const lines = [];
|
|
21
|
+
if (entries.length === 0) {
|
|
22
|
+
lines.push('The human read the page and dismissed it. No slot responses came back, so the acknowledgement is the whole answer.');
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
lines.push(`The human answered ${entries.length} slot${entries.length === 1 ? '' : 's'}:`, '');
|
|
26
|
+
for (const [slotId, response] of entries)
|
|
27
|
+
lines.push(`${slotId}: ${JSON.stringify(response)}`);
|
|
28
|
+
const summary = result.summary.trim();
|
|
29
|
+
if (summary !== '')
|
|
30
|
+
lines.push('', summary);
|
|
31
|
+
}
|
|
32
|
+
lines.push('', `Response metadata (slot ids, typed responses, timestamp): ${resultPath}`);
|
|
33
|
+
return lines.join('\n');
|
|
18
34
|
}
|
|
19
35
|
function deliveryFailed(error) {
|
|
20
36
|
// A canonical final is committed inside its own write boundary and can never
|
|
@@ -35,9 +51,14 @@ async function deliverOrConflict(nodeId) {
|
|
|
35
51
|
}
|
|
36
52
|
throw new ApiError(409, 'ticket_already_resolved', 'ticket already resolved');
|
|
37
53
|
}
|
|
38
|
-
/**
|
|
39
|
-
|
|
40
|
-
|
|
54
|
+
/**
|
|
55
|
+
* The one completion path for a page ticket: claim takeover, exclusive result
|
|
56
|
+
* publication, then the asking node's wake. Every addressing surface — the
|
|
57
|
+
* node-addressed resolve route and the ticket-id-addressed inbox respond route —
|
|
58
|
+
* finishes a page here, so an answer always reaches the node that asked.
|
|
59
|
+
*/
|
|
60
|
+
export async function resolvePageTicket(nodeId, responses, productKinds = []) {
|
|
61
|
+
const completed = takeoverAndCompletePage(ticketDir(nodeId), responses, { host: 'crtrd', pid: process.pid }, { productKinds });
|
|
41
62
|
if (!completed.won)
|
|
42
63
|
return deliverOrConflict(nodeId);
|
|
43
64
|
try {
|
|
@@ -108,9 +129,8 @@ export async function deliverTerminalResult(nodeId) {
|
|
|
108
129
|
transition(nodeId, 'finish');
|
|
109
130
|
return true;
|
|
110
131
|
}
|
|
111
|
-
if (result.kind
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
await pushFinal(nodeId, renderDeckAnswer(envelope));
|
|
132
|
+
if (result.kind === 'page') {
|
|
133
|
+
await pushFinal(nodeId, renderPageAnswer(result, responsePath(dir)));
|
|
134
|
+
}
|
|
115
135
|
return true;
|
|
116
136
|
}
|
|
@@ -20,6 +20,7 @@ import { operationIdContext } from '../../core/events/operation-id.js';
|
|
|
20
20
|
import { readCursor, readInboxSince } from '../../core/feed/inbox.js';
|
|
21
21
|
import { activeBackgroundBashJobs } from '../../core/bash-jobs.js';
|
|
22
22
|
import { focusOf } from '../../core/runtime/placement.js';
|
|
23
|
+
import { hasLiveObligation } from './live-obligation.js';
|
|
23
24
|
const execFileAsync = promisify(execFile);
|
|
24
25
|
function configuredPositiveInteger(name, fallback) {
|
|
25
26
|
const raw = process.env[name];
|
|
@@ -205,7 +206,12 @@ export class BrokerSupervisionReconciler {
|
|
|
205
206
|
if (now - since < unattendedParkMsForDaemon())
|
|
206
207
|
return;
|
|
207
208
|
this.unattendedSince.delete(id);
|
|
208
|
-
|
|
209
|
+
const liveObligation = hasLiveObligation(id);
|
|
210
|
+
// A resident with nothing left to wake it has completed its conversation:
|
|
211
|
+
// terminalize it so history retention can eventually prune it. A resident
|
|
212
|
+
// still awaiting a child/controller/human/deadline/output stays wakeable, as does
|
|
213
|
+
// every terminal node (whose own stop guard owns semantic completion).
|
|
214
|
+
transition(id, row.lifecycle === 'resident' && !liveObligation ? 'finish' : 'release');
|
|
209
215
|
headlessBrokerHost.teardown(id);
|
|
210
216
|
}
|
|
211
217
|
async warnOverCapInTmux(count, warning, max) {
|
|
@@ -7,6 +7,7 @@ import { appendControllerDeathNotice, readCursor, readInboxSince, } from '../../
|
|
|
7
7
|
import { fanDoctrineWake } from '../../core/runtime/close.js';
|
|
8
8
|
import { transition } from '../../core/runtime/lifecycle.js';
|
|
9
9
|
import { reviveNode } from '../../core/runtime/revive.js';
|
|
10
|
+
import { hasLiveObligation } from './live-obligation.js';
|
|
10
11
|
// Idle-release/inbox retry cap (2026-07-10 diagnosis, round 2): the SAME
|
|
11
12
|
// livelock shape as the stranded-relaunch case, but on the separate pass that
|
|
12
13
|
// resumes a dormant idle-released node when its inbox gains an unseen entry.
|
|
@@ -78,6 +79,13 @@ export class DormantInboxReconciler {
|
|
|
78
79
|
// set neither revives the node nor starts a stuck-relaunch episode.
|
|
79
80
|
if (!entries.some((entry) => entry.tier !== 'deferred')) {
|
|
80
81
|
this.relaunchState.delete(id);
|
|
82
|
+
const meta = getNode(id);
|
|
83
|
+
// An idle resident with no wake-worthy inbox entry still needs
|
|
84
|
+
// lifecycle settlement. Once nothing live remains, this is completed
|
|
85
|
+
// history rather than a permanently protected idle row.
|
|
86
|
+
if (meta?.lifecycle === 'resident' && !hasLiveObligation(id)) {
|
|
87
|
+
transition(id, 'finish');
|
|
88
|
+
}
|
|
81
89
|
return;
|
|
82
90
|
}
|
|
83
91
|
const prior = this.relaunchState.get(id);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { hasActiveLiveSubscription, hasLiveMessageWait, hasPendingCancelOnWakeCron, } from '../../core/canvas/index.js';
|
|
2
|
+
import { countTickets } from '../../core/canvas/attention.js';
|
|
3
|
+
import { readOutputRequest } from '../../core/runtime/structured-output.js';
|
|
4
|
+
/** Durable work that requires a resident to remain automatically wakeable. */
|
|
5
|
+
export function hasLiveObligation(nodeId) {
|
|
6
|
+
return hasActiveLiveSubscription(nodeId)
|
|
7
|
+
|| hasLiveMessageWait(nodeId)
|
|
8
|
+
|| hasPendingCancelOnWakeCron(nodeId)
|
|
9
|
+
|| countTickets(nodeId) > 0
|
|
10
|
+
|| readOutputRequest(nodeId).state !== 'absent';
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:where(:root,:host){color-scheme:light dark;--crtr-color-background: #ffffff;--crtr-color-surface: #f7f7f8;--crtr-color-surface-hover: #eeeeef;--crtr-color-text: #1c1c1f;--crtr-color-text-muted: #68686f;--crtr-color-border: #d9d9de;--crtr-color-accent: #5753c9;--crtr-color-accent-contrast: #ffffff;--crtr-color-danger: #c73535;--crtr-color-focus: #706bea;--crtr-chart-1: #5753c9;--crtr-chart-2: #2c8c75;--crtr-chart-3: #cf7c2a;--crtr-chart-4: #b84c84;--crtr-chart-5: #477eb8;--crtr-font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--crtr-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;--crtr-font-size: .875rem;--crtr-line-height: 1.5;--crtr-radius-small: .375rem;--crtr-radius-medium: .625rem;--crtr-space-1: .25rem;--crtr-space-2: .5rem;--crtr-space-3: .75rem;--crtr-space-4: 1rem;--crtr-shadow: 0 1px 2px rgb(0 0 0 / .08)}:where(crtr-options,crtr-text,crtr-table,crtr-cards,crtr-chart,crtr-pages,crtr-page,crtr-slot){color:var(--crtr-color-text);font-family:var(--crtr-font-family);font-size:var(--crtr-font-size);line-height:var(--crtr-line-height)}@media(prefers-color-scheme:dark){:where(:root,:host){--crtr-color-background: #161618;--crtr-color-surface: #222225;--crtr-color-surface-hover: #2d2d31;--crtr-color-text: #f2f2f3;--crtr-color-text-muted: #aaaab1;--crtr-color-border: #414147;--crtr-color-accent: #9692ff;--crtr-color-accent-contrast: #18181b;--crtr-color-danger: #f06a6a;--crtr-color-focus: #aaa7ff}}
|