@north-light/crouter 0.3.221 → 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 +21 -1
- package/dist/api/client.js +34 -0
- package/dist/api/dto/chat-inventory.d.ts +13 -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/reviews.d.ts +2 -0
- 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 +10 -0
- 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/plugins.md +10 -1
- package/dist/builtin-memory/internal/storage-tiers.md +1 -1
- package/dist/builtin-memory/plan/roadmap.md +6 -28
- package/dist/builtin-memory/spec/guide.md +19 -8
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/memory-slash-commands.ts +28 -15
- 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/viewer.js +525 -523
- package/dist/clients/inbox/controller.js +1 -1
- package/dist/clients/inbox/resolve.d.ts +1 -0
- package/dist/clients/inbox/review/review-client.js +3 -1
- 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/sys/config.js +2 -2
- package/dist/commands/sys/doctor.js +54 -2
- package/dist/core/__tests__/broker-extension-canvas-db-boundary.test.js +7 -4
- package/dist/core/__tests__/fixtures/memory-slash-live-probe.d.ts +1 -0
- 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 +1 -1
- package/dist/core/__tests__/profile-project-memory-delivery.test.js +1 -1
- 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/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/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/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/memory-resolver.js +1 -1
- package/dist/core/profiles/select.d.ts +2 -0
- package/dist/core/profiles/select.js +21 -4
- package/dist/core/runtime/broker/frame-dispatch.js +2 -5
- package/dist/core/runtime/broker-inventory.d.ts +1 -2
- package/dist/core/runtime/broker-inventory.js +2 -77
- package/dist/core/runtime/broker-persona-guidance.js +1 -1
- package/dist/core/runtime/broker.js +4 -4
- 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 +8 -3
- package/dist/core/runtime/command-surface.js +42 -6
- package/dist/core/runtime/launch-target.d.ts +25 -0
- package/dist/core/runtime/launch-target.js +54 -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/substrate/on-read.js +17 -28
- 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 +51 -19
- package/dist/core/substrate/schema.d.ts +5 -1
- package/dist/core/substrate/schema.js +4 -4
- 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 +52 -3
- 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/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/server.js +4 -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/types.d.ts +8 -0
- 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/builtin-memory/{02-lifecycle/00-terminal.md → 02-turn-lifecycle/01-terminal.md} +0 -0
|
@@ -9,6 +9,8 @@ export declare function resolveProfileSearchAction(bindings: BindingResolution<B
|
|
|
9
9
|
* `crtr profile default set`, which rejects pinning a profile that doesn't
|
|
10
10
|
* cover cwd (the selector would ignore/self-heal such a pin anyway). */
|
|
11
11
|
export declare function profileCoversCwd(entry: ProfileEntry, cwd: string): boolean;
|
|
12
|
+
/** Resolve the headless selector against existing state without changing it. */
|
|
13
|
+
export declare function selectProfileForCwdReadOnly(cwd: string): string;
|
|
12
14
|
/** Select the profile a node about to boot at `cwd` should run under.
|
|
13
15
|
*
|
|
14
16
|
* 1. `explicitProfile` present → resolve id/name via `loadProfileManifest`. If
|
|
@@ -93,6 +93,26 @@ function isMoreRecent(a, b) {
|
|
|
93
93
|
function pickMostRecent(entries) {
|
|
94
94
|
return entries.reduce((best, cur) => isMoreRecent(cur.manifest.last_used_at, best.manifest.last_used_at) ? cur : best);
|
|
95
95
|
}
|
|
96
|
+
function profileSelectionState(cwd) {
|
|
97
|
+
const resolvedCwd = resolveCwd(cwd);
|
|
98
|
+
const covering = listProfiles().filter((profile) => profileCoversCwd(profile, resolvedCwd));
|
|
99
|
+
const exact = covering.filter((profile) => profile.manifest.projects.some((project) => project.path === resolvedCwd));
|
|
100
|
+
const pinnedId = getDefaultProfileId(resolvedCwd);
|
|
101
|
+
const pinned = pinnedId === null ? null : covering.find((profile) => profile.profileId === pinnedId) ?? null;
|
|
102
|
+
return { covering, exact, pinnedId, pinned };
|
|
103
|
+
}
|
|
104
|
+
/** Resolve the headless selector against existing state without changing it. */
|
|
105
|
+
export function selectProfileForCwdReadOnly(cwd) {
|
|
106
|
+
const { covering, exact, pinned } = profileSelectionState(cwd);
|
|
107
|
+
if (pinned !== null)
|
|
108
|
+
return pinned.profileId;
|
|
109
|
+
if (exact.length === 1)
|
|
110
|
+
return exact[0].profileId;
|
|
111
|
+
const pool = exact.length > 0 ? exact : covering;
|
|
112
|
+
if (pool.length > 0)
|
|
113
|
+
return pickMostRecent(pool).profileId;
|
|
114
|
+
return ROOT_PROFILE_ID;
|
|
115
|
+
}
|
|
96
116
|
function isInteractive() {
|
|
97
117
|
return Boolean(process.stdin.isTTY) && Boolean(process.stdout.isTTY);
|
|
98
118
|
}
|
|
@@ -620,14 +640,11 @@ export async function selectProfileForCwd(cwd, explicitProfile, forcePicker = fa
|
|
|
620
640
|
updateProfileLastUsed(entry.profileId);
|
|
621
641
|
return entry.profileId;
|
|
622
642
|
}
|
|
623
|
-
const covering
|
|
624
|
-
const exact = covering.filter((p) => p.manifest.projects.some((proj) => proj.path === resolvedCwd));
|
|
643
|
+
const { covering, exact, pinnedId, pinned } = profileSelectionState(resolvedCwd);
|
|
625
644
|
// A per-cwd pinned default (set from the menu with `d`) is the user's
|
|
626
645
|
// explicit "default to THIS profile HERE". It outranks both the exact-single
|
|
627
646
|
// auto-pick and global-MRU, so long as it still covers cwd; a stale pin
|
|
628
647
|
// (profile deleted, or its dir removed from purview) is self-healed away.
|
|
629
|
-
const pinnedId = getDefaultProfileId(resolvedCwd);
|
|
630
|
-
const pinned = pinnedId !== null ? covering.find((p) => p.profileId === pinnedId) ?? null : null;
|
|
631
648
|
if (pinnedId !== null && pinned === null)
|
|
632
649
|
clearDefaultProfile(resolvedCwd);
|
|
633
650
|
// A valid pin decides it outright and STOPS ASKING — the whole point of
|
|
@@ -9,7 +9,7 @@ import { operationIdContext } from '../../events/operation-id.js';
|
|
|
9
9
|
import { buildRefInventory } from '../../memory/inline-ref-inventory.js';
|
|
10
10
|
import { buildGuidance } from '../../memory/inline-ref-guidance.js';
|
|
11
11
|
import { clearSessionCache } from '../../substrate/session-cache.js';
|
|
12
|
-
import {
|
|
12
|
+
import { captureSessionChatCapabilities } from '../command-surface.js';
|
|
13
13
|
import { promptWithAdvertisedCommandInvocation } from '../advertised-command-invocation.js';
|
|
14
14
|
export { promptWithAdvertisedCommandInvocation } from '../advertised-command-invocation.js';
|
|
15
15
|
import { configuredLadderProviders, isProviderPinnedModelToken, nextLadderModel, normalizeModel, } from '../launch.js';
|
|
@@ -1210,7 +1210,7 @@ export function createFrameDispatchContext(deps) {
|
|
|
1210
1210
|
// cached docs. A FAILED reload (`.catch`) leaves both untouched: nothing
|
|
1211
1211
|
// about the corpus is known to have changed.
|
|
1212
1212
|
void currentSession()
|
|
1213
|
-
.reload()
|
|
1213
|
+
.reload({ beforeSessionStart: () => captureSessionChatCapabilities(currentSession()) })
|
|
1214
1214
|
.then(() => {
|
|
1215
1215
|
// pi's reload() re-syncs queue modes from the shared settings.json
|
|
1216
1216
|
// (its ONE post-bind syncQueueModesFromSettings call site) —
|
|
@@ -1221,9 +1221,6 @@ export function createFrameDispatchContext(deps) {
|
|
|
1221
1221
|
agent.steeringMode = 'all';
|
|
1222
1222
|
agent.followUpMode = 'all';
|
|
1223
1223
|
}
|
|
1224
|
-
// `session.reload()` has replaced pi's loaded template objects;
|
|
1225
|
-
// capture their crouter-only metadata before any observer refresh.
|
|
1226
|
-
captureSessionPromptTemplateChatSurfaces(currentSession());
|
|
1227
1224
|
memoizedRefInventory = null;
|
|
1228
1225
|
clearSessionCache();
|
|
1229
1226
|
registry.forEachHelloed((observer) => {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { ChatInventoryDTO
|
|
2
|
-
export declare function gatewayMemoryRows(refs: unknown): ChatInventoryMemoryRefDTO[];
|
|
1
|
+
import type { ChatInventoryDTO } from '../../api/dto/chat-inventory.js';
|
|
3
2
|
/** Collect the chat inventories for one node. Always resolves — every failure
|
|
4
3
|
* mode is expressed in the returned document, not thrown. */
|
|
5
4
|
export declare function readChatInventory(nodeId: string): Promise<ChatInventoryDTO>;
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
import { randomUUID } from 'node:crypto';
|
|
25
25
|
import { getNode } from '../canvas/canvas.js';
|
|
26
26
|
import { BrokerClient } from '../broker-client/index.js';
|
|
27
|
-
import {
|
|
27
|
+
import { gatewayCommandRowsFromDetail, gatewayMemoryRows } from './chat-inventory-rows.js';
|
|
28
28
|
import { isBrokerLive } from './model-swap.js';
|
|
29
29
|
/** Same budget as `broker-request.ts`: this is local engine work, so it is
|
|
30
30
|
* generous slack rather than a tuned deadline. One budget covers BOTH answers —
|
|
@@ -36,81 +36,6 @@ const READ_TIMEOUT_MS = 15_000;
|
|
|
36
36
|
* throws away — on every activation and reconnect, worst on the longest
|
|
37
37
|
* conversations. One entry is the smallest legal request. */
|
|
38
38
|
const SNAPSHOT_TAIL = 1;
|
|
39
|
-
const MEMORY_KINDS = new Set(['knowledge', 'preference']);
|
|
40
|
-
const MEMORY_SCOPES = new Set(['node', 'project', 'profile', 'user', 'builtin']);
|
|
41
|
-
function nonEmptyString(value) {
|
|
42
|
-
return typeof value === 'string' && value !== '';
|
|
43
|
-
}
|
|
44
|
-
/** Keep the rows a chat surface can actually use and drop the rest. Malformed
|
|
45
|
-
* rows are discarded individually so one bad registration cannot take down the
|
|
46
|
-
* whole inventory.
|
|
47
|
-
*
|
|
48
|
-
* This validation lives here rather than reusing the attach viewer's parser:
|
|
49
|
-
* `src/core` does not import `src/clients`, and adding that import would invert
|
|
50
|
-
* the layering. */
|
|
51
|
-
function commandRows(detail) {
|
|
52
|
-
if (detail === undefined)
|
|
53
|
-
return [];
|
|
54
|
-
let parsed;
|
|
55
|
-
try {
|
|
56
|
-
parsed = JSON.parse(detail);
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
return [];
|
|
60
|
-
}
|
|
61
|
-
if (!Array.isArray(parsed))
|
|
62
|
-
return [];
|
|
63
|
-
const out = [];
|
|
64
|
-
for (const row of parsed) {
|
|
65
|
-
if (typeof row !== 'object' || row === null)
|
|
66
|
-
continue;
|
|
67
|
-
const record = row;
|
|
68
|
-
if (record['gatewayCapable'] !== true)
|
|
69
|
-
continue;
|
|
70
|
-
const source = record['source'];
|
|
71
|
-
if (source !== 'command' && source !== 'template')
|
|
72
|
-
continue;
|
|
73
|
-
if (!nonEmptyString(record['name']) || typeof record['description'] !== 'string')
|
|
74
|
-
continue;
|
|
75
|
-
const expansion = record['expansion'];
|
|
76
|
-
const template = record['template'];
|
|
77
|
-
const argumentHint = record['argumentHint'];
|
|
78
|
-
out.push({
|
|
79
|
-
name: record['name'],
|
|
80
|
-
description: record['description'],
|
|
81
|
-
source,
|
|
82
|
-
...(nonEmptyString(argumentHint) ? { argument_hint: argumentHint } : {}),
|
|
83
|
-
...(isDeterministicCommandExpansion(expansion) ? { expansion } : {}),
|
|
84
|
-
...(typeof template === 'string' ? { template } : {}),
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
return out;
|
|
88
|
-
}
|
|
89
|
-
export function gatewayMemoryRows(refs) {
|
|
90
|
-
if (!Array.isArray(refs))
|
|
91
|
-
return [];
|
|
92
|
-
const out = [];
|
|
93
|
-
for (const row of refs) {
|
|
94
|
-
if (typeof row !== 'object' || row === null)
|
|
95
|
-
continue;
|
|
96
|
-
const record = row;
|
|
97
|
-
if (record['gatewayVisible'] !== true)
|
|
98
|
-
continue;
|
|
99
|
-
if (!nonEmptyString(record['name']) || typeof record['shortForm'] !== 'string')
|
|
100
|
-
continue;
|
|
101
|
-
if (typeof record['kind'] !== 'string' || !MEMORY_KINDS.has(record['kind']))
|
|
102
|
-
continue;
|
|
103
|
-
if (typeof record['scope'] !== 'string' || !MEMORY_SCOPES.has(record['scope']))
|
|
104
|
-
continue;
|
|
105
|
-
out.push({
|
|
106
|
-
name: record['name'],
|
|
107
|
-
kind: record['kind'],
|
|
108
|
-
scope: record['scope'],
|
|
109
|
-
shortForm: record['shortForm'],
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
return out;
|
|
113
|
-
}
|
|
114
39
|
const empty = (nodeId) => ({
|
|
115
40
|
node_id: nodeId,
|
|
116
41
|
broker_live: false,
|
|
@@ -174,7 +99,7 @@ export function readChatInventory(nodeId) {
|
|
|
174
99
|
client.on('frame', (frame) => {
|
|
175
100
|
// `get_commands` carries no id, so its ack is matched on `for` alone.
|
|
176
101
|
if (frame.type === 'ack' && frame.for === 'get_commands') {
|
|
177
|
-
commands = frame.ok ?
|
|
102
|
+
commands = frame.ok ? gatewayCommandRowsFromDetail(frame.detail) : [];
|
|
178
103
|
settleWhenComplete();
|
|
179
104
|
return;
|
|
180
105
|
}
|
|
@@ -44,7 +44,7 @@ export function transitionGuidanceFromBrokerState(node, from, to) {
|
|
|
44
44
|
sections.push(to.mode === 'orchestrator' ? orchestrationGuidance(node) : baseModeGuidance());
|
|
45
45
|
}
|
|
46
46
|
if (from.lifecycle !== to.lifecycle) {
|
|
47
|
-
sections.push(loadMemoryBody(to.lifecycle === 'resident' ? 'lifecycle/resident' : 'lifecycle/terminal'));
|
|
47
|
+
sections.push(loadMemoryBody(to.lifecycle === 'resident' ? 'turn-lifecycle/resident' : 'turn-lifecycle/terminal'));
|
|
48
48
|
}
|
|
49
49
|
return sections.join('\n\n');
|
|
50
50
|
}
|
|
@@ -31,7 +31,7 @@ import { FRONT_DOOR_ENV } from './front-door.js';
|
|
|
31
31
|
import { CANVAS_EXTENSIONS, piInvocationToSdkConfig, } from './launch.js';
|
|
32
32
|
import { assertEngineVersion, loadBrokerEngine } from './broker-sdk.js';
|
|
33
33
|
import { resolveBundledPiPackageDir } from './pi-cli.js';
|
|
34
|
-
import {
|
|
34
|
+
import { captureSessionChatCapabilities } from './command-surface.js';
|
|
35
35
|
import { PoolCredentialStore } from '../pool-credential-store.js';
|
|
36
36
|
import { clearCleanAbort, markBusy, markCleanAbort } from './busy.js';
|
|
37
37
|
import { recordFaultForCurrentNode } from './fault.js';
|
|
@@ -899,9 +899,9 @@ export async function buildBrokerSession(engine, cfg) {
|
|
|
899
899
|
sessionStartEvent: o.sessionStartEvent,
|
|
900
900
|
});
|
|
901
901
|
installAdvertisedCommandInvocationContract(created.session);
|
|
902
|
-
// The resource loader has completed its initial
|
|
903
|
-
//
|
|
904
|
-
|
|
902
|
+
// The resource loader has completed its initial load, but extensions have
|
|
903
|
+
// not received session_start yet. Freeze the Gateway capability boundary.
|
|
904
|
+
captureSessionChatCapabilities(created.session);
|
|
905
905
|
// CONSTRUCTION-ONLY MASK — see modelLessRegistry: reveal now that model
|
|
906
906
|
// selection (construction-time only) is done, so a later /login re-drive's
|
|
907
907
|
// list_models/cycle_model/provider-rotation see the real registry.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ChatInventoryCommandDTO, ChatInventoryMemoryRefDTO } from '../../api/dto/chat-inventory.js';
|
|
2
|
+
/** Keep the command rows a Gateway can use. Malformed registrations are
|
|
3
|
+
* discarded individually so one producer cannot take down the inventory. */
|
|
4
|
+
export declare function gatewayCommandRows(rows: unknown): ChatInventoryCommandDTO[];
|
|
5
|
+
export declare function chatInventoryCommandRows(rows: unknown): ChatInventoryCommandDTO[];
|
|
6
|
+
export declare function gatewayCommandRowsFromDetail(detail: string | undefined): ChatInventoryCommandDTO[];
|
|
7
|
+
export declare function chatInventoryMemoryRows(rows: unknown): ChatInventoryMemoryRefDTO[];
|
|
8
|
+
export declare function gatewayMemoryRows(refs: unknown): ChatInventoryMemoryRefDTO[];
|
|
@@ -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
|
+
}
|
|
@@ -25,9 +25,14 @@ export declare function registrationChatSurface(command: unknown): ChatCommandSu
|
|
|
25
25
|
* is pi's already-parsed `argument-hint`. A missing file, unreadable path, or
|
|
26
26
|
* invalid YAML is not chat-capable. */
|
|
27
27
|
export declare function capturePromptTemplateChatSurfaces(templates: unknown): void;
|
|
28
|
-
/** Capture
|
|
29
|
-
|
|
30
|
-
|
|
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;
|
|
31
36
|
/** Read the chat surface captured for this loaded template. An uncaptured
|
|
32
37
|
* template is not disclosed to chat surfaces. */
|
|
33
38
|
export declare function templateChatSurface(template: unknown): ChatCommandSurface;
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
import { readFileSync } from 'node:fs';
|
|
19
19
|
import { parseFrontmatterGeneric } from '../frontmatter.js';
|
|
20
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();
|
|
21
25
|
// Pi drops unknown prompt frontmatter keys. This cache restores crouter's
|
|
22
26
|
// metadata without re-reading a template during command disclosure.
|
|
23
27
|
const templateChatSurfaces = new WeakMap();
|
|
@@ -31,10 +35,33 @@ export function registrationChatSurface(command) {
|
|
|
31
35
|
if (typeof command !== 'object' || command === null)
|
|
32
36
|
return NOT_CAPABLE;
|
|
33
37
|
const record = command;
|
|
34
|
-
|
|
38
|
+
const { sourceInfo, invocationName } = record;
|
|
39
|
+
if (typeof sourceInfo !== 'object' ||
|
|
40
|
+
sourceInfo === null ||
|
|
41
|
+
typeof invocationName !== 'string' ||
|
|
42
|
+
!startupGatewayNames.get(sourceInfo)?.has(invocationName))
|
|
35
43
|
return NOT_CAPABLE;
|
|
36
44
|
return { gatewayCapable: true, ...hintOf(record['argumentHint']) };
|
|
37
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
|
+
}
|
|
38
65
|
/** Capture prompt-template chat metadata immediately after pi loads or reloads
|
|
39
66
|
* its templates. The opt-in is the template file's own frontmatter; the hint
|
|
40
67
|
* is pi's already-parsed `argument-hint`. A missing file, unreadable path, or
|
|
@@ -60,16 +87,25 @@ export function capturePromptTemplateChatSurfaces(templates) {
|
|
|
60
87
|
templateChatSurfaces.set(template, surface);
|
|
61
88
|
}
|
|
62
89
|
}
|
|
63
|
-
/** Capture
|
|
64
|
-
|
|
65
|
-
|
|
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) {
|
|
66
98
|
try {
|
|
67
99
|
if (typeof session !== 'object' || session === null)
|
|
68
100
|
return;
|
|
69
|
-
|
|
101
|
+
const value = session;
|
|
102
|
+
captureChatCapabilities({
|
|
103
|
+
commands: value.extensionRunner?.getRegisteredCommands?.(),
|
|
104
|
+
templates: value.promptTemplates,
|
|
105
|
+
});
|
|
70
106
|
}
|
|
71
107
|
catch {
|
|
72
|
-
// Fail closed when an engine cannot expose its
|
|
108
|
+
// Fail closed when an engine cannot expose its loaded resources.
|
|
73
109
|
}
|
|
74
110
|
}
|
|
75
111
|
/** Read the chat surface captured for this loaded template. An uncaptured
|
|
@@ -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
|
+
}
|
|
@@ -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>;
|