@crouton-kit/crouter 0.3.17 → 0.3.19
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/build-root.js +6 -0
- package/dist/builtin-memory/crouter-development/marketplaces.md +164 -0
- package/dist/builtin-memory/crouter-development/personas/base-prompt.md +58 -0
- package/dist/builtin-memory/crouter-development/personas/orchestrator-prompt.md +60 -0
- package/dist/builtin-memory/crouter-development/personas.md +117 -0
- package/dist/builtin-memory/crouter-development/plugins.md +163 -0
- package/dist/builtin-memory/design.md +60 -0
- package/dist/builtin-memory/development.md +116 -0
- package/dist/builtin-memory/planning.md +66 -0
- package/dist/builtin-memory/spec.md +99 -0
- package/dist/builtin-personas/design/{base.md → PERSONA.md} +4 -0
- package/dist/builtin-personas/design/orchestrator.md +1 -1
- package/dist/builtin-personas/developer/{base.md → PERSONA.md} +4 -0
- package/dist/builtin-personas/developer/orchestrator.md +1 -1
- package/dist/builtin-personas/explore/{base.md → PERSONA.md} +4 -0
- package/dist/builtin-personas/general/{base.md → PERSONA.md} +4 -0
- package/dist/builtin-personas/lifecycle/resident.md +1 -1
- package/dist/builtin-personas/lifecycle/terminal.md +5 -2
- package/dist/builtin-personas/orchestration-kernel.md +15 -24
- package/dist/builtin-personas/plan/{base.md → PERSONA.md} +4 -0
- package/dist/builtin-personas/plan/orchestrator.md +1 -1
- package/dist/builtin-personas/plan/reviewers/architecture-fit/{base.md → PERSONA.md} +1 -1
- package/dist/builtin-personas/plan/reviewers/code-smells/{base.md → PERSONA.md} +1 -1
- package/dist/builtin-personas/plan/reviewers/pattern-consistency/{base.md → PERSONA.md} +1 -1
- package/dist/builtin-personas/plan/reviewers/requirements-coverage/{base.md → PERSONA.md} +1 -1
- package/dist/builtin-personas/plan/reviewers/security/{base.md → PERSONA.md} +1 -1
- package/dist/builtin-personas/review/{base.md → PERSONA.md} +4 -0
- package/dist/builtin-personas/runtime-base.md +6 -3
- package/dist/builtin-personas/spec/{base.md → PERSONA.md} +4 -0
- package/dist/builtin-personas/spec/orchestrator.md +1 -1
- package/dist/builtin-personas/waiting.md +8 -0
- package/dist/builtin-skills/skills/crouter-development/personas/SKILL.md +24 -14
- package/dist/builtin-skills/skills/crouter-development/personas/base-prompt/SKILL.md +4 -4
- package/dist/builtin-skills/skills/crouter-development/personas/orchestrator-prompt/SKILL.md +1 -1
- package/dist/builtin-skills/skills/crouter-development/plugins/SKILL.md +2 -2
- package/dist/builtin-views/_lib/states.mjs +161 -0
- package/dist/builtin-views/canvas/client.mjs +303 -0
- package/dist/builtin-views/canvas/view.mjs +576 -0
- package/dist/builtin-views/git-pr/client.mjs +440 -0
- package/dist/builtin-views/git-pr/view.mjs +675 -0
- package/dist/builtin-views/inbox/_lib/render.mjs +177 -0
- package/dist/builtin-views/inbox/sources/gmail.mjs +965 -0
- package/dist/builtin-views/inbox/sources/linkedin.mjs +427 -0
- package/dist/builtin-views/inbox/view.mjs +889 -0
- package/dist/builtin-views/linkedin/client.mjs +610 -0
- package/dist/builtin-views/linkedin/view.mjs +1171 -0
- package/dist/clients/attach/attach-cmd.d.ts +2 -0
- package/dist/clients/attach/attach-cmd.js +354 -0
- package/dist/clients/attach/chat-view.d.ts +77 -0
- package/dist/clients/attach/chat-view.js +450 -0
- package/dist/clients/attach/clipboard-image.d.ts +16 -0
- package/dist/clients/attach/clipboard-image.js +113 -0
- package/dist/clients/attach/config-load.d.ts +31 -0
- package/dist/clients/attach/config-load.js +113 -0
- package/dist/clients/attach/extension-dialogs.d.ts +29 -0
- package/dist/clients/attach/extension-dialogs.js +101 -0
- package/dist/clients/attach/input-controller.d.ts +54 -0
- package/dist/clients/attach/input-controller.js +204 -0
- package/dist/clients/attach/slash-commands.d.ts +36 -0
- package/dist/clients/attach/slash-commands.js +200 -0
- package/dist/clients/attach/view-socket.d.ts +48 -0
- package/dist/clients/attach/view-socket.js +126 -0
- package/dist/commands/attention.js +3 -3
- package/dist/commands/canvas-prune.js +1 -1
- package/dist/commands/daemon.js +4 -3
- package/dist/commands/human/prompts.js +4 -10
- package/dist/commands/human/queue.js +43 -8
- package/dist/commands/human/shared.d.ts +28 -1
- package/dist/commands/human/shared.js +48 -10
- package/dist/commands/memory/find.d.ts +1 -0
- package/dist/commands/memory/find.js +180 -0
- package/dist/commands/memory/lint.d.ts +1 -0
- package/dist/commands/memory/lint.js +140 -0
- package/dist/commands/memory/list.d.ts +1 -0
- package/dist/commands/memory/list.js +79 -0
- package/dist/commands/memory/read.js +103 -0
- package/dist/commands/memory/shared.d.ts +30 -0
- package/dist/commands/memory/shared.js +122 -0
- package/dist/commands/memory/write.d.ts +1 -0
- package/dist/commands/memory/write.js +85 -0
- package/dist/commands/memory.d.ts +2 -0
- package/dist/commands/memory.js +27 -0
- package/dist/commands/node.d.ts +3 -2
- package/dist/commands/node.js +660 -65
- package/dist/commands/pkg/market-manage.js +1 -1
- package/dist/commands/push.js +6 -6
- package/dist/commands/revive.js +1 -1
- package/dist/commands/skill/author.js +1 -1
- package/dist/commands/skill/shared.d.ts +1 -8
- package/dist/commands/skill/shared.js +2 -55
- package/dist/commands/skill.js +9 -14
- package/dist/commands/sys/doctor.js +1 -1
- package/dist/commands/sys/update.js +1 -1
- package/dist/commands/view-cycle.d.ts +2 -0
- package/dist/commands/view-cycle.js +125 -0
- package/dist/commands/view-list.d.ts +2 -0
- package/dist/commands/view-list.js +66 -0
- package/dist/commands/view-new.d.ts +2 -0
- package/dist/commands/view-new.js +70 -0
- package/dist/commands/view-pick.d.ts +2 -0
- package/dist/commands/view-pick.js +119 -0
- package/dist/commands/view-run.d.ts +2 -0
- package/dist/commands/view-run.js +191 -0
- package/dist/commands/view.d.ts +2 -0
- package/dist/commands/view.js +29 -0
- package/dist/core/__tests__/broker-lifecycle.test.d.ts +1 -0
- package/dist/core/__tests__/broker-lifecycle.test.js +677 -0
- package/dist/core/__tests__/broker-sdk-wiring.test.d.ts +1 -0
- package/dist/core/__tests__/broker-sdk-wiring.test.js +166 -0
- package/dist/core/__tests__/cascade-close.test.js +12 -2
- package/dist/core/__tests__/child-death-wake.test.d.ts +1 -0
- package/dist/core/__tests__/child-death-wake.test.js +245 -0
- package/dist/core/__tests__/context-intro.test.js +76 -62
- package/dist/core/__tests__/daemon-boot.test.js +14 -5
- package/dist/core/__tests__/daemon-liveness.test.js +34 -9
- package/dist/core/__tests__/detach-focus.test.d.ts +1 -0
- package/dist/core/__tests__/detach-focus.test.js +206 -0
- package/dist/core/__tests__/draw-style.test.d.ts +1 -0
- package/dist/core/__tests__/draw-style.test.js +258 -0
- package/dist/core/__tests__/fixtures/c3-custom-provider-ext.d.ts +2 -0
- package/dist/core/__tests__/fixtures/c3-custom-provider-ext.js +18 -0
- package/dist/core/__tests__/fixtures/fake-engine.d.ts +138 -0
- package/dist/core/__tests__/fixtures/fake-engine.js +614 -0
- package/dist/core/__tests__/fixtures/fake-pi-host.js +1 -0
- package/dist/core/__tests__/flagship-lifecycle.test.js +7 -1
- package/dist/core/__tests__/frame-decoder-perf.test.d.ts +1 -0
- package/dist/core/__tests__/frame-decoder-perf.test.js +198 -0
- package/dist/core/__tests__/helpers/harness.d.ts +9 -0
- package/dist/core/__tests__/helpers/harness.js +111 -1
- package/dist/core/__tests__/home-session.test.js +41 -8
- package/dist/core/__tests__/human-new-window-regression.test.d.ts +1 -0
- package/dist/core/__tests__/human-new-window-regression.test.js +101 -0
- package/dist/core/__tests__/human-surface-target.test.d.ts +1 -0
- package/dist/core/__tests__/human-surface-target.test.js +98 -0
- package/dist/core/__tests__/kickoff.test.js +37 -3
- package/dist/core/__tests__/live-mutation.test.js +50 -33
- package/dist/core/__tests__/memory.test.js +23 -115
- package/dist/core/__tests__/persona-subkind.test.js +18 -15
- package/dist/core/__tests__/placement-focus.test.js +5 -0
- package/dist/core/__tests__/placement-teardown.test.js +54 -11
- package/dist/core/__tests__/relaunch.test.js +4 -3
- package/dist/core/__tests__/review-render-pane-regression.test.d.ts +1 -0
- package/dist/core/__tests__/review-render-pane-regression.test.js +133 -0
- package/dist/core/__tests__/spawn-root.test.js +10 -0
- package/dist/core/__tests__/spike-harness.test.js +1 -0
- package/dist/core/__tests__/wake-bearings.test.d.ts +1 -0
- package/dist/core/__tests__/wake-bearings.test.js +156 -0
- package/dist/core/__tests__/wake-origin.test.d.ts +1 -0
- package/dist/core/__tests__/wake-origin.test.js +110 -0
- package/dist/core/bootstrap.js +1 -1
- package/dist/core/canvas/browse/__tests__/render.test.js +1 -0
- package/dist/core/canvas/browse/app.js +24 -2
- package/dist/core/canvas/browse/model.d.ts +38 -2
- package/dist/core/canvas/browse/model.js +134 -10
- package/dist/core/canvas/browse/render.d.ts +6 -12
- package/dist/core/canvas/browse/render.js +72 -104
- package/dist/core/canvas/canvas.js +15 -22
- package/dist/core/canvas/db.js +46 -0
- package/dist/core/canvas/index.d.ts +1 -0
- package/dist/core/canvas/index.js +1 -0
- package/dist/core/canvas/paths.d.ts +4 -1
- package/dist/core/canvas/paths.js +10 -4
- package/dist/core/canvas/pid.d.ts +4 -0
- package/dist/core/canvas/pid.js +23 -0
- package/dist/core/canvas/render.d.ts +11 -2
- package/dist/core/canvas/render.js +69 -0
- package/dist/core/canvas/types.d.ts +85 -3
- package/dist/core/canvas/types.js +2 -2
- package/dist/core/canvas/wakeups.d.ts +76 -0
- package/dist/core/canvas/wakeups.js +185 -0
- package/dist/core/config.js +4 -1
- package/dist/core/frontmatter.js +37 -124
- package/dist/core/help.d.ts +6 -0
- package/dist/core/help.js +7 -0
- package/dist/core/memory-resolver.d.ts +49 -0
- package/dist/core/memory-resolver.js +141 -0
- package/dist/core/personas/index.d.ts +4 -3
- package/dist/core/personas/index.js +3 -2
- package/dist/core/personas/loader.d.ts +41 -16
- package/dist/core/personas/loader.js +133 -29
- package/dist/core/personas/resolve.d.ts +4 -4
- package/dist/core/personas/resolve.js +28 -16
- package/dist/core/predicate.d.ts +63 -0
- package/dist/core/predicate.js +189 -0
- package/dist/core/resolver.js +26 -5
- package/dist/core/runtime/bearings.d.ts +53 -12
- package/dist/core/runtime/bearings.js +132 -59
- package/dist/core/runtime/broker-cli.d.ts +1 -0
- package/dist/core/runtime/broker-cli.js +46 -0
- package/dist/core/runtime/broker-protocol.d.ts +332 -0
- package/dist/core/runtime/broker-protocol.js +153 -0
- package/dist/core/runtime/broker-sdk.d.ts +48 -0
- package/dist/core/runtime/broker-sdk.js +72 -0
- package/dist/core/runtime/broker.d.ts +55 -0
- package/dist/core/runtime/broker.js +1128 -0
- package/dist/core/runtime/close.js +35 -6
- package/dist/core/runtime/host.d.ts +53 -0
- package/dist/core/runtime/host.js +186 -0
- package/dist/core/runtime/kickoff.d.ts +2 -1
- package/dist/core/runtime/kickoff.js +91 -5
- package/dist/core/runtime/launch.d.ts +45 -2
- package/dist/core/runtime/launch.js +65 -2
- package/dist/core/runtime/lifecycle.js +23 -6
- package/dist/core/runtime/memory.d.ts +2 -42
- package/dist/core/runtime/memory.js +11 -162
- package/dist/core/runtime/nodes.d.ts +33 -0
- package/dist/core/runtime/nodes.js +59 -1
- package/dist/core/runtime/persona.js +21 -11
- package/dist/core/runtime/pi-vendored.d.ts +18 -0
- package/dist/core/runtime/pi-vendored.js +49 -0
- package/dist/core/runtime/placement.d.ts +42 -14
- package/dist/core/runtime/placement.js +228 -38
- package/dist/core/runtime/promote.d.ts +0 -6
- package/dist/core/runtime/promote.js +1 -12
- package/dist/core/runtime/{demote.d.ts → recycle.d.ts} +5 -5
- package/dist/core/runtime/{demote.js → recycle.js} +27 -11
- package/dist/core/runtime/reset.js +8 -6
- package/dist/core/runtime/revive.d.ts +2 -0
- package/dist/core/runtime/revive.js +34 -34
- package/dist/core/runtime/spawn.d.ts +19 -0
- package/dist/core/runtime/spawn.js +75 -22
- package/dist/core/runtime/stop-guard.d.ts +1 -1
- package/dist/core/runtime/stop-guard.js +6 -1
- package/dist/core/runtime/tmux-chrome.d.ts +1 -1
- package/dist/core/runtime/tmux-chrome.js +1 -1
- package/dist/core/runtime/tmux.d.ts +28 -0
- package/dist/core/runtime/tmux.js +80 -6
- package/dist/core/scope.d.ts +11 -0
- package/dist/core/scope.js +39 -0
- package/dist/core/spawn.d.ts +25 -1
- package/dist/core/spawn.js +72 -7
- package/dist/core/substrate/gate.d.ts +13 -0
- package/dist/core/substrate/gate.js +21 -0
- package/dist/core/substrate/index.d.ts +7 -0
- package/dist/core/substrate/index.js +18 -0
- package/dist/core/substrate/on-read.d.ts +14 -0
- package/dist/core/substrate/on-read.js +292 -0
- package/dist/core/substrate/render.d.ts +25 -0
- package/dist/core/substrate/render.js +256 -0
- package/dist/core/substrate/schema.d.ts +76 -0
- package/dist/core/substrate/schema.js +124 -0
- package/dist/core/substrate/session-cache.d.ts +30 -0
- package/dist/core/substrate/session-cache.js +77 -0
- package/dist/core/substrate/subject.d.ts +41 -0
- package/dist/core/substrate/subject.js +54 -0
- package/dist/core/tui/contract.d.ts +83 -0
- package/dist/core/tui/contract.js +8 -0
- package/dist/core/tui/draw.d.ts +96 -0
- package/dist/core/tui/draw.js +339 -0
- package/dist/core/tui/host.d.ts +29 -0
- package/dist/core/tui/host.js +379 -0
- package/dist/core/tui/loader.d.ts +16 -0
- package/dist/core/tui/loader.js +94 -0
- package/dist/core/{canvas/browse → tui}/terminal.js +7 -6
- package/dist/core/wake.d.ts +86 -0
- package/dist/core/wake.js +308 -0
- package/dist/daemon/crtrd.d.ts +29 -4
- package/dist/daemon/crtrd.js +662 -46
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.d.ts +1 -0
- package/dist/pi-extensions/__tests__/canvas-context-intro.test.js +171 -0
- package/dist/pi-extensions/__tests__/canvas-stophook-agentend.test.js +19 -12
- package/dist/pi-extensions/canvas-commands.d.ts +3 -0
- package/dist/pi-extensions/canvas-commands.js +10 -0
- package/dist/pi-extensions/canvas-context-intro.d.ts +17 -0
- package/dist/pi-extensions/canvas-context-intro.js +55 -15
- package/dist/pi-extensions/canvas-doc-substrate.d.ts +44 -0
- package/dist/pi-extensions/canvas-doc-substrate.js +112 -0
- package/dist/pi-extensions/canvas-nav.d.ts +3 -0
- package/dist/pi-extensions/canvas-nav.js +145 -34
- package/dist/pi-extensions/canvas-stophook.js +17 -8
- package/dist/pi-extensions/canvas-view.d.ts +21 -0
- package/dist/pi-extensions/canvas-view.js +75 -0
- package/dist/prompts/skill.js +19 -26
- package/dist/prompts/view.d.ts +7 -0
- package/dist/prompts/view.js +101 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.js +1 -0
- package/package.json +9 -4
- package/dist/commands/skill/find.d.ts +0 -4
- package/dist/commands/skill/find.js +0 -257
- package/dist/commands/skill/read.js +0 -91
- /package/dist/commands/{skill → memory}/read.d.ts +0 -0
- /package/dist/core/{canvas/browse → tui}/terminal.d.ts +0 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Predicate matcher engine — vendored verbatim from pi's frontmatter-rules
|
|
3
|
+
* extension.
|
|
4
|
+
*
|
|
5
|
+
* Source: pi-personal-extensions/extensions/frontmatter-rules/index.ts
|
|
6
|
+
* (the "Condition language" block, functions `asArray` … `evalCondition`).
|
|
7
|
+
*
|
|
8
|
+
* The unified document substrate uses this to evaluate a document's `gate`
|
|
9
|
+
* predicate against a node-config subject (e.g. `orchestration.depth: { gte: 2 }`).
|
|
10
|
+
*
|
|
11
|
+
* This is intentionally a COPY, not an import: the source lives in a separate,
|
|
12
|
+
* private, unpublished repo that crtr cannot depend on. The functions are pure
|
|
13
|
+
* TS over `unknown` / `Record<string, unknown>` with zero external imports, so
|
|
14
|
+
* vendoring them keeps this module standalone and dependency-free. Keep it in
|
|
15
|
+
* sync with the source by hand if the matcher language ever changes.
|
|
16
|
+
*
|
|
17
|
+
* Condition language (evaluated against the subject object):
|
|
18
|
+
* A condition is a map of <field> -> <matcher>, AND-ed across fields.
|
|
19
|
+
* `field` may be dotted to reach nested values (e.g. `orchestration.depth`).
|
|
20
|
+
*
|
|
21
|
+
* Matcher forms:
|
|
22
|
+
* scalar (string/number/bool/null)
|
|
23
|
+
* field === value, OR (if the field is an array) the array includes value
|
|
24
|
+
* array [a, b, c]
|
|
25
|
+
* membership/intersection: field is one of these, OR the field array
|
|
26
|
+
* shares any element with these
|
|
27
|
+
* object { <op>: <arg>, ... } (multiple ops AND together)
|
|
28
|
+
* eq equals (scalar or array-includes)
|
|
29
|
+
* ne not eq
|
|
30
|
+
* in [..] — field (or any of its array elements) is in the list
|
|
31
|
+
* nin not in
|
|
32
|
+
* exists true|false — field is present / absent
|
|
33
|
+
* contains field array includes this value
|
|
34
|
+
* containsAll [..] — field array includes all of these
|
|
35
|
+
* containsAny [..] — field array includes any of these
|
|
36
|
+
* matches regex string, case-sensitive (tests String(field); arrays: any)
|
|
37
|
+
* imatches regex string, case-insensitive
|
|
38
|
+
* gt gte lt lte numeric comparison
|
|
39
|
+
*
|
|
40
|
+
* Combinators (reserved keys at any condition level):
|
|
41
|
+
* all: [ {..}, {..} ] every sub-condition true (AND)
|
|
42
|
+
* any: [ {..}, {..} ] at least one true (OR)
|
|
43
|
+
* not: { .. } sub-condition false
|
|
44
|
+
* Sibling field matchers next to combinators are AND-ed in.
|
|
45
|
+
*
|
|
46
|
+
* Two load-bearing edge cases preserved from the source:
|
|
47
|
+
* - An empty condition (`{}`) is INERT: it returns false, NOT match-all.
|
|
48
|
+
* - An unknown op never matches.
|
|
49
|
+
*/
|
|
50
|
+
export function asArray(v) {
|
|
51
|
+
if (Array.isArray(v))
|
|
52
|
+
return v;
|
|
53
|
+
if (v === undefined)
|
|
54
|
+
return [];
|
|
55
|
+
return [v];
|
|
56
|
+
}
|
|
57
|
+
// Scalar equality with light, predictable coercion: exact match, or
|
|
58
|
+
// string/number cross-compare so `version: 2` matches a subject "2".
|
|
59
|
+
export function scalarEq(a, b) {
|
|
60
|
+
if (a === b)
|
|
61
|
+
return true;
|
|
62
|
+
if ((typeof a === 'number' || typeof a === 'string') &&
|
|
63
|
+
(typeof b === 'number' || typeof b === 'string')) {
|
|
64
|
+
return String(a) === String(b);
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
// Resolve a (possibly dotted) field path against the subject object.
|
|
69
|
+
export function getField(subject, key) {
|
|
70
|
+
if (subject && typeof subject === 'object' && key in subject) {
|
|
71
|
+
return subject[key];
|
|
72
|
+
}
|
|
73
|
+
if (!key.includes('.'))
|
|
74
|
+
return undefined;
|
|
75
|
+
let cur = subject;
|
|
76
|
+
for (const part of key.split('.')) {
|
|
77
|
+
if (cur == null || typeof cur !== 'object')
|
|
78
|
+
return undefined;
|
|
79
|
+
cur = cur[part];
|
|
80
|
+
}
|
|
81
|
+
return cur;
|
|
82
|
+
}
|
|
83
|
+
export function toRegExp(arg, flags) {
|
|
84
|
+
if (typeof arg !== 'string')
|
|
85
|
+
return null;
|
|
86
|
+
try {
|
|
87
|
+
return new RegExp(arg, flags);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export function applyOp(op, value, arg) {
|
|
94
|
+
const vals = asArray(value);
|
|
95
|
+
switch (op) {
|
|
96
|
+
case 'eq':
|
|
97
|
+
return Array.isArray(value) ? value.some((v) => scalarEq(v, arg)) : scalarEq(value, arg);
|
|
98
|
+
case 'ne':
|
|
99
|
+
return !applyOp('eq', value, arg);
|
|
100
|
+
case 'in':
|
|
101
|
+
return asArray(arg).some((a) => vals.some((v) => scalarEq(v, a)));
|
|
102
|
+
case 'nin':
|
|
103
|
+
return !applyOp('in', value, arg);
|
|
104
|
+
case 'exists':
|
|
105
|
+
return (value !== undefined) === (arg !== false);
|
|
106
|
+
case 'contains':
|
|
107
|
+
return vals.some((v) => scalarEq(v, arg));
|
|
108
|
+
case 'containsAll':
|
|
109
|
+
return asArray(arg).every((a) => vals.some((v) => scalarEq(v, a)));
|
|
110
|
+
case 'containsAny':
|
|
111
|
+
return asArray(arg).some((a) => vals.some((v) => scalarEq(v, a)));
|
|
112
|
+
case 'matches':
|
|
113
|
+
case 'imatches': {
|
|
114
|
+
const re = toRegExp(arg, op === 'imatches' ? 'i' : '');
|
|
115
|
+
if (!re)
|
|
116
|
+
return false;
|
|
117
|
+
return vals.some((v) => (typeof v === 'string' || typeof v === 'number' ? re.test(String(v)) : false));
|
|
118
|
+
}
|
|
119
|
+
case 'gt':
|
|
120
|
+
case 'gte':
|
|
121
|
+
case 'lt':
|
|
122
|
+
case 'lte': {
|
|
123
|
+
const n = Number(value);
|
|
124
|
+
const t = Number(arg);
|
|
125
|
+
if (Number.isNaN(n) || Number.isNaN(t))
|
|
126
|
+
return false;
|
|
127
|
+
if (op === 'gt')
|
|
128
|
+
return n > t;
|
|
129
|
+
if (op === 'gte')
|
|
130
|
+
return n >= t;
|
|
131
|
+
if (op === 'lt')
|
|
132
|
+
return n < t;
|
|
133
|
+
return n <= t;
|
|
134
|
+
}
|
|
135
|
+
default:
|
|
136
|
+
return false; // unknown op never matches
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
export function matchField(value, matcher) {
|
|
140
|
+
if (matcher === null || ['string', 'number', 'boolean'].includes(typeof matcher)) {
|
|
141
|
+
if (Array.isArray(value))
|
|
142
|
+
return value.some((v) => scalarEq(v, matcher));
|
|
143
|
+
return scalarEq(value, matcher);
|
|
144
|
+
}
|
|
145
|
+
if (Array.isArray(matcher)) {
|
|
146
|
+
const vals = asArray(value);
|
|
147
|
+
return matcher.some((m) => vals.some((v) => scalarEq(v, m)));
|
|
148
|
+
}
|
|
149
|
+
if (matcher && typeof matcher === 'object') {
|
|
150
|
+
return Object.entries(matcher).every(([op, arg]) => applyOp(op, value, arg));
|
|
151
|
+
}
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Evaluate a `gate` predicate against a subject object.
|
|
156
|
+
*
|
|
157
|
+
* This is the one function consumers call. Returns true iff `condition`
|
|
158
|
+
* matches `subject`. A null/non-object condition and an empty object both
|
|
159
|
+
* return false (inert), never match-all.
|
|
160
|
+
*/
|
|
161
|
+
export function evalCondition(condition, subject) {
|
|
162
|
+
if (condition == null)
|
|
163
|
+
return false;
|
|
164
|
+
if (Array.isArray(condition))
|
|
165
|
+
return condition.every((sub) => evalCondition(sub, subject));
|
|
166
|
+
if (typeof condition !== 'object')
|
|
167
|
+
return false;
|
|
168
|
+
const c = condition;
|
|
169
|
+
const hasCombinator = 'all' in c || 'any' in c || 'not' in c;
|
|
170
|
+
if (hasCombinator) {
|
|
171
|
+
let ok = true;
|
|
172
|
+
if ('all' in c)
|
|
173
|
+
ok = ok && asArray(c.all).every((s) => evalCondition(s, subject));
|
|
174
|
+
if ('any' in c)
|
|
175
|
+
ok = ok && asArray(c.any).some((s) => evalCondition(s, subject));
|
|
176
|
+
if ('not' in c)
|
|
177
|
+
ok = ok && !evalCondition(c.not, subject);
|
|
178
|
+
for (const k of Object.keys(c)) {
|
|
179
|
+
if (k === 'all' || k === 'any' || k === 'not')
|
|
180
|
+
continue;
|
|
181
|
+
ok = ok && matchField(getField(subject, k), c[k]);
|
|
182
|
+
}
|
|
183
|
+
return ok;
|
|
184
|
+
}
|
|
185
|
+
const keys = Object.keys(c);
|
|
186
|
+
if (keys.length === 0)
|
|
187
|
+
return false; // empty condition is inert, not match-all
|
|
188
|
+
return keys.every((k) => matchField(getField(subject, k), c[k]));
|
|
189
|
+
}
|
package/dist/core/resolver.js
CHANGED
|
@@ -5,6 +5,7 @@ import { listDirs, pathExists, readText, readTextIfExists, walkFiles, } from './
|
|
|
5
5
|
import { readMarketplaceManifest, readPluginManifest } from './manifest.js';
|
|
6
6
|
import { parseFrontmatter } from './frontmatter.js';
|
|
7
7
|
import { ambiguous, notFound, usage } from './errors.js';
|
|
8
|
+
import { warn } from './output.js';
|
|
8
9
|
import { InputError } from './io.js';
|
|
9
10
|
import { builtinSkillsRoot, marketplacesDir, pluginsDir, projectScopeRoot, scopeSkillsDir, userScopeRoot, } from './scope.js';
|
|
10
11
|
function getBuiltinPlugin() {
|
|
@@ -94,6 +95,22 @@ export function effectiveSkillEnabled(pluginName, skillName, cfgs) {
|
|
|
94
95
|
}
|
|
95
96
|
return { enabled: true };
|
|
96
97
|
}
|
|
98
|
+
/** Parse one skill file's frontmatter at the COLLECTION layer: the strict
|
|
99
|
+
* parser THROWS on invalid YAML (the frontmatter contract is "valid YAML"),
|
|
100
|
+
* so a single malformed SKILL.md must not brick a whole `skill find`/catalog
|
|
101
|
+
* scan across the corpus. On a parse error, emit a clear scoped notice naming
|
|
102
|
+
* the file and return null so the iterator SKIPS it and continues. A doc with
|
|
103
|
+
* no frontmatter block parses fine (data === null) and is kept. */
|
|
104
|
+
function readSkillFrontmatterSafe(file) {
|
|
105
|
+
try {
|
|
106
|
+
return parseFrontmatter(readText(file));
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
const msg = (e instanceof Error ? e.message : String(e)).split('\n')[0];
|
|
110
|
+
warn(`invalid frontmatter in ${file}: ${msg}`);
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
97
114
|
export function listSkillsInPlugin(plugin, cfgs) {
|
|
98
115
|
const skillsRoot = join(plugin.root, SKILLS_DIR);
|
|
99
116
|
if (!pathExists(skillsRoot))
|
|
@@ -106,8 +123,10 @@ export function listSkillsInPlugin(plugin, cfgs) {
|
|
|
106
123
|
const name = rel.split(sep).join('/');
|
|
107
124
|
if (!name)
|
|
108
125
|
continue;
|
|
109
|
-
const
|
|
110
|
-
|
|
126
|
+
const parsed = readSkillFrontmatterSafe(file);
|
|
127
|
+
if (parsed === null)
|
|
128
|
+
continue;
|
|
129
|
+
const { data } = parsed;
|
|
111
130
|
const { enabled, disabledIn } = effectiveSkillEnabled(plugin.name, name, configs);
|
|
112
131
|
skills.push({
|
|
113
132
|
name,
|
|
@@ -136,8 +155,10 @@ export function listScopeRootSkills(scope, cfgs) {
|
|
|
136
155
|
const name = rel.split(sep).join('/');
|
|
137
156
|
if (!name)
|
|
138
157
|
continue;
|
|
139
|
-
const
|
|
140
|
-
|
|
158
|
+
const parsed = readSkillFrontmatterSafe(file);
|
|
159
|
+
if (parsed === null)
|
|
160
|
+
continue;
|
|
161
|
+
const { data } = parsed;
|
|
141
162
|
const { enabled, disabledIn } = effectiveSkillEnabled(SCOPE_SKILL_PLUGIN, name, configs);
|
|
142
163
|
skills.push({
|
|
143
164
|
name,
|
|
@@ -639,7 +660,7 @@ export function buildCategoryIndex(cat) {
|
|
|
639
660
|
lines.push(`- \`${fullId}\` — ${desc}`);
|
|
640
661
|
}
|
|
641
662
|
lines.push('');
|
|
642
|
-
lines.push(
|
|
663
|
+
lines.push('Read one with `crtr memory read <full-id>`. Search with `crtr memory find "<topic>"`.');
|
|
643
664
|
return lines.join('\n');
|
|
644
665
|
}
|
|
645
666
|
export function scopeRootsLabel() {
|
|
@@ -1,20 +1,61 @@
|
|
|
1
|
+
import { type WakeKind, type Wakeup } from '../canvas/index.js';
|
|
1
2
|
/** Base framing — present for every node. No path baked in: the caller carries
|
|
2
|
-
* the dir in the
|
|
3
|
+
* the dir in the `<crtr-context dir="…">` attribute. */
|
|
3
4
|
export declare const BASE_CONTEXT_NOTE: string;
|
|
4
5
|
/** Orchestrator-only framing: a resident orchestrator survives refresh cycles,
|
|
5
6
|
* so its context dir is also where a future cycle of itself resumes the work.
|
|
6
7
|
* Used inside the bearings block AND in the promotion guidance dump, so a
|
|
7
8
|
* promoted node gets the same note a born-orchestrator gets. */
|
|
8
9
|
export declare function orchestratorContextNote(nodeId: string): string;
|
|
9
|
-
/** The
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
*
|
|
19
|
-
*
|
|
10
|
+
/** The IDENTITY assertion that opens every boot intro — the load-bearing fix
|
|
11
|
+
* for the `--fork-from` impersonation bug. A fork copies the SOURCE node's
|
|
12
|
+
* entire first-person conversation into its own session, so without an explicit
|
|
13
|
+
* re-assertion the forked agent reads that copied narrative as its own and
|
|
14
|
+
* impersonates the source (it kept "monitoring itself" as a phantom child).
|
|
15
|
+
* This block names the node unambiguously. ONLY when the node IS a fork does it
|
|
16
|
+
* additionally name the source AND disown the copied first-person narrative as
|
|
17
|
+
* inherited reference material — a non-fork node's bearings are its first session
|
|
18
|
+
* entry, so there is no earlier narrative to disown (emitting a disown line there
|
|
19
|
+
* is dead weight that reads as a contradiction with no referent). Always the FIRST
|
|
20
|
+
* thing the node reads. Exported for testing. */
|
|
21
|
+
export declare function buildIdentityAssertion(nodeId: string): string;
|
|
22
|
+
/** Why a node woke or was born, as carried from the fired wakeup row to the
|
|
23
|
+
* injection seam. `ownerName` is the armer's resolved display name when it
|
|
24
|
+
* still exists (a reaped cron's armer renders as a bare id, never crashes). */
|
|
25
|
+
export interface WakeOrigin {
|
|
26
|
+
/** The firing kind. The <crtr-wake> block is rendered for 'spawn' (birth) and
|
|
27
|
+
* 'bare' (revive); 'noted'/'deadline' self-mark at the message seam instead. */
|
|
28
|
+
kind: WakeKind;
|
|
29
|
+
/** The armer node id (wakeups.owner_id), or null if somehow absent. */
|
|
30
|
+
ownerId: string | null;
|
|
31
|
+
/** The armer's display name, resolved if it still exists. */
|
|
32
|
+
ownerName?: string;
|
|
33
|
+
/** wakeups.created — when the wake was armed (ISO). */
|
|
34
|
+
armedAt: string;
|
|
35
|
+
/** wakeups.recur JSON when recurring, else null/undefined for a one-shot. */
|
|
36
|
+
recur?: string | null;
|
|
37
|
+
}
|
|
38
|
+
/** Build a WakeOrigin from a fired wakeup row, resolving the armer's display
|
|
39
|
+
* name if the node still exists. The daemon calls this at fire time for the
|
|
40
|
+
* bare-revive and spawn-birth seams. */
|
|
41
|
+
export declare function wakeOriginFrom(w: Wakeup): WakeOrigin;
|
|
42
|
+
/** The <crtr-wake> provenance block — load-bearing agent-facing prose read by
|
|
43
|
+
* every wake-born or wake-woken node. Decision-first: it leads with the fact a
|
|
44
|
+
* TIMER (not a message/event) caused this turn, names the wake kind, surfaces
|
|
45
|
+
* the cadence for a recurrence (so the agent knows it is one run of a standing
|
|
46
|
+
* job, not a one-off), and ends with the directive. The spawn (birth) variant
|
|
47
|
+
* names the ARMER explicitly ("armed by node X") so the newborn never reads that
|
|
48
|
+
* id as its own; the bare (revive) variant drops armer attribution entirely — a
|
|
49
|
+
* bare wake can be armed for a node by ANOTHER (`--node`), and who armed it is
|
|
50
|
+
* not decision-relevant to a timed re-check. No timestamp is rendered ("to fire
|
|
51
|
+
* now" / the cadence already carry the signal; a raw ISO instant is noise an
|
|
52
|
+
* agent cannot cheaply turn into an elapsed delta). Rendered for 'spawn' and
|
|
53
|
+
* 'bare'; other kinds self-mark elsewhere. */
|
|
54
|
+
export declare function buildWakeBearings(origin: WakeOrigin): string;
|
|
55
|
+
/** The full boot intro: the IDENTITY assertion (always first, so it overrides
|
|
56
|
+
* any copied-in persona) followed by the `<crtr-context>` bearings block. Base
|
|
57
|
+
* framing rides for EVERY node; the across-cycles context-dir note is added
|
|
58
|
+
* ONLY for an orchestrator (by mode) — the one node whose dir a future cycle
|
|
59
|
+
* resumes from. The `## References` block (substrate reference docs +
|
|
60
|
+
* node-local docs) replaces the old `<memory>` block. */
|
|
20
61
|
export declare function buildContextBearings(nodeId: string): string;
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
// bearings.ts — the <crtr-
|
|
2
|
-
// deliver it so they can
|
|
1
|
+
// bearings.ts — the boot-intro prose (the <crtr-identity> assertion + the
|
|
2
|
+
// <crtr-context> framing), shared by the paths that deliver it so they can
|
|
3
|
+
// never drift:
|
|
3
4
|
//
|
|
4
5
|
// • the context-intro pi-extension injects buildContextBearings() as the
|
|
5
|
-
// node's first session message in every brand-new chat
|
|
6
|
+
// node's first session message in every brand-new chat. It opens with a
|
|
7
|
+
// <crtr-identity> block (buildIdentityAssertion) that names the node and,
|
|
8
|
+
// ONLY for a fork, disowns the source's copied first-person narrative as
|
|
9
|
+
// inherited context — the fix for the `--fork-from` bug where a fork copies
|
|
10
|
+
// the source's whole conversation and then impersonates it. A non-fork
|
|
11
|
+
// node's bearings are its FIRST entry, so there is nothing earlier to
|
|
12
|
+
// disown; it gets only the declarative identity line;
|
|
6
13
|
// • promote.ts folds orchestratorContextNote() into the promotion guidance
|
|
7
14
|
// dump, so a node that becomes an orchestrator MID-LIFE gets the
|
|
8
15
|
// orchestrator framing it never received at spawn — it spawned as a base
|
|
@@ -12,17 +19,17 @@
|
|
|
12
19
|
// one place other nodes on the canvas can read from, so it is for documents
|
|
13
20
|
// worth a shared reference, NOT a task tracker, and NOT a "future memory-wiped
|
|
14
21
|
// you" stash (a terminal worker has no future cycle — that framing only makes
|
|
15
|
-
// sense once a node is a resident orchestrator).
|
|
22
|
+
// sense once a node is a resident orchestrator). The `## References` block
|
|
23
|
+
// (substrate reference docs + node-local docs) rides into the context message.
|
|
16
24
|
//
|
|
17
|
-
// Orchestrator addendum (
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
import {
|
|
23
|
-
import { hasMemory, memoryDir, readMemory, hasUserMemory, userMemoryDir, readUserMemory, hasProjectMemory, projectMemoryDir, readProjectMemory, } from './memory.js';
|
|
25
|
+
// Orchestrator addendum (gated on orchestrator MODE): the dir ALSO survives
|
|
26
|
+
// refresh cycles, so it is where a future cycle of the orchestrator resumes.
|
|
27
|
+
// This across-cycles note is the ONE thing a terminal worker's bearings drop.
|
|
28
|
+
import { contextDir, getNode, fullName } from '../canvas/index.js';
|
|
29
|
+
import { cadenceDisplay } from '../wake.js';
|
|
30
|
+
import { renderReferencesBlock } from '../substrate/index.js';
|
|
24
31
|
/** Base framing — present for every node. No path baked in: the caller carries
|
|
25
|
-
* the dir in the
|
|
32
|
+
* the dir in the `<crtr-context dir="…">` attribute. */
|
|
26
33
|
export const BASE_CONTEXT_NOTE = 'This is your context directory — durable scratch space on disk, and the one place the other ' +
|
|
27
34
|
'nodes on the canvas can read from. Put documents here that you want to share by reference ' +
|
|
28
35
|
'instead of re-explaining them in a prompt: specs, designs, findings, notes worth pointing a ' +
|
|
@@ -36,57 +43,123 @@ export function orchestratorContextNote(nodeId) {
|
|
|
36
43
|
`also where a future cycle of you resumes the work — keep the working notes and decisions a ` +
|
|
37
44
|
`refreshed you would need there, alongside the docs you share with the nodes you spawn.`);
|
|
38
45
|
}
|
|
39
|
-
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* the
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
/** The IDENTITY assertion that opens every boot intro — the load-bearing fix
|
|
47
|
+
* for the `--fork-from` impersonation bug. A fork copies the SOURCE node's
|
|
48
|
+
* entire first-person conversation into its own session, so without an explicit
|
|
49
|
+
* re-assertion the forked agent reads that copied narrative as its own and
|
|
50
|
+
* impersonates the source (it kept "monitoring itself" as a phantom child).
|
|
51
|
+
* This block names the node unambiguously. ONLY when the node IS a fork does it
|
|
52
|
+
* additionally name the source AND disown the copied first-person narrative as
|
|
53
|
+
* inherited reference material — a non-fork node's bearings are its first session
|
|
54
|
+
* entry, so there is no earlier narrative to disown (emitting a disown line there
|
|
55
|
+
* is dead weight that reads as a contradiction with no referent). Always the FIRST
|
|
56
|
+
* thing the node reads. Exported for testing. */
|
|
57
|
+
export function buildIdentityAssertion(nodeId) {
|
|
58
|
+
const meta = getNode(nodeId);
|
|
59
|
+
const name = meta?.name ?? nodeId;
|
|
60
|
+
const kind = meta?.kind ?? 'general';
|
|
61
|
+
const mode = meta?.mode ?? 'base';
|
|
62
|
+
const lines = [
|
|
63
|
+
'<crtr-identity>',
|
|
64
|
+
`You are node ${nodeId} — name "${name}", kind ${kind}, mode ${mode}.`,
|
|
65
|
+
];
|
|
66
|
+
const forkFrom = meta?.fork_from;
|
|
67
|
+
if (forkFrom !== undefined && forkFrom !== null && forkFrom !== '') {
|
|
68
|
+
// Name the source: a known node id gets its human label; a raw path/uuid
|
|
69
|
+
// passes through as-is.
|
|
70
|
+
const src = getNode(forkFrom);
|
|
71
|
+
const sourceLabel = src !== null ? `${forkFrom} ("${fullName(src)}")` : forkFrom;
|
|
72
|
+
lines.push(`You are a FORK of ${sourceLabel}: at spawn pi COPIED that node's conversation into your ` +
|
|
73
|
+
`session as a starting point. You are NOT ${sourceLabel}. Everything earlier in this ` +
|
|
74
|
+
'conversation is THEIR first-person history — inherited reference material, not your own ' +
|
|
75
|
+
'past. Do not speak or act as them, do not continue their task as if it were yours, and do ' +
|
|
76
|
+
'not "monitor yourself" as though you were a child they spawned.');
|
|
77
|
+
}
|
|
78
|
+
lines.push('</crtr-identity>');
|
|
79
|
+
return lines.join('\n');
|
|
80
|
+
}
|
|
81
|
+
/** Build a WakeOrigin from a fired wakeup row, resolving the armer's display
|
|
82
|
+
* name if the node still exists. The daemon calls this at fire time for the
|
|
83
|
+
* bare-revive and spawn-birth seams. */
|
|
84
|
+
export function wakeOriginFrom(w) {
|
|
85
|
+
const owner = w.owner_id != null ? getNode(w.owner_id) : null;
|
|
86
|
+
return {
|
|
87
|
+
kind: w.kind,
|
|
88
|
+
ownerId: w.owner_id ?? null,
|
|
89
|
+
ownerName: owner !== null ? fullName(owner) : undefined,
|
|
90
|
+
armedAt: w.created,
|
|
91
|
+
recur: w.recur,
|
|
92
|
+
};
|
|
52
93
|
}
|
|
53
|
-
/** The
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
94
|
+
/** The armer rendered for prose, ROLE-explicit so a newborn never mistakes the
|
|
95
|
+
* id for its own: `node <id> ("<name>")` when the name resolved, `node <id>
|
|
96
|
+
* (now gone)` when the armer was reaped, `an unknown node` only if the owner id
|
|
97
|
+
* is somehow absent (owner_id is NOT NULL on every real row — defensive). */
|
|
98
|
+
function armerPhrase(origin) {
|
|
99
|
+
if (origin.ownerId === null || origin.ownerId === '')
|
|
100
|
+
return 'an unknown node';
|
|
101
|
+
if (origin.ownerName !== undefined)
|
|
102
|
+
return `node ${origin.ownerId} ("${origin.ownerName}")`;
|
|
103
|
+
return `node ${origin.ownerId} (now gone)`;
|
|
104
|
+
}
|
|
105
|
+
/** The <crtr-wake> provenance block — load-bearing agent-facing prose read by
|
|
106
|
+
* every wake-born or wake-woken node. Decision-first: it leads with the fact a
|
|
107
|
+
* TIMER (not a message/event) caused this turn, names the wake kind, surfaces
|
|
108
|
+
* the cadence for a recurrence (so the agent knows it is one run of a standing
|
|
109
|
+
* job, not a one-off), and ends with the directive. The spawn (birth) variant
|
|
110
|
+
* names the ARMER explicitly ("armed by node X") so the newborn never reads that
|
|
111
|
+
* id as its own; the bare (revive) variant drops armer attribution entirely — a
|
|
112
|
+
* bare wake can be armed for a node by ANOTHER (`--node`), and who armed it is
|
|
113
|
+
* not decision-relevant to a timed re-check. No timestamp is rendered ("to fire
|
|
114
|
+
* now" / the cadence already carry the signal; a raw ISO instant is noise an
|
|
115
|
+
* agent cannot cheaply turn into an elapsed delta). Rendered for 'spawn' and
|
|
116
|
+
* 'bare'; other kinds self-mark elsewhere. */
|
|
117
|
+
export function buildWakeBearings(origin) {
|
|
118
|
+
const recurring = origin.recur !== null && origin.recur !== undefined && origin.recur !== '';
|
|
119
|
+
const cadence = recurring ? cadenceDisplay(origin.recur) : null;
|
|
120
|
+
let body;
|
|
121
|
+
if (origin.kind === 'spawn') {
|
|
122
|
+
const armer = armerPhrase(origin);
|
|
123
|
+
body = recurring
|
|
124
|
+
? `You were BORN by a scheduled wake, not spawned on demand — a recurring spawn-cron armed by ` +
|
|
125
|
+
`${armer}, firing ${cadence}. The runtime re-births a fresh node like you on this cadence whether ` +
|
|
126
|
+
`or not earlier runs survived: you are one run of a standing job, not a one-off, and you inherit ` +
|
|
127
|
+
`nothing from prior runs but this task. Your task follows.`
|
|
128
|
+
: `You were BORN by a scheduled wake, not spawned on demand — a one-shot deferred birth armed by ` +
|
|
129
|
+
`${armer} to fire now. You are its only run, not a recurring job. Your task follows.`;
|
|
64
130
|
}
|
|
65
|
-
|
|
66
|
-
|
|
131
|
+
else {
|
|
132
|
+
// bare scheduled alarm (the only kind delivered through this block at the
|
|
133
|
+
// revive seam; noted/deadline self-mark via their inbox label instead).
|
|
134
|
+
body = recurring
|
|
135
|
+
? `You woke because a recurring scheduled alarm fired (${cadence}) — a timer, NOT a new message or ` +
|
|
136
|
+
`request. This is one tick of a standing re-check: re-read your roadmap and the disk bearings below ` +
|
|
137
|
+
`and decide what this moment calls for.`
|
|
138
|
+
: `You woke because a scheduled alarm fired — a timer, NOT a new message or request. This is a timed ` +
|
|
139
|
+
`re-check, not a new task: re-read your roadmap and the disk bearings below and decide what this ` +
|
|
140
|
+
`moment calls for.`;
|
|
67
141
|
}
|
|
68
|
-
|
|
69
|
-
const n = stanzas.length;
|
|
70
|
-
return ('<memory>\n' +
|
|
71
|
-
`Long-term memory, ${n} scope${n === 1 ? '' : 's'}. Each line ` +
|
|
72
|
-
'`- [Title](slug.md) — hook`; load a detail file by slug from the scope dir on demand. ' +
|
|
73
|
-
'Write a new fact to the scope matching its `type` (see "Your long-term memory").\n\n' +
|
|
74
|
-
stanzas.join('\n\n') +
|
|
75
|
-
'\n</memory>');
|
|
142
|
+
return `<crtr-wake>\n${body}\n</crtr-wake>`;
|
|
76
143
|
}
|
|
77
|
-
/** The full
|
|
78
|
-
*
|
|
79
|
-
*
|
|
144
|
+
/** The full boot intro: the IDENTITY assertion (always first, so it overrides
|
|
145
|
+
* any copied-in persona) followed by the `<crtr-context>` bearings block. Base
|
|
146
|
+
* framing rides for EVERY node; the across-cycles context-dir note is added
|
|
147
|
+
* ONLY for an orchestrator (by mode) — the one node whose dir a future cycle
|
|
148
|
+
* resumes from. The `## References` block (substrate reference docs +
|
|
149
|
+
* node-local docs) replaces the old `<memory>` block. */
|
|
80
150
|
export function buildContextBearings(nodeId) {
|
|
151
|
+
const identity = buildIdentityAssertion(nodeId);
|
|
81
152
|
const dir = contextDir(nodeId);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
153
|
+
const node = getNode(nodeId);
|
|
154
|
+
const parts = [BASE_CONTEXT_NOTE];
|
|
155
|
+
// Orchestrator-only: the across-cycles framing (a terminal has no future cycle).
|
|
156
|
+
if (node?.mode === 'orchestrator')
|
|
157
|
+
parts.push(orchestratorContextNote(nodeId));
|
|
158
|
+
// The substrate references block: eligible `reference` docs at their
|
|
159
|
+
// system-prompt rung + node-local memory docs; dropped (returns '') when
|
|
160
|
+
// nothing is eligible.
|
|
161
|
+
const references = renderReferencesBlock(nodeId);
|
|
162
|
+
if (references !== '')
|
|
163
|
+
parts.push(references);
|
|
164
|
+
return `${identity}\n<crtr-context dir="${dir}">\n${parts.join('\n')}\n</crtr-context>`;
|
|
92
165
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// broker-cli.ts — the headless broker's dedicated detached entry (plan T4 /
|
|
2
|
+
// decision §1.13). Spawned directly via `spawn(execPath, [thisFile, nodeId], …)`
|
|
3
|
+
// by HeadlessBrokerHost.launch (T6) — NEVER routed through src/cli.ts, which
|
|
4
|
+
// would run the whole bootstrap chain (auto-update / scope-init / slash-template
|
|
5
|
+
// rewrite) on every broker boot. Mirrors src/daemon/crtrd-cli.ts: parse the one
|
|
6
|
+
// positional arg and hand off; keep this file a thin shim.
|
|
7
|
+
import { runBroker, disposeActiveSession } from './broker.js';
|
|
8
|
+
const nodeId = process.argv[2];
|
|
9
|
+
if (nodeId === undefined || nodeId.trim() === '') {
|
|
10
|
+
process.stderr.write('[broker] usage: broker-cli <nodeId>\n');
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
// M3 (scout mq5thyli): dispose the live engine before a FATAL exit. The bash tool
|
|
14
|
+
// spawns children `detached` (own pgid); only session.dispose() (→ abortBash /
|
|
15
|
+
// agent.abort → killProcessTree) reaps them. The graceful path (shutdownHandler /
|
|
16
|
+
// SIGTERM → disposeAndExit) already disposes; this routes the crash path —
|
|
17
|
+
// uncaughtException / unhandledRejection / a runBroker reject — through dispose
|
|
18
|
+
// too, so a fatal error never ORPHANS in-flight bash subprocesses. dispose is
|
|
19
|
+
// idempotent + a no-op once the graceful path has run.
|
|
20
|
+
function disposeAndExit(code) {
|
|
21
|
+
try {
|
|
22
|
+
disposeActiveSession();
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
/* dispose must not block the fatal exit */
|
|
26
|
+
}
|
|
27
|
+
process.exit(code);
|
|
28
|
+
}
|
|
29
|
+
// Last-resort safety net (review N-4): a stray throw/rejection from the SDK event
|
|
30
|
+
// stream or an extension's async hook would otherwise crash the broker on Node's
|
|
31
|
+
// default handler. Now that the host redirects the broker's stderr to the node's
|
|
32
|
+
// job/broker.log (M-2), log it there and exit non-zero — pre-session_start this
|
|
33
|
+
// folds into the daemon's boot-failure detection (M-1: pid stays null past the
|
|
34
|
+
// boot grace → surfaceBootFailure); post-session_start it is a clean grace-revive.
|
|
35
|
+
process.on('uncaughtException', (err) => {
|
|
36
|
+
process.stderr.write(`[broker] uncaughtException: ${err instanceof Error ? err.stack ?? err.message : String(err)}\n`);
|
|
37
|
+
disposeAndExit(1);
|
|
38
|
+
});
|
|
39
|
+
process.on('unhandledRejection', (reason) => {
|
|
40
|
+
process.stderr.write(`[broker] unhandledRejection: ${reason instanceof Error ? reason.stack ?? reason.message : String(reason)}\n`);
|
|
41
|
+
disposeAndExit(1);
|
|
42
|
+
});
|
|
43
|
+
runBroker(nodeId).catch((err) => {
|
|
44
|
+
process.stderr.write(`[broker] fatal: ${err instanceof Error ? err.stack ?? err.message : String(err)}\n`);
|
|
45
|
+
disposeAndExit(1);
|
|
46
|
+
});
|