@crouton-kit/crouter 0.3.19 → 0.3.21
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/builtin-memory/crouter-development/marketplaces.md +2 -4
- package/dist/builtin-memory/crouter-development/personas/base-prompt.md +3 -5
- package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +3 -6
- package/dist/builtin-memory/crouter-development/personas.md +2 -6
- package/dist/builtin-memory/crouter-development/plugins.md +2 -4
- package/dist/builtin-memory/design.md +2 -4
- package/dist/builtin-memory/development.md +3 -3
- package/dist/builtin-memory/planning.md +3 -4
- package/dist/builtin-memory/spec.md +3 -10
- package/dist/builtin-personas/design/PERSONA.md +1 -0
- package/dist/builtin-personas/design/orchestrator.md +1 -0
- package/dist/builtin-personas/developer/PERSONA.md +1 -0
- package/dist/builtin-personas/developer/orchestrator.md +1 -0
- package/dist/builtin-personas/explore/PERSONA.md +1 -0
- package/dist/builtin-personas/explore/orchestrator.md +4 -0
- package/dist/builtin-personas/general/PERSONA.md +1 -0
- package/dist/builtin-personas/general/orchestrator.md +4 -0
- package/dist/builtin-personas/orchestration-kernel.md +1 -1
- package/dist/builtin-personas/plan/PERSONA.md +1 -0
- package/dist/builtin-personas/plan/orchestrator.md +1 -0
- package/dist/builtin-personas/plan/reviewers/architecture-fit/PERSONA.md +1 -0
- package/dist/builtin-personas/plan/reviewers/code-smells/PERSONA.md +1 -0
- package/dist/builtin-personas/plan/reviewers/pattern-consistency/PERSONA.md +1 -0
- package/dist/builtin-personas/plan/reviewers/requirements-coverage/PERSONA.md +1 -0
- package/dist/builtin-personas/plan/reviewers/security/PERSONA.md +1 -0
- package/dist/builtin-personas/review/PERSONA.md +1 -0
- package/dist/builtin-personas/review/orchestrator.md +4 -0
- package/dist/builtin-personas/spec/PERSONA.md +1 -0
- package/dist/builtin-personas/spec/orchestrator.md +1 -0
- package/dist/commands/human/queue.js +13 -2
- package/dist/commands/memory/__tests__/lint-schema.test.d.ts +1 -0
- package/dist/commands/memory/__tests__/lint-schema.test.js +29 -0
- package/dist/commands/memory/find.js +10 -15
- package/dist/commands/memory/lint.d.ts +7 -0
- package/dist/commands/memory/lint.js +10 -1
- package/dist/commands/memory/shared.js +1 -2
- package/dist/commands/memory/write.js +6 -9
- package/dist/commands/node.js +51 -26
- package/dist/commands/push.js +10 -15
- package/dist/commands/tmux-spread.js +16 -1
- package/dist/commands/view-cycle.js +9 -5
- package/dist/commands/view-run.js +3 -2
- package/dist/core/__tests__/broker-attach-limits.test.d.ts +1 -0
- package/dist/core/__tests__/broker-attach-limits.test.js +157 -0
- package/dist/core/__tests__/broker-attach-stream.test.d.ts +1 -0
- package/dist/core/__tests__/broker-attach-stream.test.js +125 -0
- package/dist/core/__tests__/broker-crash-teardown.test.d.ts +1 -0
- package/dist/core/__tests__/broker-crash-teardown.test.js +116 -0
- package/dist/core/__tests__/broker-dialogs.test.d.ts +1 -0
- package/dist/core/__tests__/broker-dialogs.test.js +126 -0
- package/dist/core/__tests__/broker-dormant-wake.test.d.ts +1 -0
- package/dist/core/__tests__/broker-dormant-wake.test.js +51 -0
- package/dist/core/__tests__/broker-lifecycle.test.js +14 -604
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.d.ts +1 -0
- package/dist/core/__tests__/canvas-inbox-watcher-hold.test.js +102 -0
- package/dist/core/__tests__/canvas-inbox-watcher.test.js +2 -36
- package/dist/core/__tests__/cascade-close.test.js +3 -2
- package/dist/core/__tests__/context-intro.test.js +23 -27
- package/dist/core/__tests__/detach-focus.test.js +2 -2
- package/dist/core/__tests__/helpers/broker-clients.d.ts +43 -0
- package/dist/core/__tests__/helpers/broker-clients.js +178 -0
- package/dist/core/__tests__/live-mutation-verbs.test.d.ts +1 -0
- package/dist/core/__tests__/live-mutation-verbs.test.js +175 -0
- package/dist/core/__tests__/live-mutation.test.js +4 -147
- package/dist/core/__tests__/memory.test.js +4 -4
- package/dist/core/__tests__/subscription-delivery.test.js +3 -3
- package/dist/core/bootstrap.js +18 -14
- package/dist/core/canvas/canvas.js +1 -1
- package/dist/core/canvas/types.d.ts +6 -0
- package/dist/core/help.d.ts +2 -2
- package/dist/core/help.js +1 -1
- package/dist/core/render.d.ts +4 -3
- package/dist/core/render.js +38 -41
- package/dist/core/resolver.js +1 -1
- package/dist/core/runtime/bearings.d.ts +2 -2
- package/dist/core/runtime/bearings.js +2 -2
- package/dist/core/runtime/launch.d.ts +9 -2
- package/dist/core/runtime/launch.js +20 -3
- package/dist/core/runtime/nodes.d.ts +4 -0
- package/dist/core/runtime/nodes.js +1 -0
- package/dist/core/runtime/promote.js +3 -0
- package/dist/core/runtime/reset.js +3 -2
- package/dist/core/runtime/spawn.d.ts +4 -0
- package/dist/core/runtime/spawn.js +2 -1
- package/dist/core/substrate/render.d.ts +1 -2
- package/dist/core/substrate/render.js +9 -12
- package/dist/core/substrate/schema.d.ts +18 -13
- package/dist/core/substrate/schema.js +12 -11
- package/dist/pi-extensions/canvas-context-intro.js +5 -3
- package/package.json +1 -1
package/dist/core/render.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
// Default stdout rendering: the result a leaf returns is written FOR the model
|
|
2
2
|
// to act on, not as data to parse. Output is a continuation of the agent's
|
|
3
|
-
// prompt —
|
|
4
|
-
//
|
|
3
|
+
// prompt — PLAIN MARKDOWN, no XML wrapper. Scalars list as `- name: value`
|
|
4
|
+
// bullets in output-schema order, prose fields render as paragraphs, and arrays
|
|
5
|
+
// become a count lead-in plus a markdown table (objects) or bullets (scalars).
|
|
6
|
+
// The raw JSON object is still available behind the `--json` global for tooling.
|
|
5
7
|
//
|
|
6
8
|
// A leaf may hand `defineLeaf` a bespoke `render(result)` for instruction-shaped
|
|
7
|
-
// output (see `node new`, `push`, `feed read`).
|
|
8
|
-
// the schema-driven generic renderer here, so
|
|
9
|
-
// without a hand-written renderer.
|
|
9
|
+
// output that leads with the outcome (see `node new`, `push`, `feed read`).
|
|
10
|
+
// Everything else falls through to the schema-driven generic renderer here, so
|
|
11
|
+
// every command obeys the markdown paradigm without a hand-written renderer.
|
|
12
|
+
// `renderError` is the one sanctioned exception: a failure is a different domain
|
|
13
|
+
// from a result, so it keeps its `<error>` block.
|
|
10
14
|
// Field names whose value is prose the agent reads, not a scalar it keys on —
|
|
11
|
-
// these always render as their own
|
|
15
|
+
// these always render as their own paragraph, never as a `- name: value` bullet.
|
|
12
16
|
const PROSE_FIELDS = new Set([
|
|
13
17
|
'follow_up', 'digest', 'message', 'next', 'result', 'content', 'body', 'guide',
|
|
14
18
|
'summary', 'note', 'reason', 'detail', 'details', 'hint', 'instruction',
|
|
@@ -37,20 +41,17 @@ function isProse(name, v) {
|
|
|
37
41
|
return true;
|
|
38
42
|
return v.includes('\n') || v.length > 80;
|
|
39
43
|
}
|
|
40
|
-
/**
|
|
41
|
-
*
|
|
42
|
-
function
|
|
43
|
-
|
|
44
|
-
return true;
|
|
45
|
-
return typeof v === 'string' && v.length <= 40 && !v.includes(' ') && !v.includes('\n');
|
|
46
|
-
}
|
|
47
|
-
/** Root element name = the command path with spaces hyphenated. */
|
|
48
|
-
function rootTag(help) {
|
|
49
|
-
return help.name.trim().split(/\s+/).join('-').replace(/[^a-zA-Z0-9_-]/g, '') || 'result';
|
|
44
|
+
/** Keep a value safe inside a markdown table cell: pipes would split columns,
|
|
45
|
+
* newlines would break the row. */
|
|
46
|
+
function cellEsc(v) {
|
|
47
|
+
return scalarStr(v).replace(/\|/g, '\\|').replace(/\n/g, ' ');
|
|
50
48
|
}
|
|
49
|
+
/** An array renders as a count lead-in followed by the collection: a markdown
|
|
50
|
+
* table when every element is an object, plain bullets otherwise. */
|
|
51
51
|
function renderArray(name, arr) {
|
|
52
52
|
if (arr.length === 0)
|
|
53
|
-
return
|
|
53
|
+
return `0 ${name}.`;
|
|
54
|
+
const lead = `${arr.length} ${name}:`;
|
|
54
55
|
const allObjects = arr.every((x) => x !== null && typeof x === 'object' && !Array.isArray(x));
|
|
55
56
|
if (allObjects) {
|
|
56
57
|
const rows = arr;
|
|
@@ -61,55 +62,51 @@ function renderArray(name, arr) {
|
|
|
61
62
|
cols.push(k);
|
|
62
63
|
const head = `| ${cols.join(' | ')} |`;
|
|
63
64
|
const sep = `| ${cols.map(() => '---').join(' | ')} |`;
|
|
64
|
-
const body = rows.map((r) => `| ${cols.map((c) =>
|
|
65
|
-
return
|
|
65
|
+
const body = rows.map((r) => `| ${cols.map((c) => cellEsc(r[c])).join(' | ')} |`).join('\n');
|
|
66
|
+
return `${lead}\n\n${head}\n${sep}\n${body}`;
|
|
66
67
|
}
|
|
67
68
|
const items = arr.map((x) => `- ${scalarStr(x)}`).join('\n');
|
|
68
|
-
return
|
|
69
|
+
return `${lead}\n${items}`;
|
|
69
70
|
}
|
|
70
|
-
/** Schema-driven fallback: turn a result object into agent-ready
|
|
71
|
+
/** Schema-driven fallback: turn a result object into agent-ready plain markdown.
|
|
71
72
|
* Field order follows the leaf's declared output schema; any extra keys append
|
|
72
|
-
* after. Scalars
|
|
73
|
-
* become
|
|
73
|
+
* after. Scalars (and nested objects) list as `- name: value` bullets, prose
|
|
74
|
+
* fields render as paragraphs, and arrays become a count lead-in plus a table
|
|
75
|
+
* or bullets. No root tag — the result is read as a continuation of the prompt. */
|
|
74
76
|
export function renderResult(result, help) {
|
|
75
77
|
const order = help.output.map((f) => f.name);
|
|
76
78
|
for (const k of Object.keys(result))
|
|
77
79
|
if (!order.includes(k))
|
|
78
80
|
order.push(k);
|
|
79
|
-
const attrs = [];
|
|
80
81
|
const bullets = [];
|
|
81
|
-
const
|
|
82
|
+
const proseFields = [];
|
|
83
|
+
const arrays = [];
|
|
82
84
|
for (const name of order) {
|
|
83
85
|
const v = result[name];
|
|
84
86
|
if (v === undefined || v === null)
|
|
85
87
|
continue;
|
|
86
88
|
if (Array.isArray(v)) {
|
|
87
|
-
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
if (typeof v === 'object') {
|
|
91
|
-
blocks.push(`<${name}>\n${JSON.stringify(v, null, 2)}\n</${name}>`);
|
|
89
|
+
arrays.push(renderArray(name, v));
|
|
92
90
|
continue;
|
|
93
91
|
}
|
|
94
92
|
if (isProse(name, v)) {
|
|
95
|
-
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
if (isAttr(v)) {
|
|
99
|
-
attrs.push(`${name}="${attrEsc(v)}"`);
|
|
93
|
+
proseFields.push({ name, text: v });
|
|
100
94
|
continue;
|
|
101
95
|
}
|
|
96
|
+
// Scalars and nested objects alike list as a bullet (objects via compact JSON).
|
|
102
97
|
bullets.push(`- ${name}: ${scalarStr(v)}`);
|
|
103
98
|
}
|
|
104
|
-
const tag = rootTag(help);
|
|
105
|
-
const open = attrs.length > 0 ? `<${tag} ${attrs.join(' ')}>` : `<${tag}>`;
|
|
106
99
|
const parts = [];
|
|
107
100
|
if (bullets.length > 0)
|
|
108
101
|
parts.push(bullets.join('\n'));
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
|
|
102
|
+
// Label prose only when more than one block would otherwise blur together.
|
|
103
|
+
const labelProse = proseFields.length > 1;
|
|
104
|
+
for (const { name, text } of proseFields) {
|
|
105
|
+
parts.push(labelProse ? `**${name}:** ${text}` : text);
|
|
106
|
+
}
|
|
107
|
+
for (const block of arrays)
|
|
108
|
+
parts.push(block);
|
|
109
|
+
return parts.join('\n\n');
|
|
113
110
|
}
|
|
114
111
|
/** Render a structured failure as an instruction-shaped block: what broke, what
|
|
115
112
|
* was received, and the recovery road sign — the same recovery info the JSON
|
package/dist/core/resolver.js
CHANGED
|
@@ -403,7 +403,7 @@ function formatNotFoundMessage(rawName, skillName, pluginQualifier) {
|
|
|
403
403
|
lines.push(` did you mean: ${formatted.join(', ')}`);
|
|
404
404
|
}
|
|
405
405
|
else {
|
|
406
|
-
lines.push(' run `crtr
|
|
406
|
+
lines.push(' run `crtr memory list` or `crtr memory find "<query>"` to discover skills');
|
|
407
407
|
}
|
|
408
408
|
return lines.join('\n');
|
|
409
409
|
}
|
|
@@ -56,6 +56,6 @@ export declare function buildWakeBearings(origin: WakeOrigin): string;
|
|
|
56
56
|
* any copied-in persona) followed by the `<crtr-context>` bearings block. Base
|
|
57
57
|
* framing rides for EVERY node; the across-cycles context-dir note is added
|
|
58
58
|
* ONLY for an orchestrator (by mode) — the one node whose dir a future cycle
|
|
59
|
-
* resumes from. The `## References` block
|
|
60
|
-
* node-local docs
|
|
59
|
+
* resumes from. The `## References` block carries the substrate reference docs
|
|
60
|
+
* + node-local docs. */
|
|
61
61
|
export declare function buildContextBearings(nodeId: string): string;
|
|
@@ -145,8 +145,8 @@ export function buildWakeBearings(origin) {
|
|
|
145
145
|
* any copied-in persona) followed by the `<crtr-context>` bearings block. Base
|
|
146
146
|
* framing rides for EVERY node; the across-cycles context-dir note is added
|
|
147
147
|
* ONLY for an orchestrator (by mode) — the one node whose dir a future cycle
|
|
148
|
-
* resumes from. The `## References` block
|
|
149
|
-
* node-local docs
|
|
148
|
+
* resumes from. The `## References` block carries the substrate reference docs
|
|
149
|
+
* + node-local docs. */
|
|
150
150
|
export function buildContextBearings(nodeId) {
|
|
151
151
|
const identity = buildIdentityAssertion(nodeId);
|
|
152
152
|
const dir = contextDir(nodeId);
|
|
@@ -22,8 +22,14 @@ export declare const CANVAS_VIEW_PATH: string;
|
|
|
22
22
|
* All self-gate on CRTR_NODE_ID. goal-capture precedes passive-context so it
|
|
23
23
|
* reads the raw user text. */
|
|
24
24
|
export declare const CANVAS_EXTENSIONS: string[];
|
|
25
|
-
/**
|
|
26
|
-
*
|
|
25
|
+
/** The named capability tiers a caller picks with `--model`, in descending
|
|
26
|
+
* strength. Each maps to a concrete pi model spec. `strong`/`medium`/`light`
|
|
27
|
+
* resolve to the latest opus/sonnet/haiku; `ultra` to the frontier model. */
|
|
28
|
+
export declare const MODEL_TIERS: Record<string, string>;
|
|
29
|
+
/** Resolve a model token to the spec pi gets via `--model`. A named tier
|
|
30
|
+
* (ultra/strong/medium/light) maps to its concrete spec; a bare alias
|
|
31
|
+
* (sonnet/opus/haiku) resolves to the anthropic provider under pi (avoids the
|
|
32
|
+
* bedrock default); anything with a `/` or an unknown name passes through. */
|
|
27
33
|
export declare function normalizeModel(model: string): string;
|
|
28
34
|
/** Compose a node's full pi launch recipe from its persona. The system prompt
|
|
29
35
|
* is composed from FOUR inputs: kind×mode (the persona body) plus lifecycle
|
|
@@ -36,6 +42,7 @@ export declare function buildLaunchSpec(kind: string, mode: Mode, opts: {
|
|
|
36
42
|
lifecycle: Lifecycle;
|
|
37
43
|
hasManager: boolean;
|
|
38
44
|
extraEnv?: Record<string, string>;
|
|
45
|
+
model?: string;
|
|
39
46
|
}): {
|
|
40
47
|
launch: LaunchSpec;
|
|
41
48
|
lifecycle: 'terminal' | 'resident';
|
|
@@ -60,9 +60,22 @@ export const CANVAS_EXTENSIONS = [
|
|
|
60
60
|
CANVAS_RESUME_PATH,
|
|
61
61
|
CANVAS_VIEW_PATH,
|
|
62
62
|
];
|
|
63
|
-
/**
|
|
64
|
-
*
|
|
63
|
+
/** The named capability tiers a caller picks with `--model`, in descending
|
|
64
|
+
* strength. Each maps to a concrete pi model spec. `strong`/`medium`/`light`
|
|
65
|
+
* resolve to the latest opus/sonnet/haiku; `ultra` to the frontier model. */
|
|
66
|
+
export const MODEL_TIERS = {
|
|
67
|
+
ultra: 'anthropic/claude-fable-5',
|
|
68
|
+
strong: 'anthropic/opus',
|
|
69
|
+
medium: 'anthropic/sonnet',
|
|
70
|
+
light: 'anthropic/haiku',
|
|
71
|
+
};
|
|
72
|
+
/** Resolve a model token to the spec pi gets via `--model`. A named tier
|
|
73
|
+
* (ultra/strong/medium/light) maps to its concrete spec; a bare alias
|
|
74
|
+
* (sonnet/opus/haiku) resolves to the anthropic provider under pi (avoids the
|
|
75
|
+
* bedrock default); anything with a `/` or an unknown name passes through. */
|
|
65
76
|
export function normalizeModel(model) {
|
|
77
|
+
if (model in MODEL_TIERS)
|
|
78
|
+
return MODEL_TIERS[model];
|
|
66
79
|
const bare = new Set(['sonnet', 'opus', 'haiku']);
|
|
67
80
|
if (bare.has(model))
|
|
68
81
|
return `anthropic/${model}`;
|
|
@@ -80,8 +93,12 @@ export function normalizeModel(model) {
|
|
|
80
93
|
* declared extensions follow. */
|
|
81
94
|
export function buildLaunchSpec(kind, mode, opts) {
|
|
82
95
|
const p = resolvePersona(kind, mode, { lifecycle: opts.lifecycle, hasManager: opts.hasManager });
|
|
96
|
+
// A caller-supplied override (durable on `meta.model_override`, re-passed on
|
|
97
|
+
// every polymorph) wins over the persona's declared default; absent both, the
|
|
98
|
+
// model is left unset and the node inherits pi's default.
|
|
99
|
+
const chosenModel = opts.model ?? p.model;
|
|
83
100
|
const launch = {
|
|
84
|
-
model:
|
|
101
|
+
model: chosenModel !== undefined ? normalizeModel(chosenModel) : undefined,
|
|
85
102
|
tools: p.tools,
|
|
86
103
|
extensions: [...CANVAS_EXTENSIONS, ...p.extensions],
|
|
87
104
|
systemPrompt: p.systemPrompt,
|
|
@@ -98,6 +98,10 @@ export interface SpawnNodeOpts {
|
|
|
98
98
|
passiveDefault?: boolean;
|
|
99
99
|
/** Resolved pi launch recipe (from resolve(kind,mode)). */
|
|
100
100
|
launch?: LaunchSpec;
|
|
101
|
+
/** Caller-pinned model tier (ultra/strong/medium/light) that overrides the
|
|
102
|
+
* persona default. Persisted to `meta.model_override` so polymorphs preserve
|
|
103
|
+
* it. Omit to use the persona default. */
|
|
104
|
+
modelOverride?: string | null;
|
|
101
105
|
/** Override the generated id (e.g. when a caller pre-allocates one). */
|
|
102
106
|
nodeId?: string;
|
|
103
107
|
}
|
|
@@ -46,6 +46,9 @@ export function promote(nodeId, opts = {}) {
|
|
|
46
46
|
const { launch } = buildLaunchSpec(targetKind, 'orchestrator', {
|
|
47
47
|
lifecycle: opts.resident === true ? 'resident' : node.lifecycle,
|
|
48
48
|
hasManager: node.parent !== null,
|
|
49
|
+
// Preserve a caller-pinned model tier across the polymorph (the persona
|
|
50
|
+
// default is recomputed fresh for targetKind).
|
|
51
|
+
model: node.model_override ?? undefined,
|
|
49
52
|
});
|
|
50
53
|
// Seed a barebones roadmap scaffold if absent so the file exists for a
|
|
51
54
|
// refresh. Pre-fill its Goal from the node's goal doc when present (set at
|
|
@@ -112,7 +112,7 @@ export function resetRoot(nodeId, newSessionId, newSessionFile) {
|
|
|
112
112
|
// Re-seed persona_ack to the fresh persona so the pristine `/new`
|
|
113
113
|
// conversation never gets a spurious mode/lifecycle transition steer (the
|
|
114
114
|
// persona injector compares against this ack).
|
|
115
|
-
const { launch } = buildLaunchSpec(meta.kind, 'base', { lifecycle: 'resident', hasManager: false });
|
|
115
|
+
const { launch } = buildLaunchSpec(meta.kind, 'base', { lifecycle: 'resident', hasManager: false, model: meta.model_override ?? undefined });
|
|
116
116
|
updateNode(nodeId, {
|
|
117
117
|
mode: 'base',
|
|
118
118
|
lifecycle: 'resident',
|
|
@@ -180,7 +180,7 @@ export function relaunchRoot(oldId, pane, deps = {}) {
|
|
|
180
180
|
window: oldMeta.window ?? null,
|
|
181
181
|
};
|
|
182
182
|
const newId = newNodeId();
|
|
183
|
-
const { launch } = buildLaunchSpec(oldMeta.kind, 'base', { lifecycle: 'resident', hasManager: false });
|
|
183
|
+
const { launch } = buildLaunchSpec(oldMeta.kind, 'base', { lifecycle: 'resident', hasManager: false, model: oldMeta.model_override ?? undefined });
|
|
184
184
|
// Park-old + mint-new is the single most fragile spot in the runtime, so it is
|
|
185
185
|
// ONE atomic unit: every ROW write below runs inside a sqlite transaction. A
|
|
186
186
|
// failure anywhere — including the respawn DISPATCH — rolls the whole thing
|
|
@@ -206,6 +206,7 @@ export function relaunchRoot(oldId, pane, deps = {}) {
|
|
|
206
206
|
parent: null,
|
|
207
207
|
spawnedBy: oldId, // audit-only successor link; does NOT touch the spine
|
|
208
208
|
nodeId: newId,
|
|
209
|
+
modelOverride: oldMeta.model_override,
|
|
209
210
|
launch,
|
|
210
211
|
});
|
|
211
212
|
transition(newId, 'yield'); // active (from spawn) + intent=refresh safety net
|
|
@@ -39,6 +39,10 @@ export interface SpawnChildOpts {
|
|
|
39
39
|
* headless broker. Persisted as `host_kind` at birth (resolved from
|
|
40
40
|
* `--headless` / the `headless` config default by the caller). */
|
|
41
41
|
hostKind?: 'tmux' | 'broker';
|
|
42
|
+
/** Pin the node to a model TIER (ultra/strong/medium/light), overriding the
|
|
43
|
+
* persona's declared default. Persisted to `meta.model_override` so it
|
|
44
|
+
* survives polymorphs. Omit to use the persona default. */
|
|
45
|
+
model?: string;
|
|
42
46
|
}
|
|
43
47
|
/** Resolve a `--fork-from` value to the source pi gets as `--fork <path|id>`.
|
|
44
48
|
* A live node id resolves to its captured session FILE (absolute, cwd-immune),
|
|
@@ -161,7 +161,7 @@ export function spawnChild(opts) {
|
|
|
161
161
|
// Spine: a managed child reports up to its spawner (has a manager); an
|
|
162
162
|
// independent root sits top-of-spine with nobody to push to. Mirrors the
|
|
163
163
|
// `parent` set below (root ? null : spawner), so hasManager === parent!==null.
|
|
164
|
-
const { launch } = buildLaunchSpec(opts.kind, mode, { lifecycle, hasManager: !root });
|
|
164
|
+
const { launch } = buildLaunchSpec(opts.kind, mode, { lifecycle, hasManager: !root, model: opts.model });
|
|
165
165
|
// Name the worker from its task now, so its first editor label carries it.
|
|
166
166
|
const meta = spawnNode({
|
|
167
167
|
kind: opts.kind,
|
|
@@ -180,6 +180,7 @@ export function spawnChild(opts) {
|
|
|
180
180
|
// identity over the source's copied-in conversation.
|
|
181
181
|
forkFrom: opts.forkFrom,
|
|
182
182
|
hostKind: opts.hostKind,
|
|
183
|
+
modelOverride: opts.model,
|
|
183
184
|
launch,
|
|
184
185
|
});
|
|
185
186
|
// Persist the task as the child's goal for a fresh revive to re-read.
|
|
@@ -14,8 +14,7 @@ export declare function renderPreferencesSection(nodeId: string): string;
|
|
|
14
14
|
* drops it when ''). Holds every eligible `kind: reference` resolver doc at its
|
|
15
15
|
* `system-prompt-visibility` (reference boot default is `none`, so only
|
|
16
16
|
* author-promoted references show) PLUS the node-local memory docs (any kind),
|
|
17
|
-
* each a `###` sub-section.
|
|
18
|
-
* nothing is eligible.
|
|
17
|
+
* each a `###` sub-section. Returns '' when nothing is eligible.
|
|
19
18
|
*
|
|
20
19
|
* DEFENSIVE: each doc is rendered in its own try/catch so a single malformed
|
|
21
20
|
* doc drops only itself (with a loud stderr warning naming the offending path),
|
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
// (renderPreferencesSection) — strings the D2 `before_agent_start` extension
|
|
6
6
|
// splices into the system prompt (built by a sibling AFTER this module);
|
|
7
7
|
// • the `<crtr-context>` half — `## References` (renderReferencesBlock) — the
|
|
8
|
-
// string wired into bearings.ts's session_start message
|
|
9
|
-
// `<memory>` block.
|
|
8
|
+
// string wired into bearings.ts's session_start message.
|
|
10
9
|
//
|
|
11
10
|
// Every doc flows through the same pipeline (design §4/§6/§9):
|
|
12
11
|
// MemoryDoc → parseSubstrateDoc → (null-filter non-substrate) →
|
|
@@ -54,10 +53,9 @@ function resolverDocs(subject, kind) {
|
|
|
54
53
|
* canvas home (`nodes/<id>/context/memory/`) and is OUTSIDE the scope resolver,
|
|
55
54
|
* so it is loaded directly here, its raw frontmatter run through
|
|
56
55
|
* parseSubstrateFrontmatter (mirroring the resolver pipeline), then gate-passed.
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* replacement for the old node-local `<memory>` stanza.
|
|
56
|
+
* Non-substrate files (those with no `kind`) parse to null and drop out.
|
|
57
|
+
* Returned across ALL kinds — node-local is the catch-all this-node store and
|
|
58
|
+
* rides into the references block.
|
|
61
59
|
*
|
|
62
60
|
* IMPORTANT: node-local docs are NOT filtered by `systemPromptVisibility` rung.
|
|
63
61
|
* A migrated node-local reference defaults to rung `none`, which would make it
|
|
@@ -122,8 +120,8 @@ function renderSubSection(d) {
|
|
|
122
120
|
* resolver-provided, NOT migrated — named-plugin skills at user/project scope;
|
|
123
121
|
* the SCOPE_SKILL_PLUGIN sentinel and builtin scope are EXCLUDED because they
|
|
124
122
|
* become substrate docs). Reuses skill/shared.ts's renderCatalogSection
|
|
125
|
-
* group-collapse
|
|
126
|
-
*
|
|
123
|
+
* group-collapse for a compact, source-grouped catalog. Returns '' when nothing
|
|
124
|
+
* is in the catalog. */
|
|
127
125
|
function renderSkillCatalog(nameRungSkillDocs) {
|
|
128
126
|
let pluginSkills;
|
|
129
127
|
try {
|
|
@@ -138,8 +136,8 @@ function renderSkillCatalog(nameRungSkillDocs) {
|
|
|
138
136
|
];
|
|
139
137
|
if (entries.length === 0)
|
|
140
138
|
return '';
|
|
141
|
-
// Group by scope+plugin → forest-root names (drop nested children)
|
|
142
|
-
//
|
|
139
|
+
// Group by scope+plugin → forest-root names (drop nested children) so each
|
|
140
|
+
// source contributes only its top-level skills.
|
|
143
141
|
const bySource = new Map();
|
|
144
142
|
for (const e of entries) {
|
|
145
143
|
const key = `${e.scope}\t${e.plugin}`;
|
|
@@ -225,8 +223,7 @@ export function renderPreferencesSection(nodeId) {
|
|
|
225
223
|
* drops it when ''). Holds every eligible `kind: reference` resolver doc at its
|
|
226
224
|
* `system-prompt-visibility` (reference boot default is `none`, so only
|
|
227
225
|
* author-promoted references show) PLUS the node-local memory docs (any kind),
|
|
228
|
-
* each a `###` sub-section.
|
|
229
|
-
* nothing is eligible.
|
|
226
|
+
* each a `###` sub-section. Returns '' when nothing is eligible.
|
|
230
227
|
*
|
|
231
228
|
* DEFENSIVE: each doc is rendered in its own try/catch so a single malformed
|
|
232
229
|
* doc drops only itself (with a loud stderr warning naming the offending path),
|
|
@@ -21,15 +21,17 @@ export declare const KIND_DEFAULT_RUNGS: Record<DocKind, {
|
|
|
21
21
|
* `all`/`any`/`not` combinators (design §4). */
|
|
22
22
|
export type GatePredicate = Record<string, unknown>;
|
|
23
23
|
/** The frontmatter-derived schema of a substrate document, with kind-aware
|
|
24
|
-
* defaults applied. Required fields (`kind`/`when
|
|
25
|
-
* resolved to concrete typed values. */
|
|
24
|
+
* defaults applied. Required fields (`kind`/`when-and-why-to-read`) and
|
|
25
|
+
* optionals all resolved to concrete typed values. */
|
|
26
26
|
export interface SubstrateSchema {
|
|
27
27
|
/** Which of the three semantic kinds. */
|
|
28
28
|
kind: DocKind;
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
/** The read-routing line — a single sentence answering WHEN to read this doc
|
|
30
|
+
* and WHY it is worth the read: "When <circumstance>, this <kind> should be
|
|
31
|
+
* read <because <payoff>>." (design §4). This is read-routing — why an agent
|
|
32
|
+
* should spend the read — NEVER justification of why the content should be
|
|
33
|
+
* obeyed. It IS the preview verbatim. Frontmatter key `when-and-why-to-read`. */
|
|
34
|
+
whenAndWhyToRead: string;
|
|
33
35
|
/** Human-facing abbreviation for `crtr memory list`. NEVER loaded into an
|
|
34
36
|
* agent's context (design §3). Empty string when absent. */
|
|
35
37
|
shortForm: string;
|
|
@@ -62,15 +64,18 @@ export interface SubstrateDoc extends SubstrateSchema {
|
|
|
62
64
|
* resolver) into a typed schema with defaults applied. Returns `null` when the
|
|
63
65
|
* record is absent or carries no valid `kind` — i.e. it is not a substrate
|
|
64
66
|
* document and cannot be classified or defaulted. Tolerant of every other
|
|
65
|
-
* imperfection (missing `when
|
|
66
|
-
* the kind default), so a renderer mapping over many docs never
|
|
67
|
+
* imperfection (a missing `when-and-why-to-read` defaults to '', a bad rung
|
|
68
|
+
* falls back to the kind default), so a renderer mapping over many docs never
|
|
69
|
+
* throws. Authoring-time enforcement of the field lives in `crtr memory lint`. */
|
|
67
70
|
export declare function parseSubstrateFrontmatter(fm: Record<string, unknown> | null): SubstrateSchema | null;
|
|
68
71
|
/** Parse a resolved MemoryDoc into a fully-typed SubstrateDoc (schema + the
|
|
69
72
|
* resolver's name/scope/path/body). Returns `null` for a non-substrate doc
|
|
70
73
|
* (no valid `kind`), so callers can `docs.map(parseSubstrateDoc).filter(...)`. */
|
|
71
74
|
export declare function parseSubstrateDoc(doc: MemoryDoc): SubstrateDoc | null;
|
|
72
|
-
/** The
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
|
|
75
|
+
/** The `preview`-rung routing line (design §4): the `when-and-why-to-read`
|
|
76
|
+
* field rendered essentially verbatim — it is already authored as the complete
|
|
77
|
+
* routing sentence ("When …, this <kind> should be read …"), so there is no
|
|
78
|
+
* template to compose. Both boot and on-read render this identical line, so it
|
|
79
|
+
* lives once here to prevent drift. Light cleanup normalizes the trailing
|
|
80
|
+
* period. */
|
|
81
|
+
export declare function previewLine(doc: Pick<SubstrateSchema, 'whenAndWhyToRead'>): string;
|
|
@@ -50,8 +50,9 @@ export const KIND_DEFAULT_RUNGS = {
|
|
|
50
50
|
* resolver) into a typed schema with defaults applied. Returns `null` when the
|
|
51
51
|
* record is absent or carries no valid `kind` — i.e. it is not a substrate
|
|
52
52
|
* document and cannot be classified or defaulted. Tolerant of every other
|
|
53
|
-
* imperfection (missing `when
|
|
54
|
-
* the kind default), so a renderer mapping over many docs never
|
|
53
|
+
* imperfection (a missing `when-and-why-to-read` defaults to '', a bad rung
|
|
54
|
+
* falls back to the kind default), so a renderer mapping over many docs never
|
|
55
|
+
* throws. Authoring-time enforcement of the field lives in `crtr memory lint`. */
|
|
55
56
|
export function parseSubstrateFrontmatter(fm) {
|
|
56
57
|
if (fm === null)
|
|
57
58
|
return null;
|
|
@@ -61,8 +62,7 @@ export function parseSubstrateFrontmatter(fm) {
|
|
|
61
62
|
const defaults = KIND_DEFAULT_RUNGS[kind];
|
|
62
63
|
return {
|
|
63
64
|
kind,
|
|
64
|
-
|
|
65
|
-
why: strField(fm.why),
|
|
65
|
+
whenAndWhyToRead: strField(fm['when-and-why-to-read']),
|
|
66
66
|
shortForm: strField(fm['short-form']),
|
|
67
67
|
systemPromptVisibility: parseRung(fm['system-prompt-visibility'], defaults.systemPrompt),
|
|
68
68
|
fileReadVisibility: parseRung(fm['file-read-visibility'], defaults.fileRead),
|
|
@@ -79,14 +79,15 @@ export function parseSubstrateDoc(doc) {
|
|
|
79
79
|
return null;
|
|
80
80
|
return { ...schema, name: doc.name, scope: doc.scope, path: doc.path, body: doc.body };
|
|
81
81
|
}
|
|
82
|
-
/** The
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
82
|
+
/** The `preview`-rung routing line (design §4): the `when-and-why-to-read`
|
|
83
|
+
* field rendered essentially verbatim — it is already authored as the complete
|
|
84
|
+
* routing sentence ("When …, this <kind> should be read …"), so there is no
|
|
85
|
+
* template to compose. Both boot and on-read render this identical line, so it
|
|
86
|
+
* lives once here to prevent drift. Light cleanup normalizes the trailing
|
|
87
|
+
* period. */
|
|
86
88
|
export function previewLine(doc) {
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
return `${when}, read this ${doc.kind}. ${why}.`;
|
|
89
|
+
const line = doc.whenAndWhyToRead.trim().replace(/\.+$/, '');
|
|
90
|
+
return line === '' ? '' : `${line}.`;
|
|
90
91
|
}
|
|
91
92
|
// ---------------------------------------------------------------------------
|
|
92
93
|
// Field coercion helpers (private).
|
|
@@ -15,9 +15,11 @@
|
|
|
15
15
|
//
|
|
16
16
|
// The block carries: the path to the node's own context dir and the framing for
|
|
17
17
|
// what belongs there (a shared document store for the other nodes). EVERY node
|
|
18
|
-
// also gets a
|
|
19
|
-
//
|
|
20
|
-
//
|
|
18
|
+
// also gets a `## References` block rendered from the document substrate —
|
|
19
|
+
// eligible `reference` docs at their system-prompt visibility rung, plus the
|
|
20
|
+
// node's own node-local substrate docs — so the bearings name what to read on
|
|
21
|
+
// demand. (Skills and preferences surface as their own `## Skills` /
|
|
22
|
+
// `## Preferences` sections of the system prompt, not in this block.) An
|
|
21
23
|
// orchestrator additionally gets the across-refresh-cycles framing (the one
|
|
22
24
|
// thing a terminal worker's bearings drop). The prose lives in
|
|
23
25
|
// core/runtime/bearings.ts (shared with the promotion guidance dump).
|