@north-light/crouter 0.3.220 → 0.3.222
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 +29 -0
- package/dist/api/client.js +44 -0
- package/dist/api/dto/chat-inventory.d.ts +56 -0
- package/dist/api/dto/chat-inventory.js +11 -0
- package/dist/api/dto/human-requests.d.ts +88 -0
- package/dist/api/dto/human-requests.js +4 -0
- package/dist/api/dto/human.d.ts +3 -0
- package/dist/api/dto/profiles.d.ts +19 -5
- package/dist/api/dto/profiles.js +2 -1
- package/dist/api/dto/reviews.d.ts +2 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +2 -0
- package/dist/api/routes.d.ts +8 -0
- package/dist/api/routes.js +11 -0
- package/dist/build-root.d.ts +2 -6
- package/dist/build-root.js +51 -4
- package/dist/builtin-memory/00-runtime-base/00-authoring.md +31 -0
- package/dist/builtin-memory/00-runtime-base/01-escalation.md +14 -0
- package/dist/builtin-memory/{insights/listen.md → 00-runtime-base/02-insight-capture.md} +1 -0
- package/dist/builtin-memory/02-turn-lifecycle/00-ending-a-turn.md +27 -0
- package/dist/builtin-memory/{02-lifecycle/01-resident.md → 02-turn-lifecycle/02-resident.md} +5 -0
- package/dist/builtin-memory/04-base-worker.md +4 -8
- package/dist/builtin-memory/04-orchestration-kernel.md +1 -1
- package/dist/builtin-memory/05-kinds/advisor/01-orchestrator.md +1 -0
- package/dist/builtin-memory/05-kinds/advisor/advice-contract.md +1 -0
- package/dist/builtin-memory/05-kinds/design/00-base.md +2 -1
- package/dist/builtin-memory/05-kinds/design/01-orchestrator.md +2 -1
- package/dist/builtin-memory/05-kinds/design/design-contract.md +19 -0
- package/dist/builtin-memory/05-kinds/developer/00-base.md +1 -0
- package/dist/builtin-memory/05-kinds/developer/01-orchestrator.md +1 -0
- package/dist/builtin-memory/05-kinds/explore/00-base.md +1 -0
- package/dist/builtin-memory/05-kinds/explore/01-orchestrator.md +1 -0
- package/dist/builtin-memory/05-kinds/general/00-base.md +1 -0
- package/dist/builtin-memory/05-kinds/plan/00-base.md +2 -1
- package/dist/builtin-memory/05-kinds/plan/01-orchestrator.md +2 -1
- package/dist/builtin-memory/05-kinds/plan/plan-contract.md +28 -0
- package/dist/builtin-memory/05-kinds/plan/reviewers/architecture-fit.md +1 -0
- package/dist/builtin-memory/05-kinds/plan/reviewers/code-smells.md +1 -0
- package/dist/builtin-memory/05-kinds/plan/reviewers/lens-contract.md +1 -0
- package/dist/builtin-memory/05-kinds/plan/reviewers/pattern-consistency.md +1 -0
- package/dist/builtin-memory/05-kinds/plan/reviewers/requirements-coverage.md +1 -0
- package/dist/builtin-memory/05-kinds/plan/reviewers/security.md +1 -0
- package/dist/builtin-memory/05-kinds/review/00-base.md +1 -0
- package/dist/builtin-memory/05-kinds/review/01-orchestrator.md +1 -0
- package/dist/builtin-memory/05-kinds/review/companion/00-base.md +1 -0
- package/dist/builtin-memory/05-kinds/review/security-findings.md +1 -0
- package/dist/builtin-memory/05-kinds/spec/00-base.md +4 -3
- package/dist/builtin-memory/05-kinds/spec/01-orchestrator.md +1 -0
- package/dist/builtin-memory/05-kinds/spec/requirements.md +1 -0
- package/dist/builtin-memory/design/guide.md +35 -0
- package/dist/builtin-memory/design/roadmap.md +21 -0
- package/dist/builtin-memory/insights/capture.md +1 -1
- package/dist/builtin-memory/internal/agent-shaping.md +3 -1
- package/dist/builtin-memory/internal/memory-loading.md +4 -0
- package/dist/builtin-memory/internal/plugins.md +10 -1
- package/dist/builtin-memory/internal/storage-tiers.md +1 -1
- package/dist/builtin-memory/plan/roadmap.md +6 -22
- package/dist/builtin-memory/spec/guide.md +23 -6
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/claude-plugin-commands.ts +1 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/crtr-commands/index.ts +7 -1
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +34 -16
- package/dist/clients/attach/__tests__/ref-autocomplete.test.js +1 -1
- package/dist/clients/attach/__tests__/titled-editor-preview.test.js +1 -1
- package/dist/clients/attach/overlays/file-review.js +2 -2
- package/dist/clients/attach/render/markdown-source.js +106 -1
- package/dist/clients/attach/session/file-links.d.ts +13 -4
- package/dist/clients/attach/session/file-links.js +54 -58
- package/dist/clients/attach/session/keys.d.ts +1 -1
- package/dist/clients/attach/session/profile-files.js +1 -1
- package/dist/clients/attach/viewer.js +698 -696
- package/dist/clients/inbox/controller.js +1 -1
- package/dist/clients/inbox/resolve.d.ts +1 -0
- package/dist/clients/inbox/review/launch.d.ts +8 -4
- package/dist/clients/inbox/review/launch.js +55 -5
- package/dist/clients/inbox/review/review-client.d.ts +1 -0
- package/dist/clients/inbox/review/review-client.js +7 -1
- package/dist/clients/inbox/review-adapter.d.ts +1 -8
- package/dist/clients/inbox/review-adapter.js +4 -52
- package/dist/commands/__tests__/human.test.js +2 -2
- package/dist/commands/human/request.d.ts +2 -0
- package/dist/commands/human/request.js +281 -0
- package/dist/commands/human.js +5 -2
- package/dist/commands/memory/lint.js +2 -1
- package/dist/commands/memory/read.js +1 -0
- package/dist/commands/memory.js +1 -1
- package/dist/commands/pkg/market-manage.js +165 -75
- package/dist/commands/pkg/plugin-inspect.js +19 -2
- package/dist/commands/pkg/plugin-manage.d.ts +8 -3
- package/dist/commands/pkg/plugin-manage.js +72 -24
- package/dist/commands/profile/default.js +6 -10
- package/dist/commands/profile/list.js +5 -3
- package/dist/commands/profile/new.js +21 -8
- package/dist/commands/profile/project.js +25 -19
- package/dist/commands/profile/show.js +3 -3
- package/dist/commands/surface-inbox.js +1 -0
- package/dist/commands/sys/__tests__/migrate.test.js +16 -5
- package/dist/commands/sys/config.js +2 -2
- package/dist/commands/sys/doctor.js +87 -5
- package/dist/commands/sys/migrate.js +38 -19
- package/dist/commands/sys/setup-core.js +1 -1
- package/dist/commands/sys/sync-project-guidance.js +1 -1
- package/dist/core/__tests__/broker-extension-canvas-db-boundary.test.js +7 -4
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +1 -1
- package/dist/core/__tests__/fixtures/c5-command-boundary-ext.js +24 -0
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +24 -18
- package/dist/core/__tests__/fixtures/fake-engine.js +8 -1
- package/dist/core/__tests__/fixtures/memory-slash-live-probe.js +71 -0
- package/dist/core/__tests__/human-action-delivery.test.d.ts +1 -0
- package/dist/core/__tests__/human-action-delivery.test.js +140 -0
- package/dist/core/__tests__/human-actions.test.d.ts +1 -0
- package/dist/core/__tests__/human-actions.test.js +116 -0
- package/dist/core/__tests__/inline-memory-refs.test.js +36 -2
- package/dist/core/__tests__/profile-project-memory-delivery.test.d.ts +1 -0
- package/dist/core/__tests__/profile-project-memory-delivery.test.js +217 -0
- package/dist/core/__tests__/prospective-inventory-capability-parity.test.d.ts +1 -0
- package/dist/core/__tests__/prospective-inventory-capability-parity.test.js +91 -0
- package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.d.ts +1 -0
- package/dist/core/__tests__/seam/memory-slash-node-relative-inventory.test.js +127 -0
- package/dist/core/__tests__/seam/prospective-inventory-stdout.test.d.ts +1 -0
- package/dist/core/__tests__/seam/prospective-inventory-stdout.test.js +31 -0
- package/dist/core/__tests__/serial/broker-sdk-wiring.test.js +102 -2
- package/dist/core/bootstrap.js +6 -0
- package/dist/core/canvas/browse/app.js +5 -2
- package/dist/core/canvas/browse/model.d.ts +25 -15
- package/dist/core/canvas/browse/model.js +86 -65
- package/dist/core/canvas/db.js +23 -0
- package/dist/core/canvas/human-deliveries.d.ts +53 -0
- package/dist/core/canvas/human-deliveries.js +75 -0
- package/dist/core/canvas/render-source.d.ts +6 -0
- package/dist/core/canvas/render-source.js +7 -1
- package/dist/core/canvas/render.js +10 -2
- package/dist/core/command-hooks/artifact.d.ts +10 -0
- package/dist/core/command-hooks/artifact.js +129 -0
- package/dist/core/command-hooks/catalog.d.ts +14 -0
- package/dist/core/command-hooks/catalog.js +38 -0
- package/dist/core/command-hooks/compose.d.ts +15 -0
- package/dist/core/command-hooks/compose.js +99 -0
- package/dist/core/command-hooks/discovery.d.ts +87 -0
- package/dist/core/command-hooks/discovery.js +174 -0
- package/dist/core/command-hooks/help.d.ts +5 -0
- package/dist/core/command-hooks/help.js +18 -0
- package/dist/core/command-hooks/index.d.ts +6 -0
- package/dist/core/command-hooks/index.js +6 -0
- package/dist/core/command-hooks/report.d.ts +23 -0
- package/dist/core/command-hooks/report.js +19 -0
- package/dist/core/command-hooks/schema.d.ts +27 -0
- package/dist/core/command-hooks/schema.js +68 -0
- package/dist/core/command-hooks/transport/exec-invoke.d.ts +22 -0
- package/dist/core/command-hooks/transport/exec-invoke.js +274 -0
- package/dist/core/command-plugins/presence.d.ts +2 -0
- package/dist/core/command-plugins/presence.js +17 -0
- package/dist/core/command-plugins/transport/exec-invoke.d.ts +5 -0
- package/dist/core/command-plugins/transport/exec-invoke.js +58 -5
- package/dist/core/command.d.ts +8 -1
- package/dist/core/command.js +12 -10
- package/dist/core/config.d.ts +13 -1
- package/dist/core/config.js +51 -1
- package/dist/core/feed/inbox.d.ts +6 -0
- package/dist/core/feed/inbox.js +9 -1
- package/dist/core/help.d.ts +7 -1
- package/dist/core/human/action-binding.d.ts +21 -0
- package/dist/core/human/action-binding.js +40 -0
- package/dist/core/human/completion.d.ts +38 -0
- package/dist/core/human/completion.js +27 -0
- package/dist/core/human/convention.d.ts +2 -0
- package/dist/core/human/convention.js +2 -0
- package/dist/core/human/tickets.d.ts +25 -6
- package/dist/core/human/tickets.js +19 -13
- package/dist/core/human/types.d.ts +5 -0
- package/dist/core/human-actions.d.ts +25 -0
- package/dist/core/human-actions.js +101 -0
- package/dist/core/io.d.ts +9 -1
- package/dist/core/io.js +44 -2
- package/dist/core/memory/inline-ref-inventory.d.ts +2 -1
- package/dist/core/memory/inline-ref-inventory.js +15 -8
- package/dist/core/memory-resolver.d.ts +13 -1
- package/dist/core/memory-resolver.js +26 -20
- package/dist/core/profiles/manifest.d.ts +13 -2
- package/dist/core/profiles/manifest.js +84 -18
- package/dist/core/profiles/select.d.ts +2 -0
- package/dist/core/profiles/select.js +29 -12
- package/dist/core/render.js +11 -0
- package/dist/core/runtime/advertised-command-invocation.d.ts +20 -0
- package/dist/core/runtime/advertised-command-invocation.js +233 -0
- package/dist/core/runtime/bearings.js +1 -1
- package/dist/core/runtime/broker/event-projection.js +7 -0
- package/dist/core/runtime/broker/frame-dispatch.d.ts +1 -1
- package/dist/core/runtime/broker/frame-dispatch.js +13 -11
- package/dist/core/runtime/broker/read-ops.d.ts +4 -0
- package/dist/core/runtime/broker/read-ops.js +6 -2
- package/dist/core/runtime/broker-extension-render.js +1 -1
- package/dist/core/runtime/broker-inventory.d.ts +4 -0
- package/dist/core/runtime/broker-inventory.js +116 -0
- package/dist/core/runtime/broker-persona-guidance.js +1 -1
- package/dist/core/runtime/broker-protocol.d.ts +9 -2
- package/dist/core/runtime/broker.js +10 -1
- package/dist/core/runtime/chat-inventory-rows.d.ts +8 -0
- package/dist/core/runtime/chat-inventory-rows.js +105 -0
- package/dist/core/runtime/command-surface.d.ts +38 -0
- package/dist/core/runtime/command-surface.js +117 -0
- package/dist/core/runtime/launch-target.d.ts +25 -0
- package/dist/core/runtime/launch-target.js +54 -0
- package/dist/core/runtime/node-read.js +5 -0
- package/dist/core/runtime/persona.js +3 -3
- package/dist/core/runtime/prospective-inventory-cli.d.ts +1 -0
- package/dist/core/runtime/prospective-inventory-cli.js +61 -0
- package/dist/core/runtime/prospective-inventory.d.ts +10 -0
- package/dist/core/runtime/prospective-inventory.js +88 -0
- package/dist/core/runtime/spawn.d.ts +3 -1
- package/dist/core/runtime/spawn.js +5 -3
- package/dist/core/scope.d.ts +26 -1
- package/dist/core/scope.js +52 -12
- package/dist/core/substrate/on-read.d.ts +7 -1
- package/dist/core/substrate/on-read.js +30 -32
- package/dist/core/substrate/render-node.d.ts +3 -2
- package/dist/core/substrate/render-node.js +3 -2
- package/dist/core/substrate/render.js +65 -24
- package/dist/core/substrate/schema.d.ts +16 -2
- package/dist/core/substrate/schema.js +14 -5
- package/dist/core/user-settings.d.ts +4 -0
- package/dist/core/user-settings.js +1 -0
- package/dist/daemon/api/__tests__/profile-launch-gates.test.js +56 -7
- package/dist/daemon/api/handlers/chat-inventory.d.ts +2 -0
- package/dist/daemon/api/handlers/chat-inventory.js +25 -0
- package/dist/daemon/api/handlers/human-requests.d.ts +2 -0
- package/dist/daemon/api/handlers/human-requests.js +409 -0
- package/dist/daemon/api/handlers/human.js +3 -0
- package/dist/daemon/api/handlers/inbox.js +3 -0
- package/dist/daemon/api/handlers/nodes.d.ts +1 -3
- package/dist/daemon/api/handlers/nodes.js +11 -46
- package/dist/daemon/api/handlers/profiles.js +7 -1
- package/dist/daemon/api/handlers/prospective-chat-inventory.d.ts +2 -0
- package/dist/daemon/api/handlers/prospective-chat-inventory.js +59 -0
- package/dist/daemon/api/handlers/reviews.js +10 -2
- package/dist/daemon/api/map.d.ts +2 -1
- package/dist/daemon/api/map.js +3 -2
- package/dist/daemon/api/server.js +6 -0
- package/dist/daemon/crtrd.js +6 -0
- package/dist/daemon/human/deliver-action.d.ts +16 -0
- package/dist/daemon/human/deliver-action.js +168 -0
- package/dist/daemon/human/finish.d.ts +8 -5
- package/dist/daemon/human/finish.js +45 -6
- package/dist/daemon/human/sweep.js +4 -1
- package/dist/daemon/reconcilers/human-delivery-lane.d.ts +10 -0
- package/dist/daemon/reconcilers/human-delivery-lane.js +41 -0
- package/dist/daemon/review/finish.d.ts +8 -3
- package/dist/daemon/review/finish.js +19 -1
- package/dist/hook-authoring.d.ts +75 -0
- package/dist/hook-authoring.js +358 -0
- package/dist/hook-process.d.ts +7 -0
- package/dist/hook-process.js +34 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/migrations/002-profile-project-memory.d.ts +2 -0
- package/dist/migrations/002-profile-project-memory.js +71 -0
- package/dist/migrations/profile-manifests.d.ts +30 -0
- package/dist/migrations/profile-manifests.js +70 -0
- package/dist/migrations/registry.js +10 -5
- package/dist/migrations/types.d.ts +28 -1
- package/dist/migrations/types.js +15 -9
- package/dist/pi-extensions/__tests__/canvas-structured-output.test.js +21 -4
- package/dist/pi-extensions/canvas-structured-output.js +85 -2
- package/dist/types.d.ts +23 -6
- package/dist/types.js +1 -0
- package/package.json +1 -1
- package/runtime.lock.json +2 -2
- package/dist/builtin-memory/00-runtime-base.md +0 -55
- package/dist/builtin-memory/design.md +0 -55
- package/dist/clients/attach/__tests__/file-review-focus.test.js +0 -49
- /package/dist/builtin-memory/{02-lifecycle/00-terminal.md → 02-turn-lifecycle/01-terminal.md} +0 -0
- /package/dist/{clients/attach/__tests__/file-review-focus.test.d.ts → core/__tests__/fixtures/memory-slash-live-probe.d.ts} +0 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { isDeterministicCommandExpansion } from './command-expansion.js';
|
|
2
|
+
const MEMORY_KINDS = new Set(['knowledge', 'preference']);
|
|
3
|
+
const MEMORY_SCOPES = new Set(['node', 'project', 'profile', 'user', 'builtin']);
|
|
4
|
+
function nonEmptyString(value) {
|
|
5
|
+
return typeof value === 'string' && value !== '';
|
|
6
|
+
}
|
|
7
|
+
/** Keep the command rows a Gateway can use. Malformed registrations are
|
|
8
|
+
* discarded individually so one producer cannot take down the inventory. */
|
|
9
|
+
export function gatewayCommandRows(rows) {
|
|
10
|
+
if (!Array.isArray(rows))
|
|
11
|
+
return [];
|
|
12
|
+
const out = [];
|
|
13
|
+
for (const row of rows) {
|
|
14
|
+
if (typeof row !== 'object' || row === null)
|
|
15
|
+
continue;
|
|
16
|
+
const record = row;
|
|
17
|
+
if (record['gatewayCapable'] !== true)
|
|
18
|
+
continue;
|
|
19
|
+
const source = record['source'];
|
|
20
|
+
if (source !== 'command' && source !== 'template')
|
|
21
|
+
continue;
|
|
22
|
+
if (!nonEmptyString(record['name']) || typeof record['description'] !== 'string')
|
|
23
|
+
continue;
|
|
24
|
+
const expansion = record['expansion'];
|
|
25
|
+
const template = record['template'];
|
|
26
|
+
const argumentHint = record['argumentHint'];
|
|
27
|
+
out.push({
|
|
28
|
+
name: record['name'],
|
|
29
|
+
description: record['description'],
|
|
30
|
+
source,
|
|
31
|
+
...(nonEmptyString(argumentHint) ? { argument_hint: argumentHint } : {}),
|
|
32
|
+
...(isDeterministicCommandExpansion(expansion) ? { expansion } : {}),
|
|
33
|
+
...(typeof template === 'string' ? { template } : {}),
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
export function chatInventoryCommandRows(rows) {
|
|
39
|
+
if (!Array.isArray(rows))
|
|
40
|
+
return [];
|
|
41
|
+
return rows.filter((row) => {
|
|
42
|
+
if (typeof row !== 'object' || row === null)
|
|
43
|
+
return false;
|
|
44
|
+
const record = row;
|
|
45
|
+
if (!nonEmptyString(record['name']) || typeof record['description'] !== 'string')
|
|
46
|
+
return false;
|
|
47
|
+
if (record['source'] !== 'command' && record['source'] !== 'template')
|
|
48
|
+
return false;
|
|
49
|
+
if (record['argument_hint'] !== undefined && !nonEmptyString(record['argument_hint']))
|
|
50
|
+
return false;
|
|
51
|
+
if (record['template'] !== undefined && typeof record['template'] !== 'string')
|
|
52
|
+
return false;
|
|
53
|
+
return record['expansion'] === undefined || isDeterministicCommandExpansion(record['expansion']);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
export function gatewayCommandRowsFromDetail(detail) {
|
|
57
|
+
if (detail === undefined)
|
|
58
|
+
return [];
|
|
59
|
+
try {
|
|
60
|
+
return gatewayCommandRows(JSON.parse(detail));
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export function chatInventoryMemoryRows(rows) {
|
|
67
|
+
if (!Array.isArray(rows))
|
|
68
|
+
return [];
|
|
69
|
+
return rows.filter((row) => {
|
|
70
|
+
if (typeof row !== 'object' || row === null)
|
|
71
|
+
return false;
|
|
72
|
+
const record = row;
|
|
73
|
+
return nonEmptyString(record['name'])
|
|
74
|
+
&& typeof record['shortForm'] === 'string'
|
|
75
|
+
&& typeof record['kind'] === 'string'
|
|
76
|
+
&& MEMORY_KINDS.has(record['kind'])
|
|
77
|
+
&& typeof record['scope'] === 'string'
|
|
78
|
+
&& MEMORY_SCOPES.has(record['scope']);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
export function gatewayMemoryRows(refs) {
|
|
82
|
+
if (!Array.isArray(refs))
|
|
83
|
+
return [];
|
|
84
|
+
const out = [];
|
|
85
|
+
for (const row of refs) {
|
|
86
|
+
if (typeof row !== 'object' || row === null)
|
|
87
|
+
continue;
|
|
88
|
+
const record = row;
|
|
89
|
+
if (record['gatewayVisible'] !== true)
|
|
90
|
+
continue;
|
|
91
|
+
if (!nonEmptyString(record['name']) || typeof record['shortForm'] !== 'string')
|
|
92
|
+
continue;
|
|
93
|
+
if (typeof record['kind'] !== 'string' || !MEMORY_KINDS.has(record['kind']))
|
|
94
|
+
continue;
|
|
95
|
+
if (typeof record['scope'] !== 'string' || !MEMORY_SCOPES.has(record['scope']))
|
|
96
|
+
continue;
|
|
97
|
+
out.push({
|
|
98
|
+
name: record['name'],
|
|
99
|
+
kind: record['kind'],
|
|
100
|
+
scope: record['scope'],
|
|
101
|
+
shortForm: record['shortForm'],
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** The optional fields a shipped extension registration may add so its command
|
|
2
|
+
* is disclosed to chat surfaces. Registrations cast their options through
|
|
3
|
+
* `ChatCapableRegistration` rather than widening pi's own types. */
|
|
4
|
+
export interface ChatCommandMetadata {
|
|
5
|
+
/** Present and exactly `true` to opt this command into chat disclosure. */
|
|
6
|
+
gateway?: true;
|
|
7
|
+
/** Argument shape shown beside the name, e.g. `'[args]'`. */
|
|
8
|
+
argumentHint?: string;
|
|
9
|
+
}
|
|
10
|
+
/** Registration options plus the preserved custom fields crouter reads back off
|
|
11
|
+
* the resolved command. `T` is `Parameters<ExtensionAPI['registerCommand']>[1]`
|
|
12
|
+
* at the call site. */
|
|
13
|
+
export type ChatCapableRegistration<T> = T & ChatCommandMetadata;
|
|
14
|
+
/** What the command disclosure needs to know about one command's chat surface. */
|
|
15
|
+
export interface ChatCommandSurface {
|
|
16
|
+
gatewayCapable: boolean;
|
|
17
|
+
argumentHint?: string;
|
|
18
|
+
}
|
|
19
|
+
/** Read the chat surface off a resolved extension command. `command` is an
|
|
20
|
+
* arbitrary runtime object from another package, so every field is probed
|
|
21
|
+
* rather than trusted. */
|
|
22
|
+
export declare function registrationChatSurface(command: unknown): ChatCommandSurface;
|
|
23
|
+
/** Capture prompt-template chat metadata immediately after pi loads or reloads
|
|
24
|
+
* its templates. The opt-in is the template file's own frontmatter; the hint
|
|
25
|
+
* is pi's already-parsed `argument-hint`. A missing file, unreadable path, or
|
|
26
|
+
* invalid YAML is not chat-capable. */
|
|
27
|
+
export declare function capturePromptTemplateChatSurfaces(templates: unknown): void;
|
|
28
|
+
/** Capture the Gateway capabilities declared by one completed resource load. */
|
|
29
|
+
export declare function captureChatCapabilities(input: {
|
|
30
|
+
commands: unknown;
|
|
31
|
+
templates: unknown;
|
|
32
|
+
}): void;
|
|
33
|
+
/** Capture every startup-declared command and prompt template a session
|
|
34
|
+
* currently exposes. A broken getter leaves that surface fail-closed. */
|
|
35
|
+
export declare function captureSessionChatCapabilities(session: unknown): void;
|
|
36
|
+
/** Read the chat surface captured for this loaded template. An uncaptured
|
|
37
|
+
* template is not disclosed to chat surfaces. */
|
|
38
|
+
export declare function templateChatSurface(template: unknown): ChatCommandSurface;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// command-surface.ts — crouter's own metadata about the commands the engine
|
|
2
|
+
// registers, and the one place that decides whether a command can complete its
|
|
3
|
+
// outcome from a non-terminal chat surface.
|
|
4
|
+
//
|
|
5
|
+
// Eligibility is OPT-IN and fail-closed. An extension registration declares
|
|
6
|
+
// `gateway: true`; a prompt template declares `gateway: true` in frontmatter.
|
|
7
|
+
// Anything else — a missing flag, a malformed value, an unreadable file — is
|
|
8
|
+
// not chat-capable. Capability is NEVER inferred from a command's name, its
|
|
9
|
+
// source, or a hard-coded list, because the command set is whatever the active
|
|
10
|
+
// VM registered, not a catalog crouter ships.
|
|
11
|
+
//
|
|
12
|
+
// pi keeps unknown registration fields on the resolved command object, so
|
|
13
|
+
// `gateway`/`argumentHint` ride along beside `expansion` without touching pi's
|
|
14
|
+
// global registration types. pi's prompt-template loader is the opposite: it
|
|
15
|
+
// parses `argument-hint` into `argumentHint` and DROPS every other frontmatter
|
|
16
|
+
// key. Capture `gateway` from the template file when the loader finishes, so
|
|
17
|
+
// the eligibility and loaded template content share the same reload boundary.
|
|
18
|
+
import { readFileSync } from 'node:fs';
|
|
19
|
+
import { parseFrontmatterGeneric } from '../frontmatter.js';
|
|
20
|
+
const NOT_CAPABLE = { gatewayCapable: false };
|
|
21
|
+
// One resource load declares Gateway capability by extension identity and pi's
|
|
22
|
+
// collision-resolved invocation name. Later registrations from that extension
|
|
23
|
+
// retain only names the load declared.
|
|
24
|
+
let startupGatewayNames = new WeakMap();
|
|
25
|
+
// Pi drops unknown prompt frontmatter keys. This cache restores crouter's
|
|
26
|
+
// metadata without re-reading a template during command disclosure.
|
|
27
|
+
const templateChatSurfaces = new WeakMap();
|
|
28
|
+
function hintOf(value) {
|
|
29
|
+
return typeof value === 'string' && value.trim() !== '' ? { argumentHint: value } : {};
|
|
30
|
+
}
|
|
31
|
+
/** Read the chat surface off a resolved extension command. `command` is an
|
|
32
|
+
* arbitrary runtime object from another package, so every field is probed
|
|
33
|
+
* rather than trusted. */
|
|
34
|
+
export function registrationChatSurface(command) {
|
|
35
|
+
if (typeof command !== 'object' || command === null)
|
|
36
|
+
return NOT_CAPABLE;
|
|
37
|
+
const record = command;
|
|
38
|
+
const { sourceInfo, invocationName } = record;
|
|
39
|
+
if (typeof sourceInfo !== 'object' ||
|
|
40
|
+
sourceInfo === null ||
|
|
41
|
+
typeof invocationName !== 'string' ||
|
|
42
|
+
!startupGatewayNames.get(sourceInfo)?.has(invocationName))
|
|
43
|
+
return NOT_CAPABLE;
|
|
44
|
+
return { gatewayCapable: true, ...hintOf(record['argumentHint']) };
|
|
45
|
+
}
|
|
46
|
+
function captureExtensionCommandSurfaces(commands) {
|
|
47
|
+
startupGatewayNames = new WeakMap();
|
|
48
|
+
if (!Array.isArray(commands))
|
|
49
|
+
return;
|
|
50
|
+
for (const command of commands) {
|
|
51
|
+
if (typeof command !== 'object' || command === null)
|
|
52
|
+
continue;
|
|
53
|
+
const record = command;
|
|
54
|
+
const { sourceInfo, invocationName } = record;
|
|
55
|
+
if (record['gateway'] !== true ||
|
|
56
|
+
typeof sourceInfo !== 'object' ||
|
|
57
|
+
sourceInfo === null ||
|
|
58
|
+
typeof invocationName !== 'string')
|
|
59
|
+
continue;
|
|
60
|
+
const names = startupGatewayNames.get(sourceInfo) ?? new Set();
|
|
61
|
+
names.add(invocationName);
|
|
62
|
+
startupGatewayNames.set(sourceInfo, names);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/** Capture prompt-template chat metadata immediately after pi loads or reloads
|
|
66
|
+
* its templates. The opt-in is the template file's own frontmatter; the hint
|
|
67
|
+
* is pi's already-parsed `argument-hint`. A missing file, unreadable path, or
|
|
68
|
+
* invalid YAML is not chat-capable. */
|
|
69
|
+
export function capturePromptTemplateChatSurfaces(templates) {
|
|
70
|
+
if (!Array.isArray(templates))
|
|
71
|
+
return;
|
|
72
|
+
for (const template of templates) {
|
|
73
|
+
if (typeof template !== 'object' || template === null)
|
|
74
|
+
continue;
|
|
75
|
+
const { filePath, argumentHint } = template;
|
|
76
|
+
let surface = NOT_CAPABLE;
|
|
77
|
+
if (typeof filePath === 'string' && filePath !== '') {
|
|
78
|
+
try {
|
|
79
|
+
const frontmatter = parseFrontmatterGeneric(readFileSync(filePath, 'utf8')).data;
|
|
80
|
+
if (frontmatter?.['gateway'] === true)
|
|
81
|
+
surface = { gatewayCapable: true, ...hintOf(argumentHint) };
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// Fail closed for malformed or unreadable template metadata.
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
templateChatSurfaces.set(template, surface);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** Capture the Gateway capabilities declared by one completed resource load. */
|
|
91
|
+
export function captureChatCapabilities(input) {
|
|
92
|
+
captureExtensionCommandSurfaces(input.commands);
|
|
93
|
+
capturePromptTemplateChatSurfaces(input.templates);
|
|
94
|
+
}
|
|
95
|
+
/** Capture every startup-declared command and prompt template a session
|
|
96
|
+
* currently exposes. A broken getter leaves that surface fail-closed. */
|
|
97
|
+
export function captureSessionChatCapabilities(session) {
|
|
98
|
+
try {
|
|
99
|
+
if (typeof session !== 'object' || session === null)
|
|
100
|
+
return;
|
|
101
|
+
const value = session;
|
|
102
|
+
captureChatCapabilities({
|
|
103
|
+
commands: value.extensionRunner?.getRegisteredCommands?.(),
|
|
104
|
+
templates: value.promptTemplates,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// Fail closed when an engine cannot expose its loaded resources.
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/** Read the chat surface captured for this loaded template. An uncaptured
|
|
112
|
+
* template is not disclosed to chat surfaces. */
|
|
113
|
+
export function templateChatSurface(template) {
|
|
114
|
+
if (typeof template !== 'object' || template === null)
|
|
115
|
+
return NOT_CAPABLE;
|
|
116
|
+
return templateChatSurfaces.get(template) ?? NOT_CAPABLE;
|
|
117
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { CreateNodeRequest } from '../../api/dto/nodes.js';
|
|
2
|
+
import type { Mode } from '../canvas/types.js';
|
|
3
|
+
import { type ProfileEntry } from '../profiles/manifest.js';
|
|
4
|
+
import { buildLaunchSpecAsync } from './launch.js';
|
|
5
|
+
export interface ResolvedLaunchTarget {
|
|
6
|
+
profileId: string | null;
|
|
7
|
+
profile: ProfileEntry | null;
|
|
8
|
+
kind: string;
|
|
9
|
+
mode: Mode;
|
|
10
|
+
cwd: string;
|
|
11
|
+
launch: Awaited<ReturnType<typeof buildLaunchSpecAsync>>['launch'];
|
|
12
|
+
}
|
|
13
|
+
export declare function resolveCreateKind(kind: string | undefined, profile: ProfileEntry | null): string;
|
|
14
|
+
/** Validate a kind against the same cwd/profile configuration used to build its launch.
|
|
15
|
+
* Resolution is subprocess-isolated: crtrd is single-threaded and a config read can
|
|
16
|
+
* block indefinitely on a network filesystem. */
|
|
17
|
+
export declare function assertLaunchKind(kind: string, cwd?: string, profileId?: string | null): Promise<void>;
|
|
18
|
+
/** Resolve the exact profile, kind, cwd, mode and launch recipe a create will use. */
|
|
19
|
+
export declare function resolveLaunchTarget(req: CreateNodeRequest, options?: {
|
|
20
|
+
readOnlyProfileSelection?: boolean;
|
|
21
|
+
/** Preserve an existing caller's validation boundary before target resolution. */
|
|
22
|
+
validateKind?: (kind: string) => void | Promise<void>;
|
|
23
|
+
}): Promise<ResolvedLaunchTarget>;
|
|
24
|
+
/** The directory a new node is pinned to. */
|
|
25
|
+
export declare function resolveNodeCwd(req: CreateNodeRequest, parent: string | undefined, profileId: string | null, contextCwd: string): string;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { getNode } from '../canvas/canvas.js';
|
|
3
|
+
import { usage } from '../errors.js';
|
|
4
|
+
import { assertProfileActive, profileHome } from '../profiles/manifest.js';
|
|
5
|
+
import { buildLaunchSpecAsync, readMergedLaunchConfigAsync } from './launch.js';
|
|
6
|
+
import { resolveProfileId } from './spawn.js';
|
|
7
|
+
export function resolveCreateKind(kind, profile) {
|
|
8
|
+
return kind ?? profile?.manifest.default_kind ?? 'general';
|
|
9
|
+
}
|
|
10
|
+
/** Validate a kind against the same cwd/profile configuration used to build its launch.
|
|
11
|
+
* Resolution is subprocess-isolated: crtrd is single-threaded and a config read can
|
|
12
|
+
* block indefinitely on a network filesystem. */
|
|
13
|
+
export async function assertLaunchKind(kind, cwd = process.cwd(), profileId = process.env['CRTR_PROFILE_ID'] || null) {
|
|
14
|
+
const kinds = Object.keys((await readMergedLaunchConfigAsync(cwd, profileId)).kinds);
|
|
15
|
+
if (!kinds.includes(kind)) {
|
|
16
|
+
throw usage(`unknown kind: ${kind}`, { received: kind, next: `Valid kinds: ${kinds.sort().join(', ')}.` });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Resolve the exact profile, kind, cwd, mode and launch recipe a create will use. */
|
|
20
|
+
export async function resolveLaunchTarget(req, options = {}) {
|
|
21
|
+
const parent = req.parent ?? undefined;
|
|
22
|
+
const contextCwd = req.cwd ?? process.cwd();
|
|
23
|
+
const profileId = await resolveProfileId(req.profile, parent ?? null, contextCwd, {
|
|
24
|
+
readOnlySelection: options.readOnlyProfileSelection,
|
|
25
|
+
});
|
|
26
|
+
const profile = assertProfileActive(profileId);
|
|
27
|
+
const kind = resolveCreateKind(req.kind, profile);
|
|
28
|
+
await options.validateKind?.(kind);
|
|
29
|
+
const cwd = resolveNodeCwd({ ...req, kind }, parent, profileId, contextCwd);
|
|
30
|
+
if (options.validateKind === undefined)
|
|
31
|
+
await assertLaunchKind(kind, cwd, profileId);
|
|
32
|
+
const mode = (req.mode ?? 'base');
|
|
33
|
+
const launch = (await buildLaunchSpecAsync(kind, mode, {
|
|
34
|
+
lifecycle: req.root === true ? (req.root_lifecycle ?? 'resident') : 'terminal',
|
|
35
|
+
hasManager: req.root !== true,
|
|
36
|
+
...(req.model !== undefined ? { model: req.model } : {}),
|
|
37
|
+
cwd,
|
|
38
|
+
profileId,
|
|
39
|
+
})).launch;
|
|
40
|
+
return { profileId, profile, kind, mode, cwd, launch };
|
|
41
|
+
}
|
|
42
|
+
/** The directory a new node is pinned to. */
|
|
43
|
+
export function resolveNodeCwd(req, parent, profileId, contextCwd) {
|
|
44
|
+
if (req.pin_cwd !== undefined && req.pin_cwd !== '')
|
|
45
|
+
return resolve(req.pin_cwd);
|
|
46
|
+
if (parent != null) {
|
|
47
|
+
const spawner = getNode(parent);
|
|
48
|
+
if (spawner !== null)
|
|
49
|
+
return spawner.cwd;
|
|
50
|
+
}
|
|
51
|
+
if (req.cwd !== undefined && req.cwd !== '')
|
|
52
|
+
return resolve(req.cwd);
|
|
53
|
+
return profileHome(profileId) ?? contextCwd;
|
|
54
|
+
}
|
|
@@ -13,6 +13,7 @@ import { basename, join } from 'node:path';
|
|
|
13
13
|
import { InputError } from '../io.js';
|
|
14
14
|
import { fullName, getNode } from '../canvas/index.js';
|
|
15
15
|
import { jobDir, nodeDir, sessionPtrPath } from '../canvas/paths.js';
|
|
16
|
+
import { hydratePersistedAdvertisedCommandMessages } from './advertised-command-invocation.js';
|
|
16
17
|
import { SessionManager } from './broker-sdk.js';
|
|
17
18
|
import { openModelRegistry } from './model-registry.js';
|
|
18
19
|
import { BUILTIN_SLASH_COMMANDS } from './pi-vendored.js';
|
|
@@ -153,7 +154,11 @@ async function reconstructVisibleMessages(nodeId) {
|
|
|
153
154
|
copyFileSync(sessionFile, copy);
|
|
154
155
|
const manager = SessionManager.open(copy);
|
|
155
156
|
const context = manager.buildSessionContext();
|
|
157
|
+
hydratePersistedAdvertisedCommandMessages(context.messages);
|
|
156
158
|
const reconstructed = cycleAwareMessages(manager);
|
|
159
|
+
if (reconstructed !== undefined) {
|
|
160
|
+
hydratePersistedAdvertisedCommandMessages(reconstructed.map(({ message }) => message));
|
|
161
|
+
}
|
|
157
162
|
const identified = reconstructed === undefined
|
|
158
163
|
? undefined
|
|
159
164
|
: visibleMessages(reconstructed, { boundaryReviewId: node.review_binding?.review_id });
|
|
@@ -79,15 +79,15 @@ function baseModeGuidance() {
|
|
|
79
79
|
}
|
|
80
80
|
// The lifecycle transition prose is the SAME contract that's baked into the
|
|
81
81
|
// static system prompt at birth — so both load from the one source
|
|
82
|
-
// (`
|
|
82
|
+
// (`turn-lifecycle/{terminal,resident}.md`) and can never drift. The flip
|
|
83
83
|
// only re-delivers that fragment as the node's new-state steer.
|
|
84
84
|
/** terminal → resident: interactable, never forced to submit. */
|
|
85
85
|
function residentLifecycleGuidance() {
|
|
86
|
-
return loadMemoryBody('lifecycle/resident');
|
|
86
|
+
return loadMemoryBody('turn-lifecycle/resident');
|
|
87
87
|
}
|
|
88
88
|
/** resident → terminal: owes a final, reaps when done. */
|
|
89
89
|
function terminalLifecycleGuidance() {
|
|
90
|
-
return loadMemoryBody('lifecycle/terminal');
|
|
90
|
+
return loadMemoryBody('turn-lifecycle/terminal');
|
|
91
91
|
}
|
|
92
92
|
/** Build the injected transition prompt for a `from → to` persona change.
|
|
93
93
|
* Concatenates the relevant section per changed axis (both when both changed).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { DefaultResourceLoader, ExtensionRunner, getAgentDir, } from '@earendil-works/pi-coding-agent';
|
|
3
|
+
import { buildRefInventory } from '../memory/inline-ref-inventory.js';
|
|
4
|
+
import { gatewayCommandRows, gatewayMemoryRows } from './chat-inventory-rows.js';
|
|
5
|
+
import { captureChatCapabilities, registrationChatSurface, templateChatSurface, } from './command-surface.js';
|
|
6
|
+
function parseInput() {
|
|
7
|
+
const parsed = JSON.parse(readFileSync(0, 'utf8'));
|
|
8
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
9
|
+
throw new Error('preflight input must be an object');
|
|
10
|
+
const extensions = parsed.extensions;
|
|
11
|
+
if (!Array.isArray(extensions) || !extensions.every((value) => typeof value === 'string')) {
|
|
12
|
+
throw new Error('preflight input extensions must be strings');
|
|
13
|
+
}
|
|
14
|
+
return { extensions };
|
|
15
|
+
}
|
|
16
|
+
async function main() {
|
|
17
|
+
const { extensions } = parseInput();
|
|
18
|
+
const cwd = process.cwd();
|
|
19
|
+
const loader = new DefaultResourceLoader({
|
|
20
|
+
cwd,
|
|
21
|
+
agentDir: getAgentDir(),
|
|
22
|
+
additionalExtensionPaths: extensions,
|
|
23
|
+
noContextFiles: true,
|
|
24
|
+
});
|
|
25
|
+
await loader.reload();
|
|
26
|
+
const loaded = loader.getExtensions();
|
|
27
|
+
const templates = loader.getPrompts().prompts;
|
|
28
|
+
// getRegisteredCommands only reads extension maps and applies pi's collision
|
|
29
|
+
// naming. The unbound placeholders make every action path unavailable.
|
|
30
|
+
const runner = new ExtensionRunner(loaded.extensions, loaded.runtime, cwd, undefined, undefined);
|
|
31
|
+
const commands = runner.getRegisteredCommands();
|
|
32
|
+
captureChatCapabilities({ commands, templates });
|
|
33
|
+
const disclosures = [
|
|
34
|
+
...commands.map((command) => {
|
|
35
|
+
const expansion = command.expansion;
|
|
36
|
+
return {
|
|
37
|
+
name: command.invocationName,
|
|
38
|
+
description: command.description ?? '',
|
|
39
|
+
source: 'command',
|
|
40
|
+
...(expansion !== undefined ? { expansion } : {}),
|
|
41
|
+
...registrationChatSurface(command),
|
|
42
|
+
};
|
|
43
|
+
}),
|
|
44
|
+
...templates.map((template) => ({
|
|
45
|
+
name: template.name,
|
|
46
|
+
description: template.description,
|
|
47
|
+
source: 'template',
|
|
48
|
+
template: template.content,
|
|
49
|
+
...templateChatSurface(template),
|
|
50
|
+
})),
|
|
51
|
+
];
|
|
52
|
+
const memory = buildRefInventory();
|
|
53
|
+
writeFileSync(3, `${JSON.stringify({
|
|
54
|
+
commands: gatewayCommandRows(disclosures),
|
|
55
|
+
memory_refs: gatewayMemoryRows(memory.refs),
|
|
56
|
+
})}\n`);
|
|
57
|
+
}
|
|
58
|
+
main().catch((error) => {
|
|
59
|
+
process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
|
|
60
|
+
process.exitCode = 1;
|
|
61
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ChatInventoryCommandDTO, ChatInventoryMemoryRefDTO } from '../../api/dto/chat-inventory.js';
|
|
2
|
+
export interface ProspectiveInventoryRows {
|
|
3
|
+
commands: ChatInventoryCommandDTO[];
|
|
4
|
+
memory_refs: ChatInventoryMemoryRefDTO[];
|
|
5
|
+
}
|
|
6
|
+
export declare function runProspectiveInventory(input: {
|
|
7
|
+
cwd: string;
|
|
8
|
+
profileId: string | null;
|
|
9
|
+
extensions: string[];
|
|
10
|
+
}): Promise<ProspectiveInventoryRows>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { chatInventoryCommandRows, chatInventoryMemoryRows } from './chat-inventory-rows.js';
|
|
5
|
+
import { FRONT_DOOR_ENV } from './front-door-env.js';
|
|
6
|
+
const PREFLIGHT_TIMEOUT_MS = 15_000;
|
|
7
|
+
function entryPath() {
|
|
8
|
+
return join(dirname(fileURLToPath(import.meta.url)), 'prospective-inventory-cli.js');
|
|
9
|
+
}
|
|
10
|
+
function parseDocument(response) {
|
|
11
|
+
const parsed = JSON.parse(response.trim());
|
|
12
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
13
|
+
throw new Error('preflight output must be an object');
|
|
14
|
+
const record = parsed;
|
|
15
|
+
if (!Array.isArray(record['commands']) || !Array.isArray(record['memory_refs'])) {
|
|
16
|
+
throw new Error('preflight output must contain command and memory-ref arrays');
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
commands: chatInventoryCommandRows(record['commands']),
|
|
20
|
+
memory_refs: chatInventoryMemoryRows(record['memory_refs']),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function runProspectiveInventory(input) {
|
|
24
|
+
return new Promise((resolve, reject) => {
|
|
25
|
+
const env = {
|
|
26
|
+
...process.env,
|
|
27
|
+
CRTR_PROFILE_ID: input.profileId ?? '',
|
|
28
|
+
[FRONT_DOOR_ENV]: '1',
|
|
29
|
+
};
|
|
30
|
+
delete env['CRTR_NODE_ID'];
|
|
31
|
+
const child = spawn(process.execPath, [entryPath()], {
|
|
32
|
+
cwd: input.cwd,
|
|
33
|
+
env,
|
|
34
|
+
stdio: ['pipe', 'pipe', 'pipe', 'pipe'],
|
|
35
|
+
});
|
|
36
|
+
let stdout = '';
|
|
37
|
+
let stderr = '';
|
|
38
|
+
let response = '';
|
|
39
|
+
let settled = false;
|
|
40
|
+
const timer = setTimeout(() => {
|
|
41
|
+
if (settled)
|
|
42
|
+
return;
|
|
43
|
+
settled = true;
|
|
44
|
+
child.kill('SIGKILL');
|
|
45
|
+
reject(new Error('prospective inventory preflight timed out'));
|
|
46
|
+
}, PREFLIGHT_TIMEOUT_MS);
|
|
47
|
+
timer.unref?.();
|
|
48
|
+
child.stdout.setEncoding('utf8');
|
|
49
|
+
child.stderr.setEncoding('utf8');
|
|
50
|
+
const responseStream = child.stdio[3];
|
|
51
|
+
if (responseStream === null || responseStream === undefined || !('setEncoding' in responseStream)) {
|
|
52
|
+
clearTimeout(timer);
|
|
53
|
+
child.kill('SIGKILL');
|
|
54
|
+
reject(new Error('prospective inventory preflight response channel unavailable'));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
responseStream.setEncoding('utf8');
|
|
58
|
+
child.stdout.on('data', (chunk) => { stdout += chunk; });
|
|
59
|
+
child.stderr.on('data', (chunk) => { stderr += chunk; });
|
|
60
|
+
responseStream.on('data', (chunk) => { response += chunk; });
|
|
61
|
+
child.on('error', (error) => {
|
|
62
|
+
if (settled)
|
|
63
|
+
return;
|
|
64
|
+
settled = true;
|
|
65
|
+
clearTimeout(timer);
|
|
66
|
+
reject(error);
|
|
67
|
+
});
|
|
68
|
+
child.on('close', (code, signal) => {
|
|
69
|
+
if (settled)
|
|
70
|
+
return;
|
|
71
|
+
settled = true;
|
|
72
|
+
clearTimeout(timer);
|
|
73
|
+
const diagnostics = [stderr.trim(), stdout.trim()].filter(Boolean).join('\n');
|
|
74
|
+
if (code !== 0) {
|
|
75
|
+
reject(new Error(`prospective inventory preflight exited ${code ?? signal ?? 'unknown'}${diagnostics === '' ? '' : `: ${diagnostics}`}`));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
resolve(parseDocument(response));
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
83
|
+
reject(new Error(`${message}${diagnostics === '' ? '' : `: ${diagnostics}`}`));
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
child.stdin.end(JSON.stringify({ extensions: input.extensions }));
|
|
87
|
+
});
|
|
88
|
+
}
|
|
@@ -88,7 +88,9 @@ export declare function resolveForkSource(value: string): string;
|
|
|
88
88
|
* shell, not from inside a node — there is no profile to inherit, so this runs
|
|
89
89
|
* the same startup selector the front door uses (MRU covering `cwd`, else the
|
|
90
90
|
* root-profile prompt/default). */
|
|
91
|
-
export declare function resolveProfileId(explicit: string | null | undefined, spawner: string | null, cwd: string
|
|
91
|
+
export declare function resolveProfileId(explicit: string | null | undefined, spawner: string | null, cwd: string, options?: {
|
|
92
|
+
readOnlySelection?: boolean;
|
|
93
|
+
}): Promise<string | null>;
|
|
92
94
|
/** Reject a permanently invalid launch recipe against the registry that will
|
|
93
95
|
* serve it. Registered-but-unauthenticated/cooling requests intentionally pass
|
|
94
96
|
* this authoring check so broker policy for transient availability is unchanged. */
|
|
@@ -13,7 +13,7 @@ import { accessSync, constants, cpSync, readdirSync, existsSync, rmSync } from '
|
|
|
13
13
|
import { isAbsolute, resolve, join } from 'node:path';
|
|
14
14
|
import { spawnNode, currentNodeContext, rootOfSpine, newNodeId, preflightNodeId } from './nodes.js';
|
|
15
15
|
import { loadProfileManifest } from '../profiles/manifest.js';
|
|
16
|
-
import { selectProfileForCwd } from '../profiles/select.js';
|
|
16
|
+
import { selectProfileForCwd, selectProfileForCwdReadOnly } from '../profiles/select.js';
|
|
17
17
|
import { buildLaunchSpecAsync, buildPiArgv } from './launch.js';
|
|
18
18
|
import { createManagedWorktree, rollbackManagedWorktree } from '../worktree.js';
|
|
19
19
|
import { usage, brokerLaunchFailed } from '../errors.js';
|
|
@@ -153,7 +153,7 @@ export function resolveForkSource(value) {
|
|
|
153
153
|
* shell, not from inside a node — there is no profile to inherit, so this runs
|
|
154
154
|
* the same startup selector the front door uses (MRU covering `cwd`, else the
|
|
155
155
|
* root-profile prompt/default). */
|
|
156
|
-
export async function resolveProfileId(explicit, spawner, cwd) {
|
|
156
|
+
export async function resolveProfileId(explicit, spawner, cwd, options = {}) {
|
|
157
157
|
if (explicit === null)
|
|
158
158
|
return null;
|
|
159
159
|
if (explicit !== undefined && explicit !== '') {
|
|
@@ -161,7 +161,9 @@ export async function resolveProfileId(explicit, spawner, cwd) {
|
|
|
161
161
|
}
|
|
162
162
|
if (spawner !== null)
|
|
163
163
|
return getNode(spawner)?.profile_id ?? null;
|
|
164
|
-
return
|
|
164
|
+
return options.readOnlySelection === true
|
|
165
|
+
? selectProfileForCwdReadOnly(cwd)
|
|
166
|
+
: selectProfileForCwd(cwd);
|
|
165
167
|
}
|
|
166
168
|
function requestFromLaunch(launch) {
|
|
167
169
|
const intent = launch.modelIntent === undefined
|
package/dist/core/scope.d.ts
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import type { InstalledPlugin, Scope } from '../types.js';
|
|
2
|
+
import type { ProfileProjectMemory } from '../api/dto/profiles.js';
|
|
3
|
+
/** The ceiling a project root carries when no profile relationship contributes
|
|
4
|
+
* it — the top rung, i.e. no cap at all. */
|
|
5
|
+
export declare const NEUTRAL_PROJECT_MEMORY: ProfileProjectMemory;
|
|
6
|
+
/** One resolved project scope root plus the selected profile relationship's
|
|
7
|
+
* memory ceiling for it. The ceiling is INDEPENDENT of how the root was
|
|
8
|
+
* discovered: a root reached by both cwd and a profile entry still takes that
|
|
9
|
+
* entry's value (first entry in manifest order wins). */
|
|
10
|
+
export interface ProjectScopeRoot {
|
|
11
|
+
/** Absolute path of the `.crouter/` dir. */
|
|
12
|
+
root: string;
|
|
13
|
+
memory: ProfileProjectMemory;
|
|
14
|
+
}
|
|
2
15
|
export declare function builtinMemoryRoot(): string;
|
|
3
16
|
export declare function builtinPiPackagesDir(): string;
|
|
4
17
|
export declare function builtinPiPackageDir(name: string): string;
|
|
@@ -15,10 +28,22 @@ export declare function findProjectScopeRoot(startDir?: string): string | null;
|
|
|
15
28
|
* selected profile (`CRTR_PROFILE_ID`); a null/unresolvable profile collapses
|
|
16
29
|
* to the single `[startDir]` pointer, reproducing today's behavior exactly.
|
|
17
30
|
* Results are cached per (resolved startDir, profileId) — `resetScopeCache()`
|
|
18
|
-
* invalidates on any profile mutation that can change project dirs or names.
|
|
31
|
+
* invalidates on any profile mutation that can change project dirs or names.
|
|
32
|
+
*
|
|
33
|
+
* Each root also carries the selected profile relationship's memory ceiling
|
|
34
|
+
* (`findProjectScopeRootsWithMemory`), a projection layered onto this order
|
|
35
|
+
* rather than part of it: discovery provenance decides precedence, the
|
|
36
|
+
* relationship decides the ceiling, and the two never influence each other. */
|
|
19
37
|
export declare function findProjectScopeRoots(startDir?: string, profileId?: string | null): string[];
|
|
38
|
+
/** `findProjectScopeRoots` with each root's profile memory ceiling attached —
|
|
39
|
+
* same roots, same order. A root contributed by a profile entry's ancestor
|
|
40
|
+
* walk takes that entry's `memory` (first entry in manifest order wins, even
|
|
41
|
+
* when cwd also reaches the root); every other root takes the neutral
|
|
42
|
+
* `content` ceiling. */
|
|
43
|
+
export declare function findProjectScopeRootsWithMemory(startDir?: string, profileId?: string | null): ProjectScopeRoot[];
|
|
20
44
|
export declare function projectScopeRoot(startDir?: string): string | null;
|
|
21
45
|
export declare function projectScopeRoots(startDir?: string, profileId?: string | null): string[];
|
|
46
|
+
export declare function projectScopeRootsWithMemory(startDir?: string, profileId?: string | null): ProjectScopeRoot[];
|
|
22
47
|
export declare function scopeRoot(scope: Scope): string | null;
|
|
23
48
|
export declare function requireScopeRoot(scope: Scope): string;
|
|
24
49
|
export declare function ensureProjectScopeRoot(startDir?: string): string;
|