@north-light/crouter 0.3.198 → 0.3.200
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 +18 -18
- package/dist/api/client.js +29 -37
- package/dist/api/dto/broker-ops.d.ts +3 -1
- package/dist/api/dto/broker.d.ts +13 -2
- package/dist/api/dto/broker.js +4 -4
- package/dist/api/dto/human.d.ts +6 -5
- package/dist/api/dto/inbox.d.ts +81 -34
- package/dist/api/dto/nodes.d.ts +8 -2
- package/dist/api/dto/profiles.d.ts +23 -0
- package/dist/api/dto/profiles.js +2 -2
- package/dist/api/dto/worktree.d.ts +3 -5
- package/dist/api/routes.d.ts +3 -5
- package/dist/api/routes.js +4 -6
- package/dist/builtin-memory/00-runtime-base.md +6 -6
- package/dist/builtin-memory/01-spine/01-no-manager.md +1 -1
- package/dist/builtin-memory/02-lifecycle/00-terminal.md +3 -3
- package/dist/builtin-memory/04-orchestration-kernel.md +6 -6
- package/dist/builtin-memory/05-kinds/design/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/plan/reviewers/security.md +2 -2
- package/dist/builtin-memory/05-kinds/review/00-base.md +2 -2
- package/dist/builtin-memory/05-kinds/review/01-orchestrator.md +1 -1
- package/dist/builtin-memory/05-kinds/spec/01-orchestrator.md +1 -1
- package/dist/builtin-memory/insights/capture.md +2 -2
- package/dist/builtin-memory/insights/init.md +3 -3
- package/dist/builtin-memory/internal/INDEX.md +1 -1
- package/dist/builtin-memory/internal/memory-loading.md +1 -1
- package/dist/builtin-memory/internal/nodes-and-canvas.md +2 -2
- package/dist/builtin-memory/internal/plugins.md +59 -2
- package/dist/builtin-memory/internal/storage-tiers.md +3 -1
- package/dist/builtin-memory/spec/roadmap.md +2 -2
- package/dist/builtin-memory/testing.md +1 -1
- package/dist/clients/attach/chrome/bash-jobs.js +3 -3
- package/dist/clients/attach/render/chat-view.d.ts +26 -1
- package/dist/clients/attach/render/chat-view.js +61 -11
- package/dist/clients/attach/render/page-block.d.ts +34 -0
- package/dist/clients/attach/render/page-block.js +226 -0
- package/dist/clients/attach/session/whip.d.ts +2 -0
- package/dist/clients/attach/session/whip.js +2 -0
- package/dist/clients/attach/viewer.js +1419 -993
- package/dist/clients/conversation/projection.js +18 -3
- package/dist/clients/inbox/__tests__/serial/inbox-controller.test.js +9 -6
- package/dist/clients/inbox/__tests__/serial/mount-panel.test.js +13 -21
- package/dist/clients/inbox/controller.js +10 -16
- package/dist/clients/inbox/page-adapter.d.ts +1 -3
- package/dist/clients/inbox/page-adapter.js +3 -4
- package/dist/clients/inbox/resolve.d.ts +1 -1
- package/dist/clients/inbox/resolve.js +3 -3
- package/dist/clients/inbox/review/document-surface.js +3 -1
- package/dist/clients/inbox/review/state.d.ts +2 -1
- package/dist/clients/inbox/review/state.js +5 -16
- package/dist/clients/inbox/tui/input.js +37 -19
- package/dist/clients/inbox/tui/panel.js +5 -31
- package/dist/clients/inbox/tui/render.d.ts +11 -0
- package/dist/clients/inbox/tui/render.js +71 -27
- package/dist/clients/inbox/tui/slots.d.ts +3 -1
- package/dist/clients/inbox/tui/slots.js +16 -12
- package/dist/clients/inbox/tui/types.d.ts +2 -7
- package/dist/clients/inbox/tui.js +2 -2
- package/dist/commands/__tests__/human.test.js +71 -114
- package/dist/commands/__tests__/node-message.test.js +14 -0
- package/dist/commands/attention.js +4 -4
- package/dist/commands/canvas-snapshot.js +1 -1
- package/dist/commands/canvas.js +1 -1
- package/dist/commands/dashboard.js +1 -1
- package/dist/commands/human/{html.d.ts → components.d.ts} +1 -1
- package/dist/commands/human/components.js +77 -0
- package/dist/commands/human/feedback.d.ts +2 -0
- package/dist/commands/human/feedback.js +108 -0
- package/dist/commands/human/prompts.d.ts +13 -5
- package/dist/commands/human/prompts.js +160 -97
- package/dist/commands/human/queue.js +57 -88
- package/dist/commands/human/shared.d.ts +2 -12
- package/dist/commands/human/shared.js +17 -11
- package/dist/commands/human.js +29 -21
- package/dist/commands/memory/origin.js +2 -2
- package/dist/commands/node/bash.js +4 -4
- package/dist/commands/node/inspect.js +3 -3
- package/dist/commands/node/lifecycle.js +1 -1
- package/dist/commands/node/message.js +2 -2
- package/dist/commands/node-worktree.js +8 -8
- package/dist/commands/pkg/browse.js +1 -1
- package/dist/commands/pkg/plugin-manage.js +129 -10
- package/dist/commands/profile/delete.js +41 -15
- package/dist/commands/profile.js +1 -1
- package/dist/commands/push.js +1 -1
- package/dist/commands/surface/node/focus.js +29 -14
- package/dist/commands/surface-edit.js +3 -3
- package/dist/commands/surface-inbox.d.ts +2 -0
- package/dist/commands/{human/inbox.js → surface-inbox.js} +14 -18
- package/dist/commands/surface.js +2 -1
- package/dist/commands/sys/doctor.js +60 -3
- package/dist/commands/sys/feedback.js +79 -25
- package/dist/commands/sys/settings.js +1 -1
- package/dist/core/__tests__/cron-broker-capacity.test.js +1 -1
- package/dist/core/__tests__/extension-abort.test.js +5 -2
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +13 -2
- package/dist/core/__tests__/fixtures/fake-engine.js +47 -3
- package/dist/core/__tests__/helpers/harness.js +17 -55
- package/dist/core/__tests__/human-cancel-guard.test.js +31 -10
- package/dist/core/__tests__/human-deliver.test.js +31 -26
- package/dist/core/__tests__/human-node-not-supervised.test.js +1 -1
- package/dist/core/__tests__/plugin-page-components.test.js +157 -0
- package/dist/core/__tests__/seam/broker-crash-teardown.test.js +4 -5
- package/dist/core/__tests__/seam/dormancy-release.test.js +40 -0
- package/dist/core/__tests__/serial/flagship-lifecycle.test.js +2 -2
- package/dist/core/__tests__/serial/human-deliver-e2e.test.js +12 -6
- package/dist/core/__tests__/serial/live-mutation.test.js +1 -1
- package/dist/core/__tests__/serial/tmux-surface.test.js +4 -2
- package/dist/core/__tests__/serial/worktree.test.js +24 -198
- package/dist/core/__tests__/session-cycles.test.js +22 -0
- package/dist/core/__tests__/stop-guard.test.js +4 -4
- package/dist/core/__tests__/watchdog-abort-arms-retry.test.js +19 -4
- package/dist/core/bash-jobs.d.ts +6 -0
- package/dist/core/bash-jobs.js +10 -0
- package/dist/core/canvas/__tests__/attention.test.js +8 -5
- package/dist/core/canvas/__tests__/render-remote.test.js +5 -5
- package/dist/core/canvas/attention.js +8 -6
- package/dist/core/canvas/browse/model.d.ts +8 -2
- package/dist/core/canvas/browse/model.js +11 -4
- package/dist/core/canvas/canvas.d.ts +7 -0
- package/dist/core/canvas/canvas.js +37 -0
- package/dist/core/canvas/crons.d.ts +9 -0
- package/dist/core/canvas/crons.js +51 -0
- package/dist/core/canvas/node-order.d.ts +22 -1
- package/dist/core/canvas/node-order.js +26 -1
- package/dist/core/canvas/render-source.d.ts +5 -0
- package/dist/core/canvas/render-source.js +1 -0
- package/dist/core/canvas/types.d.ts +9 -3
- package/dist/core/command-plugins/bundle.d.ts +6 -1
- package/dist/core/command-plugins/bundle.js +26 -13
- package/dist/core/command.js +2 -1
- package/dist/core/config.d.ts +47 -1
- package/dist/core/config.js +137 -4
- package/dist/core/help.js +1 -1
- package/dist/core/human/__tests__/page-catalog.test.js +4 -0
- package/dist/core/human/__tests__/page-tickets.test.js +65 -52
- package/dist/core/human/__tests__/page.test.js +56 -98
- package/dist/core/human/__tests__/serial/inbox-core.test.js +6 -6
- package/dist/core/human/answer-text.js +3 -3
- package/dist/core/human/answer.d.ts +6 -4
- package/dist/core/human/answer.js +4 -10
- package/dist/core/human/component-docs.d.ts +11 -7
- package/dist/core/human/component-docs.js +361 -88
- package/dist/core/human/convention.d.ts +17 -3
- package/dist/core/human/convention.js +36 -5
- package/dist/core/human/feedback-companion.d.ts +20 -0
- package/dist/core/human/feedback-companion.js +98 -0
- package/dist/core/human/feedback.d.ts +65 -0
- package/dist/core/human/feedback.js +116 -0
- package/dist/core/human/page-catalog.d.ts +40 -5
- package/dist/core/human/page-catalog.js +102 -30
- package/dist/core/human/page-errors.d.ts +4 -0
- package/dist/core/human/page-errors.js +7 -0
- package/dist/core/human/page-eval.d.ts +20 -0
- package/dist/core/human/page-eval.js +123 -0
- package/dist/core/human/page-schema.d.ts +23 -191
- package/dist/core/human/page-schema.js +60 -89
- package/dist/core/human/page-synth.d.ts +1 -1
- package/dist/core/human/page-synth.js +15 -18
- package/dist/core/human/page.d.ts +12 -27
- package/dist/core/human/page.js +142 -474
- package/dist/core/human/root.d.ts +6 -2
- package/dist/core/human/root.js +12 -4
- package/dist/core/human/scan.d.ts +3 -4
- package/dist/core/human/scan.js +16 -15
- package/dist/core/human/summary.d.ts +1 -2
- package/dist/core/human/summary.js +2 -2
- package/dist/core/human/tickets.d.ts +20 -14
- package/dist/core/human/tickets.js +91 -31
- package/dist/core/human/types.d.ts +10 -2
- package/dist/core/preview-registry.js +8 -10
- package/dist/core/profiles/default-binding.d.ts +4 -0
- package/dist/core/profiles/default-binding.js +24 -0
- package/dist/core/profiles/deletion-reservation.d.ts +8 -0
- package/dist/core/profiles/deletion-reservation.js +38 -0
- package/dist/core/profiles/manifest.d.ts +3 -0
- package/dist/core/profiles/manifest.js +12 -0
- package/dist/core/profiles/select.js +2 -2
- package/dist/core/review/realize.js +3 -3
- package/dist/core/review/store.d.ts +6 -0
- package/dist/core/review/store.js +31 -0
- package/dist/core/runtime/bearings.d.ts +0 -4
- package/dist/core/runtime/bearings.js +4 -62
- package/dist/core/runtime/bin-contributions.d.ts +44 -0
- package/dist/core/runtime/bin-contributions.js +186 -0
- package/dist/core/runtime/broker/client-registry.d.ts +4 -0
- package/dist/core/runtime/broker/client-registry.js +13 -1
- package/dist/core/runtime/broker/extension-abort.d.ts +1 -1
- package/dist/core/runtime/broker/extension-abort.js +4 -2
- package/dist/core/runtime/broker/fault-retry.js +4 -0
- package/dist/core/runtime/broker/frame-dispatch.d.ts +3 -1
- package/dist/core/runtime/broker/frame-dispatch.js +5 -6
- package/dist/core/runtime/broker/read-ops.d.ts +1 -1
- package/dist/core/runtime/broker/read-ops.js +2 -2
- package/dist/core/runtime/broker/rebind.d.ts +1 -0
- package/dist/core/runtime/broker/rebind.js +6 -1
- package/dist/core/runtime/broker-extension-render.js +19 -0
- package/dist/core/runtime/broker-protocol.d.ts +14 -0
- package/dist/core/runtime/broker.d.ts +1 -1
- package/dist/core/runtime/broker.js +17 -6
- package/dist/core/runtime/front-door-env.d.ts +5 -0
- package/dist/core/runtime/front-door-env.js +16 -0
- package/dist/core/runtime/front-door.d.ts +1 -5
- package/dist/core/runtime/front-door.js +2 -5
- package/dist/core/runtime/host.d.ts +1 -1
- package/dist/core/runtime/invocation.d.ts +6 -0
- package/dist/core/runtime/invocation.js +10 -0
- package/dist/core/runtime/kickoff.js +34 -0
- package/dist/core/runtime/launch.d.ts +2 -6
- package/dist/core/runtime/node-read.js +4 -1
- package/dist/core/runtime/nodes.js +2 -0
- package/dist/core/runtime/promote.d.ts +1 -0
- package/dist/core/runtime/promote.js +3 -1
- package/dist/core/runtime/session-cycles.d.ts +6 -0
- package/dist/core/runtime/session-cycles.js +15 -1
- package/dist/core/runtime/session-visibility.d.ts +17 -8
- package/dist/core/runtime/session-visibility.js +19 -10
- package/dist/core/runtime/spawn-env.d.ts +9 -1
- package/dist/core/runtime/spawn-env.js +62 -2
- package/dist/core/runtime/stop-guard.d.ts +2 -2
- package/dist/core/runtime/stop-guard.js +2 -2
- package/dist/core/runtime/stop-signals.d.ts +1 -1
- package/dist/core/runtime/stop-signals.js +2 -2
- package/dist/core/runtime/tmux-bindings.js +1 -1
- package/dist/core/session-model/session-state.d.ts +4 -3
- package/dist/core/session-model/session-state.js +14 -1
- package/dist/core/user-settings.d.ts +21 -2
- package/dist/core/user-settings.js +32 -11
- package/dist/core/worktree.d.ts +15 -45
- package/dist/core/worktree.js +59 -261
- package/dist/daemon/api/__tests__/seam/profile-delete.test.d.ts +1 -0
- package/dist/daemon/api/__tests__/seam/profile-delete.test.js +386 -0
- package/dist/daemon/api/handlers/bash-jobs.js +1 -1
- package/dist/daemon/api/handlers/broker-ops.js +9 -5
- package/dist/daemon/api/handlers/feedback-comments.d.ts +2 -0
- package/dist/daemon/api/handlers/feedback-comments.js +207 -0
- package/dist/daemon/api/handlers/human.js +24 -20
- package/dist/daemon/api/handlers/inbox.d.ts +10 -0
- package/dist/daemon/api/handlers/inbox.js +39 -115
- package/dist/daemon/api/handlers/profiles.js +11 -29
- package/dist/daemon/api/map.d.ts +6 -1
- package/dist/daemon/api/map.js +23 -0
- package/dist/daemon/api/server.js +3 -1
- package/dist/daemon/companion-retire.d.ts +2 -0
- package/dist/daemon/companion-retire.js +33 -0
- package/dist/daemon/cron-run.js +14 -16
- package/dist/daemon/human/finish.d.ts +12 -7
- package/dist/daemon/human/finish.js +101 -38
- package/dist/daemon/human/sweep.js +5 -1
- package/dist/daemon/profile-delete.d.ts +7 -0
- package/dist/daemon/profile-delete.js +306 -0
- package/dist/daemon/reconcilers/broker-supervision.js +3 -1
- package/dist/daemon/review/deliver.js +3 -3
- package/dist/daemon/review/finish.d.ts +0 -2
- package/dist/daemon/review/finish.js +1 -29
- package/dist/pi-extensions/__tests__/canvas-bash-valve.test.js +56 -1
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +4 -4
- package/dist/pi-extensions/canvas-bash-valve.js +19 -5
- package/dist/pi-extensions/canvas-inbox-watcher.js +1 -1
- package/dist/pi-extensions/canvas-review-boundary.d.ts +4 -0
- package/dist/pi-extensions/canvas-review-boundary.js +18 -9
- package/dist/pi-extensions/canvas-stophook.js +4 -4
- package/dist/shared/generated-context.js +2 -2
- package/dist/types.d.ts +44 -2
- package/dist/types.js +7 -2
- package/package.json +6 -5
- package/runtime.lock.json +5 -33
- package/dist/builtin-memory/init.md +0 -38
- package/dist/builtin-memory/plan.md +0 -19
- package/dist/builtin-memory/spec.md +0 -19
- package/dist/commands/human/doc.d.ts +0 -2
- package/dist/commands/human/doc.js +0 -91
- package/dist/commands/human/html.js +0 -81
- package/dist/commands/human/inbox.d.ts +0 -2
- package/dist/core/human/__tests__/html-markdown.test.js +0 -52
- package/dist/core/human/__tests__/page-render.test.js +0 -66
- package/dist/core/human/html-markdown.d.ts +0 -3
- package/dist/core/human/html-markdown.js +0 -288
- package/dist/core/human/page-render.d.ts +0 -4
- package/dist/core/human/page-render.js +0 -105
- package/dist/pages/bundle.css +0 -1
- package/dist/pages/bundle.js +0 -1778
- package/dist/pages/comments.d.ts +0 -72
- package/dist/pages/comments.js +0 -176
- package/dist/pages/controls.d.ts +0 -98
- package/dist/pages/controls.js +0 -261
- package/dist/pages/elements/cards.d.ts +0 -28
- package/dist/pages/elements/cards.js +0 -915
- package/dist/pages/elements/chart.d.ts +0 -23
- package/dist/pages/elements/chart.js +0 -664
- package/dist/pages/elements/options.d.ts +0 -30
- package/dist/pages/elements/options.js +0 -742
- package/dist/pages/elements/pages.d.ts +0 -30
- package/dist/pages/elements/pages.js +0 -377
- package/dist/pages/elements/slot.d.ts +0 -26
- package/dist/pages/elements/slot.js +0 -112
- package/dist/pages/elements/table.d.ts +0 -36
- package/dist/pages/elements/table.js +0 -1043
- package/dist/pages/elements/text.d.ts +0 -31
- package/dist/pages/elements/text.js +0 -1175
- package/dist/pages/entry.d.ts +0 -8
- package/dist/pages/entry.js +0 -8
- package/dist/pages/host.d.ts +0 -134
- package/dist/pages/host.js +0 -165
- package/dist/pages/readonly.d.ts +0 -24
- package/dist/pages/readonly.js +0 -31
- package/dist/pages/register.d.ts +0 -2
- package/dist/pages/register.js +0 -3
- package/dist/pages/responses.d.ts +0 -37
- package/dist/pages/responses.js +0 -56
- package/dist/pages/slot-config.d.ts +0 -36
- package/dist/pages/slot-config.js +0 -50
- package/dist/pages/types.d.ts +0 -98
- package/dist/pages/types.js +0 -23
- /package/dist/{core/human/__tests__/html-markdown.test.d.ts → commands/__tests__/node-message.test.d.ts} +0 -0
- /package/dist/core/{human/__tests__/page-render.test.d.ts → __tests__/plugin-page-components.test.d.ts} +0 -0
package/dist/daemon/cron-run.js
CHANGED
|
@@ -30,26 +30,24 @@
|
|
|
30
30
|
// promises for graceful-teardown drain but never blocks on a slow run.
|
|
31
31
|
import { spawn } from 'node:child_process';
|
|
32
32
|
import { createHash, randomBytes, randomUUID } from 'node:crypto';
|
|
33
|
-
import { join } from 'node:path';
|
|
34
33
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
35
34
|
import { rmSync } from 'node:fs';
|
|
36
35
|
import { synthesizedPageDocument, writeSynthesizedPage } from '../core/human/page-synth.js';
|
|
37
36
|
import { ticketDir } from '../core/human/root.js';
|
|
38
37
|
import { preparePage, publishPage } from '../core/human/tickets.js';
|
|
39
|
-
import {
|
|
38
|
+
import { resolvePageComponents } from '../core/config.js';
|
|
40
39
|
import { dueClockCrons, dueOverlapCrons, expiredIdleCrons, runningCrons, acquireCronRunLease, releaseCronRunLease, recordCronRun, listCronRuns, advanceCron, consumeCron, cancelCron, killLeasedCronRun, verifyLeasedCronRun, getCron, setCronState, setCronLastOutputHash, } from '../core/canvas/crons.js';
|
|
41
40
|
import { getNode } from '../core/canvas/canvas.js';
|
|
42
41
|
import { fullName } from '../core/canvas/labels.js';
|
|
43
42
|
import { killProcessGroup } from '../core/canvas/pid.js';
|
|
44
43
|
import { appendInbox } from '../core/feed/inbox.js';
|
|
45
|
-
import { atomicWriteJson } from '../core/fs-utils.js';
|
|
46
44
|
import { emitEvent } from '../core/events/emit.js';
|
|
47
45
|
import { loadProfileManifest } from '../core/profiles/manifest.js';
|
|
48
46
|
import { hasNoNaturalCycle } from '../core/runtime/revive-all.js';
|
|
49
47
|
import { spawnChild } from '../core/runtime/spawn.js';
|
|
50
48
|
import { cronWakeOrigin } from '../core/runtime/bearings.js';
|
|
51
49
|
import { nextSlotAfter, cadenceDisplay } from '../core/wake.js';
|
|
52
|
-
import { buildOperationalEnvBase } from '../core/runtime/spawn-env.js';
|
|
50
|
+
import { ADMITTED_HOST_PATH_ENV, buildOperationalEnvBase, resolveAdmittedHostCommand } from '../core/runtime/spawn-env.js';
|
|
53
51
|
import { parseSink } from './cron-sink.js';
|
|
54
52
|
import { readProfileEnvVars } from '../core/profiles/env-store.js';
|
|
55
53
|
/** Output head caps for the run log (spec: "capped, e.g. 4KB"). */
|
|
@@ -223,9 +221,9 @@ async function deliverSpawnSink(c, kind, stdout) {
|
|
|
223
221
|
});
|
|
224
222
|
return res.node.node_id;
|
|
225
223
|
}
|
|
226
|
-
/** Deliver stdout through crouter as a
|
|
227
|
-
*
|
|
228
|
-
*
|
|
224
|
+
/** Deliver stdout through crouter as a standalone human page. Mirrors
|
|
225
|
+
* `crtr human send --inbox` for a display-only page, minus the CLI-env source
|
|
226
|
+
* stamp: the page names the cron instead. */
|
|
229
227
|
function deliverHumanSink(c, stdout) {
|
|
230
228
|
const synthesized = writeSynthesizedPage(synthesizedPageDocument(`⏰ cron ${c.name}`, `Scheduled cron "${c.name}" delivered output for your attention.`, stdout, false));
|
|
231
229
|
// Cron stdout rides into the page verbatim, so it can carry invalid page
|
|
@@ -236,20 +234,18 @@ function deliverHumanSink(c, stdout) {
|
|
|
236
234
|
const profileName = resolveLiveProfileName(c);
|
|
237
235
|
prepared = preparePage({
|
|
238
236
|
sourceFile: synthesized.path,
|
|
239
|
-
dialect: 'html',
|
|
240
237
|
source: {
|
|
241
238
|
sessionName: `cron ${c.name} (${c.cron_id})`,
|
|
242
239
|
...(profileName === undefined ? {} : { profileName }),
|
|
243
240
|
},
|
|
244
|
-
productKinds:
|
|
241
|
+
productKinds: resolvePageComponents(),
|
|
242
|
+
delivery: { placement: 'inline', inbox: true, reply: false },
|
|
245
243
|
});
|
|
246
244
|
}
|
|
247
245
|
finally {
|
|
248
246
|
rmSync(synthesized.dir, { recursive: true, force: true });
|
|
249
247
|
}
|
|
250
|
-
const id = `
|
|
251
|
-
const rc = { mode: 'notify' };
|
|
252
|
-
atomicWriteJson(join(ticketDir(id), 'run.json'), rc);
|
|
248
|
+
const id = `page-${randomBytes(4).toString('hex')}`;
|
|
253
249
|
publishPage(ticketDir(id), prepared);
|
|
254
250
|
}
|
|
255
251
|
/** Route stdout to the cron's armed sink. Returns the delivered string on
|
|
@@ -267,7 +263,7 @@ async function deliverToSink(c, stdout) {
|
|
|
267
263
|
return `delivered: spawned ${nodeId}`;
|
|
268
264
|
}
|
|
269
265
|
deliverHumanSink(c, stdout);
|
|
270
|
-
return 'delivered: human
|
|
266
|
+
return 'delivered: human send --inbox';
|
|
271
267
|
}
|
|
272
268
|
function failureSummary(ctx, deliveryFailure) {
|
|
273
269
|
if (deliveryFailure !== null)
|
|
@@ -328,7 +324,7 @@ function buildEscalationKickoff(c, ctx, deliveryFailure) {
|
|
|
328
324
|
`Diagnose why it failed and decide what to do:`,
|
|
329
325
|
`- Transient or fixable: fix the environment/command context, verify with \`crtr cron run ${c.cron_id}\` (fires now, out of band, shows you the result), then \`crtr cron resume ${c.cron_id}\` to re-arm it.`,
|
|
330
326
|
`- Obsolete: \`crtr cron cancel ${c.cron_id}\`.`,
|
|
331
|
-
`- The user's call (or the fix needs a change you cannot make): \`crtr human
|
|
327
|
+
`- The user's call (or the fix needs a change you cannot make): \`crtr human send\`.`,
|
|
332
328
|
'',
|
|
333
329
|
'The cron stays paused until someone resumes it — nothing refires while you work.',
|
|
334
330
|
].join('\n');
|
|
@@ -639,8 +635,9 @@ export function executeCron(c, opts) {
|
|
|
639
635
|
// Layering: profile store < the arm-time `--env` snapshot (an explicit
|
|
640
636
|
// choice) < crtr's own constructed CRTR_* names, which always win.
|
|
641
637
|
const profileId = resolveLiveProfile(c);
|
|
638
|
+
const operationalEnv = buildOperationalEnvBase({ targetCwd: c.cwd, targetProfileId: profileId });
|
|
642
639
|
const env = {
|
|
643
|
-
...
|
|
640
|
+
...operationalEnv,
|
|
644
641
|
...readProfileEnvVars(profileId),
|
|
645
642
|
...extraEnv,
|
|
646
643
|
...(profileId !== null ? { CRTR_PROFILE_ID: profileId } : {}),
|
|
@@ -660,11 +657,12 @@ export function executeCron(c, opts) {
|
|
|
660
657
|
}
|
|
661
658
|
if (c.anchor_node != null)
|
|
662
659
|
env['CRTR_ANCHOR'] = c.anchor_node;
|
|
660
|
+
env[ADMITTED_HOST_PATH_ENV] = operationalEnv[ADMITTED_HOST_PATH_ENV];
|
|
663
661
|
const startedAtMs = Date.now();
|
|
664
662
|
const startedAtIso = new Date(startedAtMs).toISOString();
|
|
665
663
|
let child;
|
|
666
664
|
try {
|
|
667
|
-
child = spawn('bash', ['-c', c.command], {
|
|
665
|
+
child = spawn(resolveAdmittedHostCommand('bash', env, c.cwd), ['-c', c.command], {
|
|
668
666
|
cwd: c.cwd,
|
|
669
667
|
env,
|
|
670
668
|
detached: true, // own process group — killProcessGroup(-pid) reaps the whole tree
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import type { ProductPageComponents } from '../../types.js';
|
|
2
1
|
import type { TicketResult } from '../../core/human/types.js';
|
|
2
|
+
/** Every ticket-ending action retires the ticket's feedback companion — done
|
|
3
|
+
* when active or idle, never left resident. Idempotent and replay-safe; a
|
|
4
|
+
* corrupt feedback file must not block result publication. Exported for the
|
|
5
|
+
* daemon-start sweep, which must settle terminal tickets whether or not a
|
|
6
|
+
* reply bridge exists. */
|
|
7
|
+
export declare function settleFeedbackCompanion(ticketId: string): void;
|
|
3
8
|
/**
|
|
4
9
|
* The one completion path for a page ticket: claim takeover, exclusive result
|
|
5
|
-
* publication, then
|
|
6
|
-
*
|
|
7
|
-
*
|
|
10
|
+
* publication, then optional reply delivery. Every addressing surface routes
|
|
11
|
+
* through this function, so the reply-route descriptor is the sole authority
|
|
12
|
+
* on whether completion reaches a bridge.
|
|
8
13
|
*/
|
|
9
|
-
export declare function resolvePageTicket(
|
|
14
|
+
export declare function resolvePageTicket(ticketId: string, responses: unknown): Promise<TicketResult>;
|
|
10
15
|
/** Cancel a ticket with the store's first-writer-wins publication. */
|
|
11
|
-
export declare function cancelHumanTicket(
|
|
16
|
+
export declare function cancelHumanTicket(ticketId: string, opts: {
|
|
12
17
|
reason?: string;
|
|
13
18
|
actor?: string;
|
|
14
19
|
}): Promise<TicketResult>;
|
|
@@ -17,4 +22,4 @@ export declare function cancelHumanTicket(nodeId: string, opts: {
|
|
|
17
22
|
* receipt: every action below is replay-safe, so lost delivery can be retried
|
|
18
23
|
* by a later resolve/cancel attempt or the daemon-start sweep.
|
|
19
24
|
*/
|
|
20
|
-
export declare function deliverTerminalResult(
|
|
25
|
+
export declare function deliverTerminalResult(ticketId: string): Promise<boolean>;
|
|
@@ -1,17 +1,67 @@
|
|
|
1
|
-
import { join } from 'node:path';
|
|
2
1
|
import { ApiError } from '../../api/index.js';
|
|
3
2
|
import { getRow, subscribersOf } from '../../core/canvas/canvas.js';
|
|
4
|
-
import { readConfig } from '../../core/config.js';
|
|
5
3
|
import { ticketDir } from '../../core/human/root.js';
|
|
6
4
|
import { parsePage } from '../../core/human/page.js';
|
|
7
5
|
import { cancelTicket, projectReviewOutput, readTicketResult, takeoverAndCompletePage, } from '../../core/human/tickets.js';
|
|
8
6
|
import { describePageAnswer } from '../../core/human/answer.js';
|
|
9
7
|
import { renderAnswerText } from '../../core/human/answer-text.js';
|
|
10
|
-
import { responsePath } from '../../core/human/convention.js';
|
|
11
|
-
import {
|
|
8
|
+
import { pagePath, readTicketReplyRoute, responsePath } from '../../core/human/convention.js';
|
|
9
|
+
import { readPageFeedback } from '../../core/human/feedback.js';
|
|
10
|
+
import { pageDocumentSha256 } from '../../core/human/feedback-companion.js';
|
|
12
11
|
import { appendInbox } from '../../core/feed/inbox.js';
|
|
13
12
|
import { FinalDeliveryError, pushFinal } from '../../core/feed/feed.js';
|
|
14
13
|
import { transition } from '../../core/runtime/lifecycle.js';
|
|
14
|
+
import { emitEvent } from '../../core/events/emit.js';
|
|
15
|
+
import { retireCompanion } from '../companion-retire.js';
|
|
16
|
+
/** Every ticket-ending action retires the ticket's feedback companion — done
|
|
17
|
+
* when active or idle, never left resident. Idempotent and replay-safe; a
|
|
18
|
+
* corrupt feedback file must not block result publication. Exported for the
|
|
19
|
+
* daemon-start sweep, which must settle terminal tickets whether or not a
|
|
20
|
+
* reply bridge exists. */
|
|
21
|
+
export function settleFeedbackCompanion(ticketId) {
|
|
22
|
+
try {
|
|
23
|
+
const companionNodeId = readPageFeedback(ticketDir(ticketId)).companionNodeId;
|
|
24
|
+
if (companionNodeId !== undefined)
|
|
25
|
+
retireCompanion(companionNodeId, 'approved');
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
emitEvent({ level: 'warn', event: 'human.feedback_companion.retire_failed', error });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** The settle-time feedback addendum to a page completion — answer and
|
|
32
|
+
* cancellation alike: a change notice when the final page differs from the
|
|
33
|
+
* version first commented on, and each still-open item's quote and note.
|
|
34
|
+
* Silent when the page is unchanged and no feedback remains open; never a
|
|
35
|
+
* manufactured prose diff. */
|
|
36
|
+
function feedbackAddendum(dir) {
|
|
37
|
+
let sections;
|
|
38
|
+
try {
|
|
39
|
+
const feedback = readPageFeedback(dir);
|
|
40
|
+
if (feedback.companionNodeId === undefined)
|
|
41
|
+
return '';
|
|
42
|
+
sections = [];
|
|
43
|
+
if (feedback.firstCommentedSha256 !== undefined && pageDocumentSha256(dir) !== feedback.firstCommentedSha256) {
|
|
44
|
+
sections.push(`The page changed during feedback: the person commented on it and its companion conversation (node ${feedback.companionNodeId}) revised it in place. ` +
|
|
45
|
+
`Reread the final page at ${pagePath(dir, 'jsx')} before interpreting this outcome.`);
|
|
46
|
+
}
|
|
47
|
+
const open = feedback.comments.filter((comment) => comment.status === 'open');
|
|
48
|
+
if (open.length > 0) {
|
|
49
|
+
const items = open.map((comment) => [
|
|
50
|
+
...comment.quote.split('\n').map((line) => `> ${line}`),
|
|
51
|
+
comment.note,
|
|
52
|
+
].join('\n'));
|
|
53
|
+
sections.push([
|
|
54
|
+
`Feedback left unresolved at settlement — the companion conversation (node ${feedback.companionNodeId}) had not dealt with these items:`,
|
|
55
|
+
...items,
|
|
56
|
+
].join('\n\n'));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// The answer itself is the deliverable; a broken feedback record adds nothing.
|
|
61
|
+
return '';
|
|
62
|
+
}
|
|
63
|
+
return sections.length === 0 ? '' : `\n\n${sections.join('\n\n')}`;
|
|
64
|
+
}
|
|
15
65
|
/** A corrupt manifest must not strand the asking node: the raw response remains its escape hatch. */
|
|
16
66
|
function renderCorruptPageAnswer(result, resultPath) {
|
|
17
67
|
return [
|
|
@@ -24,22 +74,21 @@ function renderCorruptPageAnswer(result, resultPath) {
|
|
|
24
74
|
}
|
|
25
75
|
function pageTitle(dir) {
|
|
26
76
|
try {
|
|
27
|
-
return parsePage(dir
|
|
77
|
+
return parsePage(dir).title;
|
|
28
78
|
}
|
|
29
79
|
catch {
|
|
30
80
|
return undefined;
|
|
31
81
|
}
|
|
32
82
|
}
|
|
33
83
|
function renderPageAnswer(dir, result, resultPath) {
|
|
34
|
-
const productKinds = readConfig('user').page_components;
|
|
35
84
|
let manifest;
|
|
36
85
|
try {
|
|
37
|
-
manifest = parsePage(dir
|
|
86
|
+
manifest = parsePage(dir);
|
|
38
87
|
}
|
|
39
88
|
catch {
|
|
40
89
|
return renderCorruptPageAnswer(result, resultPath);
|
|
41
90
|
}
|
|
42
|
-
return renderAnswerText(describePageAnswer(manifest, result.responses, { completedAt: result.completedAt
|
|
91
|
+
return renderAnswerText(describePageAnswer(manifest, result.responses, { completedAt: result.completedAt }), resultPath) + feedbackAddendum(dir);
|
|
43
92
|
}
|
|
44
93
|
function deliveryFailed(error) {
|
|
45
94
|
// A canonical final is committed inside its own write boundary and can never
|
|
@@ -51,9 +100,9 @@ function deliveryFailed(error) {
|
|
|
51
100
|
const detail = error instanceof Error ? error.message : String(error);
|
|
52
101
|
return new ApiError(500, 'delivery_failed', `The answer is recorded and delivery will be retried: ${detail}`);
|
|
53
102
|
}
|
|
54
|
-
async function deliverOrConflict(
|
|
103
|
+
async function deliverOrConflict(ticketId) {
|
|
55
104
|
try {
|
|
56
|
-
await deliverTerminalResult(
|
|
105
|
+
await deliverTerminalResult(ticketId);
|
|
57
106
|
}
|
|
58
107
|
catch (error) {
|
|
59
108
|
throw deliveryFailed(error);
|
|
@@ -62,16 +111,16 @@ async function deliverOrConflict(nodeId) {
|
|
|
62
111
|
}
|
|
63
112
|
/**
|
|
64
113
|
* The one completion path for a page ticket: claim takeover, exclusive result
|
|
65
|
-
* publication, then
|
|
66
|
-
*
|
|
67
|
-
*
|
|
114
|
+
* publication, then optional reply delivery. Every addressing surface routes
|
|
115
|
+
* through this function, so the reply-route descriptor is the sole authority
|
|
116
|
+
* on whether completion reaches a bridge.
|
|
68
117
|
*/
|
|
69
|
-
export async function resolvePageTicket(
|
|
70
|
-
const completed = takeoverAndCompletePage(ticketDir(
|
|
118
|
+
export async function resolvePageTicket(ticketId, responses) {
|
|
119
|
+
const completed = takeoverAndCompletePage(ticketDir(ticketId), responses, { host: 'crtrd', pid: process.pid });
|
|
71
120
|
if (!completed.won)
|
|
72
|
-
return deliverOrConflict(
|
|
121
|
+
return deliverOrConflict(ticketId);
|
|
73
122
|
try {
|
|
74
|
-
await deliverTerminalResult(
|
|
123
|
+
await deliverTerminalResult(ticketId);
|
|
75
124
|
}
|
|
76
125
|
catch (error) {
|
|
77
126
|
throw deliveryFailed(error);
|
|
@@ -79,12 +128,12 @@ export async function resolvePageTicket(nodeId, responses, productKinds = []) {
|
|
|
79
128
|
return completed.result;
|
|
80
129
|
}
|
|
81
130
|
/** Cancel a ticket with the store's first-writer-wins publication. */
|
|
82
|
-
export async function cancelHumanTicket(
|
|
83
|
-
const canceled = cancelTicket(ticketDir(
|
|
131
|
+
export async function cancelHumanTicket(ticketId, opts) {
|
|
132
|
+
const canceled = cancelTicket(ticketDir(ticketId), opts);
|
|
84
133
|
if (canceled.status === 'already_resolved')
|
|
85
|
-
return deliverOrConflict(
|
|
134
|
+
return deliverOrConflict(ticketId);
|
|
86
135
|
try {
|
|
87
|
-
await deliverTerminalResult(
|
|
136
|
+
await deliverTerminalResult(ticketId);
|
|
88
137
|
}
|
|
89
138
|
catch (error) {
|
|
90
139
|
throw deliveryFailed(error);
|
|
@@ -96,41 +145,55 @@ export async function cancelHumanTicket(nodeId, opts) {
|
|
|
96
145
|
* receipt: every action below is replay-safe, so lost delivery can be retried
|
|
97
146
|
* by a later resolve/cancel attempt or the daemon-start sweep.
|
|
98
147
|
*/
|
|
99
|
-
export async function deliverTerminalResult(
|
|
100
|
-
const dir = ticketDir(
|
|
101
|
-
const run = readJsonOrNull(join(dir, 'run.json'));
|
|
102
|
-
if (run?.mode === 'notify')
|
|
103
|
-
return false;
|
|
148
|
+
export async function deliverTerminalResult(ticketId) {
|
|
149
|
+
const dir = ticketDir(ticketId);
|
|
104
150
|
const result = readTicketResult(dir);
|
|
105
151
|
if (result === null)
|
|
106
152
|
return false;
|
|
153
|
+
// Companion retirement rides the committed result, BEFORE the reply-route
|
|
154
|
+
// gate: every ticket-ending path (resolve, cancel, conflict replay) funnels
|
|
155
|
+
// through here, and an inbox-only page with no reply bridge still settles
|
|
156
|
+
// its companion. The daemon-start sweep settles the tickets that die before
|
|
157
|
+
// reaching this call.
|
|
158
|
+
settleFeedbackCompanion(ticketId);
|
|
159
|
+
const replyRoute = readTicketReplyRoute(dir);
|
|
160
|
+
if (replyRoute === null)
|
|
161
|
+
return false;
|
|
162
|
+
const bridgeNodeId = replyRoute.bridge_node_id;
|
|
107
163
|
projectReviewOutput(dir);
|
|
108
|
-
// The ROW, not
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
// cancellation notice each time. No row means nothing left to deliver to.
|
|
113
|
-
const node = getRow(nodeId);
|
|
164
|
+
// The ROW, not node metadata: a bridge whose row has been pruned may retain
|
|
165
|
+
// its ticket files, but no lifecycle target remains. No row means nothing is
|
|
166
|
+
// left to deliver to.
|
|
167
|
+
const node = getRow(bridgeNodeId);
|
|
114
168
|
if (node === null || node.status === 'dead' || node.status === 'canceled' || (node.status === 'done' && node.final_report !== null)) {
|
|
115
169
|
return true;
|
|
116
170
|
}
|
|
117
171
|
if (result.kind === 'canceled') {
|
|
118
172
|
const title = pageTitle(dir);
|
|
119
173
|
const reason = result.reason !== undefined && result.reason !== '' ? result.reason : 'no reason was given';
|
|
120
|
-
|
|
121
|
-
|
|
174
|
+
// A human dismissal is a rejection, not a routing event: say so plainly so
|
|
175
|
+
// the agent acts on the "no" instead of re-raising the same ask.
|
|
176
|
+
const dismissed = result.actor === 'human';
|
|
177
|
+
const subject = dismissed
|
|
178
|
+
? (title === undefined ? `human interaction ${ticketId} dismissed without answering` : `human interaction ${ticketId} — “${title}” dismissed without answering`)
|
|
179
|
+
: (title === undefined ? `human interaction ${ticketId} canceled — no answer is coming` : `human interaction ${ticketId} — “${title}” canceled, no answer is coming`);
|
|
180
|
+
// Dismissal and withdrawal are settlement paths too: the sender still
|
|
181
|
+
// needs any unresolved feedback and the exceptional page-change notice.
|
|
182
|
+
const body = (dismissed
|
|
183
|
+
? `The user dismissed ${title === undefined ? `the human interaction ${ticketId}` : `“${title}”`} without answering — a deliberate “no, I'm not answering this.” Treat it as a rejection: no response will arrive, no partial answer exists, and the same ask must not be raised again.`
|
|
184
|
+
: (title === undefined ? `The human interaction ${ticketId} was canceled — ${reason}. No response will arrive, and any partial answers the user had entered were discarded unsent.` : `The page “${title}” was canceled — ${reason}. No response will arrive, and any partial answers the user had entered were discarded unsent.`)) + feedbackAddendum(dir);
|
|
122
185
|
// One failing subscriber must not silence the rest, and the bridge stays
|
|
123
186
|
// live until every notice lands: a partial fanout leaves the ticket for the
|
|
124
187
|
// next resolve/cancel attempt or the daemon-start sweep to replay. A
|
|
125
188
|
// subscriber notified twice reads the same idempotent "no answer is coming";
|
|
126
189
|
// a subscriber never notified waits forever.
|
|
127
190
|
const failedTo = [];
|
|
128
|
-
for (const subscriber of subscribersOf(
|
|
191
|
+
for (const subscriber of subscribersOf(bridgeNodeId)) {
|
|
129
192
|
if (result.actor !== undefined && subscriber.node_id === result.actor)
|
|
130
193
|
continue;
|
|
131
194
|
try {
|
|
132
195
|
appendInbox(subscriber.node_id, {
|
|
133
|
-
from:
|
|
196
|
+
from: bridgeNodeId,
|
|
134
197
|
tier: 'normal',
|
|
135
198
|
kind: 'message',
|
|
136
199
|
label: subject,
|
|
@@ -143,11 +206,11 @@ export async function deliverTerminalResult(nodeId) {
|
|
|
143
206
|
}
|
|
144
207
|
if (failedTo.length > 0)
|
|
145
208
|
throw new Error(`cancellation notice failed for ${failedTo.join(', ')}`);
|
|
146
|
-
transition(
|
|
209
|
+
transition(bridgeNodeId, 'finish');
|
|
147
210
|
return true;
|
|
148
211
|
}
|
|
149
212
|
if (result.kind === 'page') {
|
|
150
|
-
await pushFinal(
|
|
213
|
+
await pushFinal(bridgeNodeId, renderPageAnswer(dir, result, responsePath(dir)));
|
|
151
214
|
}
|
|
152
215
|
return true;
|
|
153
216
|
}
|
|
@@ -5,7 +5,7 @@ import { getReviewByBridge } from '../../core/review/store.js';
|
|
|
5
5
|
import { emitEvent } from '../../core/events/emit.js';
|
|
6
6
|
import { ticketsRoot } from '../../core/human/root.js';
|
|
7
7
|
import { readTicketResult } from '../../core/human/tickets.js';
|
|
8
|
-
import { deliverTerminalResult } from './finish.js';
|
|
8
|
+
import { deliverTerminalResult, settleFeedbackCompanion } from './finish.js';
|
|
9
9
|
function isTerminalBridge(status) {
|
|
10
10
|
return status === 'done' || status === 'dead' || status === 'canceled';
|
|
11
11
|
}
|
|
@@ -27,6 +27,10 @@ export async function reconcileUndeliveredTickets() {
|
|
|
27
27
|
// finisher retries only when its winning operation is explicitly repeated.
|
|
28
28
|
if (getReviewByBridge(entry) !== null)
|
|
29
29
|
continue;
|
|
30
|
+
// A terminal page ticket retires its feedback companion whether or not a
|
|
31
|
+
// reply bridge exists: inbox-only pages have no bridge row, yet the
|
|
32
|
+
// daemon can die between result publication and companion retirement.
|
|
33
|
+
settleFeedbackCompanion(entry);
|
|
30
34
|
// The ROW is what carries status; `getNode` would answer from a pruned
|
|
31
35
|
// bridge's surviving meta.json with no status at all.
|
|
32
36
|
const bridge = getRow(entry);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { DeleteProfileResultDTO } from '../api/index.js';
|
|
2
|
+
/** Validate and enact one forced profile deletion. A request from a node that
|
|
3
|
+
* belongs to the target profile is acknowledged before its broker is stopped. */
|
|
4
|
+
export declare function requestProfileDeletion(name: string, body: unknown): Promise<{
|
|
5
|
+
status: 200 | 202;
|
|
6
|
+
body: DeleteProfileResultDTO;
|
|
7
|
+
}>;
|