@cleocode/cant 2026.4.11 → 2026.4.13
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/bundle.d.ts +158 -0
- package/dist/bundle.js +423 -0
- package/dist/composer.d.ts +165 -0
- package/dist/composer.js +160 -0
- package/dist/hierarchy.d.ts +95 -0
- package/dist/hierarchy.js +103 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +28 -1
- package/dist/mental-model.d.ts +142 -0
- package/dist/mental-model.js +194 -0
- package/dist/worktree.d.ts +106 -0
- package/dist/worktree.js +179 -0
- package/package.json +3 -3
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JIT Agent Composer -- composes agent spawn payloads at runtime.
|
|
3
|
+
*
|
|
4
|
+
* Takes a compiled agent definition + context sources + mental model
|
|
5
|
+
* and produces a token-budgeted system prompt with tier escalation.
|
|
6
|
+
*
|
|
7
|
+
* Implements ULTRAPLAN section 9.3: at spawn time, the composer resolves context
|
|
8
|
+
* sources from BRAIN, loads the agent's mental model, enforces token budgets
|
|
9
|
+
* per tier, and escalates tiers when content overflows.
|
|
10
|
+
*
|
|
11
|
+
* @packageDocumentation
|
|
12
|
+
*/
|
|
13
|
+
/** Token budget caps per tier. */
|
|
14
|
+
export declare const TIER_CAPS: {
|
|
15
|
+
readonly low: {
|
|
16
|
+
readonly systemPrompt: 4000;
|
|
17
|
+
readonly mentalModel: 0;
|
|
18
|
+
readonly contextSources: 0;
|
|
19
|
+
};
|
|
20
|
+
readonly mid: {
|
|
21
|
+
readonly systemPrompt: 12000;
|
|
22
|
+
readonly mentalModel: 1000;
|
|
23
|
+
readonly contextSources: 4000;
|
|
24
|
+
};
|
|
25
|
+
readonly high: {
|
|
26
|
+
readonly systemPrompt: 32000;
|
|
27
|
+
readonly mentalModel: 2000;
|
|
28
|
+
readonly contextSources: 12000;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
/** Agent tier levels: low, mid, or high. */
|
|
32
|
+
export type Tier = 'low' | 'mid' | 'high';
|
|
33
|
+
/**
|
|
34
|
+
* Return the next tier up for escalation.
|
|
35
|
+
*
|
|
36
|
+
* @param tier - The current tier.
|
|
37
|
+
* @returns The next tier, or `null` if already at the highest tier.
|
|
38
|
+
*/
|
|
39
|
+
export declare function escalateTier(tier: Tier): Tier | null;
|
|
40
|
+
/**
|
|
41
|
+
* Rough token estimation: approximately 4 characters per token.
|
|
42
|
+
*
|
|
43
|
+
* @param text - The text to estimate tokens for.
|
|
44
|
+
* @returns The estimated token count.
|
|
45
|
+
*/
|
|
46
|
+
export declare function estimateTokens(text: string): number;
|
|
47
|
+
/** A context source query result from BRAIN. */
|
|
48
|
+
export interface ContextSlice {
|
|
49
|
+
/** The source category (e.g. "patterns", "decisions", "conventions"). */
|
|
50
|
+
source: string;
|
|
51
|
+
/** The retrieved text content. */
|
|
52
|
+
content: string;
|
|
53
|
+
/** Pre-computed token count for this slice. */
|
|
54
|
+
tokens: number;
|
|
55
|
+
}
|
|
56
|
+
/** A mental model loaded from BRAIN. */
|
|
57
|
+
export interface MentalModelSlice {
|
|
58
|
+
/** The mental model text content. */
|
|
59
|
+
content: string;
|
|
60
|
+
/** Pre-computed token count for this slice. */
|
|
61
|
+
tokens: number;
|
|
62
|
+
/** ISO date of the last consolidation, or `null` if never consolidated. */
|
|
63
|
+
lastConsolidated: string | null;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Interface for BRAIN context retrieval (mockable for testing).
|
|
67
|
+
*
|
|
68
|
+
* @remarks
|
|
69
|
+
* Implementations should query the BRAIN database for relevant context
|
|
70
|
+
* and mental model data, respecting the provided token budgets.
|
|
71
|
+
*/
|
|
72
|
+
export interface ContextProvider {
|
|
73
|
+
/** Retrieve context for a named source with a token budget. */
|
|
74
|
+
queryContext(source: string, query: string, maxTokens: number): Promise<ContextSlice>;
|
|
75
|
+
/** Load the mental model for an agent + project. */
|
|
76
|
+
loadMentalModel(agentName: string, projectHash: string, maxTokens: number): Promise<MentalModelSlice>;
|
|
77
|
+
}
|
|
78
|
+
/** The composed spawn payload returned by {@link composeSpawnPayload}. */
|
|
79
|
+
export interface SpawnPayload {
|
|
80
|
+
/** The agent name. */
|
|
81
|
+
agentName: string;
|
|
82
|
+
/** The resolved tier (may be escalated from declared tier). */
|
|
83
|
+
resolvedTier: Tier;
|
|
84
|
+
/** Whether the tier was escalated from the declared tier. */
|
|
85
|
+
escalated: boolean;
|
|
86
|
+
/** The original declared tier. */
|
|
87
|
+
declaredTier: Tier;
|
|
88
|
+
/** The composed system prompt. */
|
|
89
|
+
systemPrompt: string;
|
|
90
|
+
/** Token count of the system prompt. */
|
|
91
|
+
systemPromptTokens: number;
|
|
92
|
+
/** The model to use (from the tier matrix). */
|
|
93
|
+
model: string;
|
|
94
|
+
/** Fallback models. */
|
|
95
|
+
fallbackModels: string[];
|
|
96
|
+
/** Skills to load. */
|
|
97
|
+
skills: string[];
|
|
98
|
+
/** Tools allowed. */
|
|
99
|
+
tools: string[];
|
|
100
|
+
/** Context sources that were injected. */
|
|
101
|
+
injectedContextSources: string[];
|
|
102
|
+
/** Whether mental model was injected. */
|
|
103
|
+
mentalModelInjected: boolean;
|
|
104
|
+
}
|
|
105
|
+
/** Agent definition extracted from a compiled `.cant` bundle. */
|
|
106
|
+
export interface AgentDefinition {
|
|
107
|
+
/** The agent name as declared in the `.cant` file. */
|
|
108
|
+
name: string;
|
|
109
|
+
/** The declared tier for this agent. */
|
|
110
|
+
tier: Tier;
|
|
111
|
+
/** The base system prompt for this agent. */
|
|
112
|
+
prompt: string;
|
|
113
|
+
/** Skills this agent should load. */
|
|
114
|
+
skills: string[];
|
|
115
|
+
/** Tools this agent is allowed to use. */
|
|
116
|
+
tools: string[];
|
|
117
|
+
/** Context sources to resolve from BRAIN at spawn time. */
|
|
118
|
+
contextSources: Array<{
|
|
119
|
+
/** The BRAIN source category. */
|
|
120
|
+
source: string;
|
|
121
|
+
/** The query string for context retrieval. */
|
|
122
|
+
query: string;
|
|
123
|
+
/** Maximum number of entries to retrieve. */
|
|
124
|
+
maxEntries: number;
|
|
125
|
+
}>;
|
|
126
|
+
/**
|
|
127
|
+
* Mental model configuration, or `null` if not enabled.
|
|
128
|
+
*
|
|
129
|
+
* @remarks
|
|
130
|
+
* When enabled, the composer loads the agent's mental model from BRAIN
|
|
131
|
+
* and injects it with a validation prefix instructing the agent to
|
|
132
|
+
* re-evaluate each claim against the current code state.
|
|
133
|
+
*/
|
|
134
|
+
mentalModel: {
|
|
135
|
+
/** Whether the mental model is enabled. */
|
|
136
|
+
enabled: boolean;
|
|
137
|
+
/** Scope: project-specific or global. */
|
|
138
|
+
scope: 'project' | 'global';
|
|
139
|
+
/** Maximum tokens allocated for mental model content. */
|
|
140
|
+
maxTokens: number;
|
|
141
|
+
/** Whether to add a validation prefix before the mental model. */
|
|
142
|
+
validateOnLoad: boolean;
|
|
143
|
+
} | null;
|
|
144
|
+
/** Behavior when total tokens exceed the tier cap. */
|
|
145
|
+
onOverflow: 'escalate_tier' | 'fail';
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Compose a spawn payload for an agent.
|
|
149
|
+
*
|
|
150
|
+
* Implements ULTRAPLAN section 9.3 spawn-time composition:
|
|
151
|
+
* 1. Start with agent's declared tier
|
|
152
|
+
* 2. Resolve `context_sources` via {@link ContextProvider}
|
|
153
|
+
* 3. Load `mental_model` via {@link ContextProvider}
|
|
154
|
+
* 4. Check token budget -- if over, escalate tier (per L4)
|
|
155
|
+
* 5. Compose final system prompt
|
|
156
|
+
*
|
|
157
|
+
* @param agent - The agent definition from a compiled `.cant` bundle.
|
|
158
|
+
* @param contextProvider - The BRAIN context retrieval interface.
|
|
159
|
+
* @param projectHash - Hash identifying the current project for scoped queries.
|
|
160
|
+
* @returns The composed spawn payload ready for PiHarness.spawnSubagent.
|
|
161
|
+
*
|
|
162
|
+
* @throws Error if context exceeds the `high` tier cap and `onOverflow` is `'fail'`
|
|
163
|
+
* @throws Error if context exceeds the `high` tier cap even with escalation
|
|
164
|
+
*/
|
|
165
|
+
export declare function composeSpawnPayload(agent: AgentDefinition, contextProvider: ContextProvider, projectHash: string): Promise<SpawnPayload>;
|
package/dist/composer.js
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* JIT Agent Composer -- composes agent spawn payloads at runtime.
|
|
4
|
+
*
|
|
5
|
+
* Takes a compiled agent definition + context sources + mental model
|
|
6
|
+
* and produces a token-budgeted system prompt with tier escalation.
|
|
7
|
+
*
|
|
8
|
+
* Implements ULTRAPLAN section 9.3: at spawn time, the composer resolves context
|
|
9
|
+
* sources from BRAIN, loads the agent's mental model, enforces token budgets
|
|
10
|
+
* per tier, and escalates tiers when content overflows.
|
|
11
|
+
*
|
|
12
|
+
* @packageDocumentation
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.TIER_CAPS = void 0;
|
|
16
|
+
exports.escalateTier = escalateTier;
|
|
17
|
+
exports.estimateTokens = estimateTokens;
|
|
18
|
+
exports.composeSpawnPayload = composeSpawnPayload;
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Token budget caps per tier (ULTRAPLAN section 9.4)
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
/** Token budget caps per tier. */
|
|
23
|
+
exports.TIER_CAPS = {
|
|
24
|
+
low: { systemPrompt: 4000, mentalModel: 0, contextSources: 0 },
|
|
25
|
+
mid: { systemPrompt: 12000, mentalModel: 1000, contextSources: 4000 },
|
|
26
|
+
high: { systemPrompt: 32000, mentalModel: 2000, contextSources: 12000 },
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Return the next tier up for escalation.
|
|
30
|
+
*
|
|
31
|
+
* @param tier - The current tier.
|
|
32
|
+
* @returns The next tier, or `null` if already at the highest tier.
|
|
33
|
+
*/
|
|
34
|
+
function escalateTier(tier) {
|
|
35
|
+
if (tier === 'low')
|
|
36
|
+
return 'mid';
|
|
37
|
+
if (tier === 'mid')
|
|
38
|
+
return 'high';
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Rough token estimation: approximately 4 characters per token.
|
|
43
|
+
*
|
|
44
|
+
* @param text - The text to estimate tokens for.
|
|
45
|
+
* @returns The estimated token count.
|
|
46
|
+
*/
|
|
47
|
+
function estimateTokens(text) {
|
|
48
|
+
return Math.ceil(text.length / 4);
|
|
49
|
+
}
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Model selection per tier (mirrors cant-router section 11)
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
/** Model selection per tier. */
|
|
54
|
+
const TIER_MODELS = {
|
|
55
|
+
low: { primary: 'claude-haiku-4-5', fallbacks: ['kimi-k2.5'] },
|
|
56
|
+
mid: { primary: 'claude-sonnet-4-6', fallbacks: ['kimi-k2.5', 'claude-haiku-4-5'] },
|
|
57
|
+
high: { primary: 'claude-opus-4-6', fallbacks: ['claude-sonnet-4-6', 'kimi-k2.5'] },
|
|
58
|
+
};
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
// Mental model validation prefix (ULTRAPLAN section 12.3)
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
/** Validation prefix injected before mental model content. */
|
|
63
|
+
const MENTAL_MODEL_VALIDATION_PREFIX = 'VALIDATE THIS MENTAL MODEL. Re-evaluate each claim against current ' +
|
|
64
|
+
'code state. Mental models are dynamic per project; assume drift.';
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Core composer
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
/**
|
|
69
|
+
* Compose a spawn payload for an agent.
|
|
70
|
+
*
|
|
71
|
+
* Implements ULTRAPLAN section 9.3 spawn-time composition:
|
|
72
|
+
* 1. Start with agent's declared tier
|
|
73
|
+
* 2. Resolve `context_sources` via {@link ContextProvider}
|
|
74
|
+
* 3. Load `mental_model` via {@link ContextProvider}
|
|
75
|
+
* 4. Check token budget -- if over, escalate tier (per L4)
|
|
76
|
+
* 5. Compose final system prompt
|
|
77
|
+
*
|
|
78
|
+
* @param agent - The agent definition from a compiled `.cant` bundle.
|
|
79
|
+
* @param contextProvider - The BRAIN context retrieval interface.
|
|
80
|
+
* @param projectHash - Hash identifying the current project for scoped queries.
|
|
81
|
+
* @returns The composed spawn payload ready for PiHarness.spawnSubagent.
|
|
82
|
+
*
|
|
83
|
+
* @throws Error if context exceeds the `high` tier cap and `onOverflow` is `'fail'`
|
|
84
|
+
* @throws Error if context exceeds the `high` tier cap even with escalation
|
|
85
|
+
*/
|
|
86
|
+
async function composeSpawnPayload(agent, contextProvider, projectHash) {
|
|
87
|
+
let currentTier = agent.tier;
|
|
88
|
+
let escalated = false;
|
|
89
|
+
// Step 1: Build base prompt
|
|
90
|
+
const basePrompt = agent.prompt;
|
|
91
|
+
const baseTokens = estimateTokens(basePrompt);
|
|
92
|
+
// Step 2: Resolve context sources within the current tier's budget
|
|
93
|
+
const contextSlices = [];
|
|
94
|
+
let contextBudget = exports.TIER_CAPS[currentTier].contextSources;
|
|
95
|
+
for (const src of agent.contextSources) {
|
|
96
|
+
if (contextBudget <= 0)
|
|
97
|
+
break;
|
|
98
|
+
const slice = await contextProvider.queryContext(src.source, src.query, contextBudget);
|
|
99
|
+
contextSlices.push(slice);
|
|
100
|
+
contextBudget -= slice.tokens;
|
|
101
|
+
}
|
|
102
|
+
// Step 3: Load mental model if enabled and tier allows it
|
|
103
|
+
let mentalModel = null;
|
|
104
|
+
if (agent.mentalModel?.enabled) {
|
|
105
|
+
const mmCap = exports.TIER_CAPS[currentTier].mentalModel;
|
|
106
|
+
if (mmCap > 0) {
|
|
107
|
+
mentalModel = await contextProvider.loadMentalModel(agent.name, agent.mentalModel.scope === 'project' ? projectHash : 'global', mmCap);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Step 4: Check total token budget and escalate if needed
|
|
111
|
+
const contextText = contextSlices.map((s) => s.content).join('\n\n');
|
|
112
|
+
const mmText = mentalModel?.content ?? '';
|
|
113
|
+
const totalTokens = baseTokens + estimateTokens(contextText) + estimateTokens(mmText);
|
|
114
|
+
// Escalation loop: keep escalating until the content fits or we hit the ceiling
|
|
115
|
+
let effectiveTier = currentTier;
|
|
116
|
+
while (totalTokens > exports.TIER_CAPS[effectiveTier].systemPrompt) {
|
|
117
|
+
if (agent.onOverflow === 'fail') {
|
|
118
|
+
throw new Error(`Agent ${agent.name}: total tokens (${totalTokens}) exceeds ` +
|
|
119
|
+
`${effectiveTier} tier cap (${exports.TIER_CAPS[effectiveTier].systemPrompt}) ` +
|
|
120
|
+
`and onOverflow is 'fail'`);
|
|
121
|
+
}
|
|
122
|
+
const next = escalateTier(effectiveTier);
|
|
123
|
+
if (next === null) {
|
|
124
|
+
throw new Error(`Agent ${agent.name}: total tokens (${totalTokens}) exceeds ` +
|
|
125
|
+
`high tier cap (${exports.TIER_CAPS.high.systemPrompt}) — cannot escalate further`);
|
|
126
|
+
}
|
|
127
|
+
effectiveTier = next;
|
|
128
|
+
escalated = true;
|
|
129
|
+
}
|
|
130
|
+
currentTier = effectiveTier;
|
|
131
|
+
// Step 5: Compose system prompt
|
|
132
|
+
const parts = [basePrompt];
|
|
133
|
+
if (contextSlices.length > 0) {
|
|
134
|
+
parts.push('\n\n## Context (JIT-injected)\n');
|
|
135
|
+
for (const slice of contextSlices) {
|
|
136
|
+
parts.push(`### ${slice.source}\n${slice.content}\n`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (mentalModel && mmText.length > 0) {
|
|
140
|
+
parts.push('\n\n## Mental Model (validate before acting)\n' +
|
|
141
|
+
`${MENTAL_MODEL_VALIDATION_PREFIX}\n\n` +
|
|
142
|
+
mmText);
|
|
143
|
+
}
|
|
144
|
+
const systemPrompt = parts.join('');
|
|
145
|
+
const model = TIER_MODELS[currentTier];
|
|
146
|
+
return {
|
|
147
|
+
agentName: agent.name,
|
|
148
|
+
resolvedTier: currentTier,
|
|
149
|
+
escalated,
|
|
150
|
+
declaredTier: agent.tier,
|
|
151
|
+
systemPrompt,
|
|
152
|
+
systemPromptTokens: estimateTokens(systemPrompt),
|
|
153
|
+
model: model.primary,
|
|
154
|
+
fallbackModels: model.fallbacks,
|
|
155
|
+
skills: agent.skills,
|
|
156
|
+
tools: agent.tools,
|
|
157
|
+
injectedContextSources: contextSlices.map((s) => s.source),
|
|
158
|
+
mentalModelInjected: mentalModel !== null && mmText.length > 0,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 3-tier hierarchy enforcement module — Wave 7.
|
|
3
|
+
*
|
|
4
|
+
* Enforces the CLEO agent hierarchy at spawn time per ULTRAPLAN section 10.
|
|
5
|
+
* Three roles (orchestrator, lead, worker) have strictly defined routing
|
|
6
|
+
* rules and tool restrictions:
|
|
7
|
+
*
|
|
8
|
+
* - Orchestrators dispatch to leads only; cannot use Edit/Write/Bash.
|
|
9
|
+
* - Leads dispatch to own-group workers only; cannot use Edit/Write/Bash.
|
|
10
|
+
* - Workers cannot dispatch agents; may only query peers.
|
|
11
|
+
*
|
|
12
|
+
* @packageDocumentation
|
|
13
|
+
*/
|
|
14
|
+
/** Agent role within the 3-tier hierarchy. */
|
|
15
|
+
export type Role = 'orchestrator' | 'lead' | 'worker';
|
|
16
|
+
/**
|
|
17
|
+
* Routing rules for a team definition.
|
|
18
|
+
*
|
|
19
|
+
* Controls which agents each role tier may communicate with. The
|
|
20
|
+
* `orchestratorCanCall` and `leadCanCall` fields accept either a keyword
|
|
21
|
+
* shorthand (`'leads'` / `'own_group_workers'`) or an explicit list of
|
|
22
|
+
* agent names for fine-grained control.
|
|
23
|
+
*/
|
|
24
|
+
export interface TeamRouting {
|
|
25
|
+
/** The human-in-the-loop target agent name. */
|
|
26
|
+
hitlTarget: string;
|
|
27
|
+
/** Who the orchestrator may dispatch to: all leads or a named subset. */
|
|
28
|
+
orchestratorCanCall: 'leads' | string[];
|
|
29
|
+
/** Who a lead may dispatch to: own group workers or a named subset. */
|
|
30
|
+
leadCanCall: 'own_group_workers' | string[];
|
|
31
|
+
/** Explicit list of agents a worker may dispatch (typically empty). */
|
|
32
|
+
workerCanCall: string[];
|
|
33
|
+
/** Who workers may query: all peers or a named subset. */
|
|
34
|
+
workerCanQuery: 'peers' | string[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Full team definition describing the hierarchy, group membership, and
|
|
38
|
+
* routing rules for an agent team.
|
|
39
|
+
*/
|
|
40
|
+
export interface TeamDefinition {
|
|
41
|
+
/** Human-readable team name. */
|
|
42
|
+
name: string;
|
|
43
|
+
/** Name of the orchestrator agent. */
|
|
44
|
+
orchestrator: string;
|
|
45
|
+
/** Map of group name to lead agent name. */
|
|
46
|
+
leads: Record<string, string>;
|
|
47
|
+
/** Map of group name to worker agent names. */
|
|
48
|
+
workers: Record<string, string[]>;
|
|
49
|
+
/** Routing rules governing inter-agent dispatch. */
|
|
50
|
+
routing: TeamRouting;
|
|
51
|
+
/** Enforcement mode: strict rejects violations, permissive logs warnings. */
|
|
52
|
+
enforcement: 'strict' | 'permissive';
|
|
53
|
+
}
|
|
54
|
+
/** Forbidden tools for lead-role agents (ULTRAPLAN section 10.4 LEAD-001). */
|
|
55
|
+
export declare const LEAD_FORBIDDEN_TOOLS: readonly ["Edit", "Write", "Bash"];
|
|
56
|
+
/** Forbidden tools for orchestrator-role agents (ULTRAPLAN section 10.5 ORCH-001). */
|
|
57
|
+
export declare const ORCHESTRATOR_FORBIDDEN_TOOLS: readonly ["Edit", "Write", "Bash"];
|
|
58
|
+
/**
|
|
59
|
+
* Result of a spawn validation check.
|
|
60
|
+
*/
|
|
61
|
+
export interface SpawnValidation {
|
|
62
|
+
/** Whether the spawn is allowed. */
|
|
63
|
+
allowed: boolean;
|
|
64
|
+
/** Human-readable reason for the decision. */
|
|
65
|
+
reason: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Validate whether a caller agent is allowed to spawn a target agent
|
|
69
|
+
* per the team's routing rules.
|
|
70
|
+
*
|
|
71
|
+
* Routing constraints (ULTRAPLAN section 10.2):
|
|
72
|
+
* - Orchestrator may only dispatch to leads.
|
|
73
|
+
* - Lead may only dispatch to workers within their own group.
|
|
74
|
+
* - Workers may never dispatch agents (peer queries only).
|
|
75
|
+
*
|
|
76
|
+
* @param callerName - Name of the agent requesting the spawn.
|
|
77
|
+
* @param callerRole - Role of the calling agent.
|
|
78
|
+
* @param targetName - Name of the agent to be spawned.
|
|
79
|
+
* @param targetRole - Role of the target agent.
|
|
80
|
+
* @param team - The team definition containing routing rules.
|
|
81
|
+
* @returns A {@link SpawnValidation} indicating whether the spawn is allowed.
|
|
82
|
+
*/
|
|
83
|
+
export declare function validateSpawnRequest(callerName: string, callerRole: Role, targetName: string, _targetRole: Role, team: TeamDefinition): SpawnValidation;
|
|
84
|
+
/**
|
|
85
|
+
* Filter a tool list based on role constraints.
|
|
86
|
+
*
|
|
87
|
+
* Leads and orchestrators have Edit, Write, and Bash stripped per
|
|
88
|
+
* ULTRAPLAN sections 10.4 (LEAD-001) and 10.5 (ORCH-001). Workers
|
|
89
|
+
* retain all tools.
|
|
90
|
+
*
|
|
91
|
+
* @param tools - The full list of tool names available to the agent.
|
|
92
|
+
* @param role - The agent's role in the hierarchy.
|
|
93
|
+
* @returns The filtered tool list with forbidden tools removed.
|
|
94
|
+
*/
|
|
95
|
+
export declare function filterToolsForRole(tools: string[], role: Role): string[];
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 3-tier hierarchy enforcement module — Wave 7.
|
|
4
|
+
*
|
|
5
|
+
* Enforces the CLEO agent hierarchy at spawn time per ULTRAPLAN section 10.
|
|
6
|
+
* Three roles (orchestrator, lead, worker) have strictly defined routing
|
|
7
|
+
* rules and tool restrictions:
|
|
8
|
+
*
|
|
9
|
+
* - Orchestrators dispatch to leads only; cannot use Edit/Write/Bash.
|
|
10
|
+
* - Leads dispatch to own-group workers only; cannot use Edit/Write/Bash.
|
|
11
|
+
* - Workers cannot dispatch agents; may only query peers.
|
|
12
|
+
*
|
|
13
|
+
* @packageDocumentation
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.ORCHESTRATOR_FORBIDDEN_TOOLS = exports.LEAD_FORBIDDEN_TOOLS = void 0;
|
|
17
|
+
exports.validateSpawnRequest = validateSpawnRequest;
|
|
18
|
+
exports.filterToolsForRole = filterToolsForRole;
|
|
19
|
+
/** Forbidden tools for lead-role agents (ULTRAPLAN section 10.4 LEAD-001). */
|
|
20
|
+
exports.LEAD_FORBIDDEN_TOOLS = ['Edit', 'Write', 'Bash'];
|
|
21
|
+
/** Forbidden tools for orchestrator-role agents (ULTRAPLAN section 10.5 ORCH-001). */
|
|
22
|
+
exports.ORCHESTRATOR_FORBIDDEN_TOOLS = ['Edit', 'Write', 'Bash'];
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Spawn validation
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
/**
|
|
27
|
+
* Validate whether a caller agent is allowed to spawn a target agent
|
|
28
|
+
* per the team's routing rules.
|
|
29
|
+
*
|
|
30
|
+
* Routing constraints (ULTRAPLAN section 10.2):
|
|
31
|
+
* - Orchestrator may only dispatch to leads.
|
|
32
|
+
* - Lead may only dispatch to workers within their own group.
|
|
33
|
+
* - Workers may never dispatch agents (peer queries only).
|
|
34
|
+
*
|
|
35
|
+
* @param callerName - Name of the agent requesting the spawn.
|
|
36
|
+
* @param callerRole - Role of the calling agent.
|
|
37
|
+
* @param targetName - Name of the agent to be spawned.
|
|
38
|
+
* @param targetRole - Role of the target agent.
|
|
39
|
+
* @param team - The team definition containing routing rules.
|
|
40
|
+
* @returns A {@link SpawnValidation} indicating whether the spawn is allowed.
|
|
41
|
+
*/
|
|
42
|
+
function validateSpawnRequest(callerName, callerRole, targetName, _targetRole, team) {
|
|
43
|
+
// Orchestrator can call leads
|
|
44
|
+
if (callerRole === 'orchestrator') {
|
|
45
|
+
const isLead = Object.values(team.leads).includes(targetName);
|
|
46
|
+
if (!isLead) {
|
|
47
|
+
return {
|
|
48
|
+
allowed: false,
|
|
49
|
+
reason: `Orchestrator ${callerName} can only dispatch to leads. ${targetName} is not a lead.`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return { allowed: true, reason: 'Orchestrator dispatching to lead' };
|
|
53
|
+
}
|
|
54
|
+
// Lead can call own-group workers
|
|
55
|
+
if (callerRole === 'lead') {
|
|
56
|
+
const callerGroup = Object.entries(team.leads).find(([, name]) => name === callerName)?.[0];
|
|
57
|
+
if (!callerGroup) {
|
|
58
|
+
return {
|
|
59
|
+
allowed: false,
|
|
60
|
+
reason: `Lead ${callerName} not found in any team group`,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const groupWorkers = team.workers[callerGroup] ?? [];
|
|
64
|
+
if (!groupWorkers.includes(targetName)) {
|
|
65
|
+
return {
|
|
66
|
+
allowed: false,
|
|
67
|
+
reason: `Lead ${callerName} (group: ${callerGroup}) cannot dispatch to ${targetName} — not in own worker group`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
allowed: true,
|
|
72
|
+
reason: `Lead dispatching to own-group worker (${callerGroup})`,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// Workers cannot dispatch
|
|
76
|
+
if (callerRole === 'worker') {
|
|
77
|
+
return {
|
|
78
|
+
allowed: false,
|
|
79
|
+
reason: `Worker ${callerName} cannot dispatch agents. Workers may only query peers.`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return { allowed: false, reason: 'Unknown caller role' };
|
|
83
|
+
}
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Tool filtering
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
/**
|
|
88
|
+
* Filter a tool list based on role constraints.
|
|
89
|
+
*
|
|
90
|
+
* Leads and orchestrators have Edit, Write, and Bash stripped per
|
|
91
|
+
* ULTRAPLAN sections 10.4 (LEAD-001) and 10.5 (ORCH-001). Workers
|
|
92
|
+
* retain all tools.
|
|
93
|
+
*
|
|
94
|
+
* @param tools - The full list of tool names available to the agent.
|
|
95
|
+
* @param role - The agent's role in the hierarchy.
|
|
96
|
+
* @returns The filtered tool list with forbidden tools removed.
|
|
97
|
+
*/
|
|
98
|
+
function filterToolsForRole(tools, role) {
|
|
99
|
+
if (role === 'worker')
|
|
100
|
+
return tools;
|
|
101
|
+
const forbidden = role === 'lead' ? exports.LEAD_FORBIDDEN_TOOLS : exports.ORCHESTRATOR_FORBIDDEN_TOOLS;
|
|
102
|
+
return tools.filter((t) => !forbidden.includes(t));
|
|
103
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
export type { LAFSEnvelope, LAFSError, LAFSMeta, MVILevel } from '@cleocode/lafs';
|
|
2
|
+
export type { AgentEntry, BundleDiagnostic, CompiledBundle, ParsedCantDocument, TeamEntry, ToolEntry, } from './bundle';
|
|
3
|
+
export { compileBundle } from './bundle';
|
|
4
|
+
export type { AgentDefinition, ContextProvider, ContextSlice, MentalModelSlice, SpawnPayload, Tier, } from './composer.js';
|
|
5
|
+
export { composeSpawnPayload, escalateTier, estimateTokens, TIER_CAPS } from './composer.js';
|
|
2
6
|
export type { CantDocumentResult, CantListResult, CantPipelineResult, CantValidationResult, SectionKind, } from './document';
|
|
3
7
|
export { executePipeline, listSections, parseDocument, validateDocument, } from './document';
|
|
8
|
+
export type { Role, SpawnValidation, TeamDefinition, TeamRouting } from './hierarchy.js';
|
|
9
|
+
export { filterToolsForRole, LEAD_FORBIDDEN_TOOLS, ORCHESTRATOR_FORBIDDEN_TOOLS, validateSpawnRequest, } from './hierarchy.js';
|
|
10
|
+
export type { ConsolidateOptions, MentalModel, MentalModelObservation, MentalModelScope, MentalModelStore, ObservationTrigger, SessionOutput, } from './mental-model.js';
|
|
11
|
+
export { consolidate, createEmptyModel, harvestObservations, renderMentalModel, } from './mental-model.js';
|
|
4
12
|
export type { ConvertedFile, MigrationOptions, MigrationResult, UnconvertedSection, } from './migrate/index';
|
|
5
13
|
export { migrateMarkdown, serializeCantDocument, showDiff, showSummary } from './migrate/index';
|
|
6
14
|
export type { NativeDiagnostic, NativeParseDocumentResult, NativeParseError, NativeParseResult, NativePipelineResult, NativePipelineStep, NativeValidateResult, } from './native-loader';
|
|
@@ -8,3 +16,5 @@ export { cantClassifyDirectiveNative, cantExecutePipelineNative, cantExtractAgen
|
|
|
8
16
|
export type { ParsedCANTMessage } from './parse';
|
|
9
17
|
export { initCantParser, parseCANTMessage } from './parse';
|
|
10
18
|
export type { DirectiveType } from './types';
|
|
19
|
+
export type { MergeResult, WorktreeConfig, WorktreeEntry, WorktreeHandle, WorktreeRequest, } from './worktree.js';
|
|
20
|
+
export { createWorktree, listWorktrees, mergeWorktree, resolveWorktreeRoot, } from './worktree.js';
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,33 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
// JIT Agent Composer (ULTRAPLAN Wave 5)
|
|
2
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseCANTMessage = exports.initCantParser = exports.isWasmAvailable = exports.isNativeAvailable = exports.initWasm = exports.cantValidateDocumentNative = exports.cantParseNative = exports.cantParseDocumentNative = exports.cantExtractAgentProfilesNative = exports.cantExecutePipelineNative = exports.cantClassifyDirectiveNative = exports.showSummary = exports.showDiff = exports.serializeCantDocument = exports.migrateMarkdown = exports.validateDocument = exports.parseDocument = exports.listSections = exports.executePipeline = void 0;
|
|
4
|
+
exports.resolveWorktreeRoot = exports.mergeWorktree = exports.listWorktrees = exports.createWorktree = exports.parseCANTMessage = exports.initCantParser = exports.isWasmAvailable = exports.isNativeAvailable = exports.initWasm = exports.cantValidateDocumentNative = exports.cantParseNative = exports.cantParseDocumentNative = exports.cantExtractAgentProfilesNative = exports.cantExecutePipelineNative = exports.cantClassifyDirectiveNative = exports.showSummary = exports.showDiff = exports.serializeCantDocument = exports.migrateMarkdown = exports.renderMentalModel = exports.harvestObservations = exports.createEmptyModel = exports.consolidate = exports.validateSpawnRequest = exports.ORCHESTRATOR_FORBIDDEN_TOOLS = exports.LEAD_FORBIDDEN_TOOLS = exports.filterToolsForRole = exports.validateDocument = exports.parseDocument = exports.listSections = exports.executePipeline = exports.TIER_CAPS = exports.estimateTokens = exports.escalateTier = exports.composeSpawnPayload = exports.compileBundle = void 0;
|
|
5
|
+
// Bundle compiler
|
|
6
|
+
var bundle_1 = require("./bundle");
|
|
7
|
+
Object.defineProperty(exports, "compileBundle", { enumerable: true, get: function () { return bundle_1.compileBundle; } });
|
|
8
|
+
var composer_js_1 = require("./composer.js");
|
|
9
|
+
Object.defineProperty(exports, "composeSpawnPayload", { enumerable: true, get: function () { return composer_js_1.composeSpawnPayload; } });
|
|
10
|
+
Object.defineProperty(exports, "escalateTier", { enumerable: true, get: function () { return composer_js_1.escalateTier; } });
|
|
11
|
+
Object.defineProperty(exports, "estimateTokens", { enumerable: true, get: function () { return composer_js_1.estimateTokens; } });
|
|
12
|
+
Object.defineProperty(exports, "TIER_CAPS", { enumerable: true, get: function () { return composer_js_1.TIER_CAPS; } });
|
|
4
13
|
// High-level API (replaces standalone cant-cli binary)
|
|
5
14
|
var document_1 = require("./document");
|
|
6
15
|
Object.defineProperty(exports, "executePipeline", { enumerable: true, get: function () { return document_1.executePipeline; } });
|
|
7
16
|
Object.defineProperty(exports, "listSections", { enumerable: true, get: function () { return document_1.listSections; } });
|
|
8
17
|
Object.defineProperty(exports, "parseDocument", { enumerable: true, get: function () { return document_1.parseDocument; } });
|
|
9
18
|
Object.defineProperty(exports, "validateDocument", { enumerable: true, get: function () { return document_1.validateDocument; } });
|
|
19
|
+
// 3-tier hierarchy enforcement (ULTRAPLAN Wave 7)
|
|
20
|
+
var hierarchy_js_1 = require("./hierarchy.js");
|
|
21
|
+
Object.defineProperty(exports, "filterToolsForRole", { enumerable: true, get: function () { return hierarchy_js_1.filterToolsForRole; } });
|
|
22
|
+
Object.defineProperty(exports, "LEAD_FORBIDDEN_TOOLS", { enumerable: true, get: function () { return hierarchy_js_1.LEAD_FORBIDDEN_TOOLS; } });
|
|
23
|
+
Object.defineProperty(exports, "ORCHESTRATOR_FORBIDDEN_TOOLS", { enumerable: true, get: function () { return hierarchy_js_1.ORCHESTRATOR_FORBIDDEN_TOOLS; } });
|
|
24
|
+
Object.defineProperty(exports, "validateSpawnRequest", { enumerable: true, get: function () { return hierarchy_js_1.validateSpawnRequest; } });
|
|
25
|
+
// Mental Model Manager (ULTRAPLAN Wave 8)
|
|
26
|
+
var mental_model_js_1 = require("./mental-model.js");
|
|
27
|
+
Object.defineProperty(exports, "consolidate", { enumerable: true, get: function () { return mental_model_js_1.consolidate; } });
|
|
28
|
+
Object.defineProperty(exports, "createEmptyModel", { enumerable: true, get: function () { return mental_model_js_1.createEmptyModel; } });
|
|
29
|
+
Object.defineProperty(exports, "harvestObservations", { enumerable: true, get: function () { return mental_model_js_1.harvestObservations; } });
|
|
30
|
+
Object.defineProperty(exports, "renderMentalModel", { enumerable: true, get: function () { return mental_model_js_1.renderMentalModel; } });
|
|
10
31
|
// Migration engine
|
|
11
32
|
var index_1 = require("./migrate/index");
|
|
12
33
|
Object.defineProperty(exports, "migrateMarkdown", { enumerable: true, get: function () { return index_1.migrateMarkdown; } });
|
|
@@ -28,3 +49,9 @@ Object.defineProperty(exports, "isWasmAvailable", { enumerable: true, get: funct
|
|
|
28
49
|
var parse_1 = require("./parse");
|
|
29
50
|
Object.defineProperty(exports, "initCantParser", { enumerable: true, get: function () { return parse_1.initCantParser; } });
|
|
30
51
|
Object.defineProperty(exports, "parseCANTMessage", { enumerable: true, get: function () { return parse_1.parseCANTMessage; } });
|
|
52
|
+
// Worktree isolation (ULTRAPLAN Wave 9)
|
|
53
|
+
var worktree_js_1 = require("./worktree.js");
|
|
54
|
+
Object.defineProperty(exports, "createWorktree", { enumerable: true, get: function () { return worktree_js_1.createWorktree; } });
|
|
55
|
+
Object.defineProperty(exports, "listWorktrees", { enumerable: true, get: function () { return worktree_js_1.listWorktrees; } });
|
|
56
|
+
Object.defineProperty(exports, "mergeWorktree", { enumerable: true, get: function () { return worktree_js_1.mergeWorktree; } });
|
|
57
|
+
Object.defineProperty(exports, "resolveWorktreeRoot", { enumerable: true, get: function () { return worktree_js_1.resolveWorktreeRoot; } });
|