@cleocode/cant 2026.4.92 → 2026.4.94

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.
@@ -55,6 +55,48 @@ export interface TeamDefinition {
55
55
  export declare const LEAD_FORBIDDEN_TOOLS: readonly ["Edit", "Write", "Bash"];
56
56
  /** Forbidden tools for orchestrator-role agents (ULTRAPLAN section 10.5 ORCH-001). */
57
57
  export declare const ORCHESTRATOR_FORBIDDEN_TOOLS: readonly ["Edit", "Write", "Bash"];
58
+ /**
59
+ * Forbidden tools for worker-role agents per the thin-agent inversion-of-control
60
+ * rule (ORC-012, T931). Workers MUST NOT spawn subagents; these tool names are
61
+ * the canonical Claude Code spawn surfaces and are stripped at parse time.
62
+ *
63
+ * @task T931 Thin-agent runtime enforcer
64
+ * @task T907 Thin-agent enforcement
65
+ */
66
+ export declare const WORKER_FORBIDDEN_SPAWN_TOOLS: readonly ["Agent", "Task"];
67
+ /**
68
+ * Diagnostic code emitted when {@link stripSpawnToolsForWorker} removed one or
69
+ * more tools from a worker-role tool list. Callers that want to surface the
70
+ * strip to audit logs should inspect the `removed` array on the warning.
71
+ */
72
+ export declare const THIN_AGENT_TOOLS_STRIPPED: "THIN_AGENT_TOOLS_STRIPPED";
73
+ /**
74
+ * Structured warning returned by {@link stripSpawnToolsForWorker} when any
75
+ * spawn-capable tool was removed from a worker-role tool list. Surfaced by the
76
+ * parser so downstream layers (bundle compiler, composer, registry install)
77
+ * can attach the warning to their own diagnostic channels.
78
+ *
79
+ * @task T931 Thin-agent runtime enforcer
80
+ */
81
+ export interface ThinAgentToolsStrippedWarning {
82
+ /** Stable diagnostic code — `'THIN_AGENT_TOOLS_STRIPPED'`. */
83
+ readonly code: typeof THIN_AGENT_TOOLS_STRIPPED;
84
+ /** Human-readable message explaining the strip. */
85
+ readonly message: string;
86
+ /** The exact tool names that were removed. */
87
+ readonly removed: readonly string[];
88
+ }
89
+ /**
90
+ * Result returned by {@link stripSpawnToolsForWorker}. Always carries the
91
+ * (possibly unchanged) `tools` list; `warning` is only populated when at least
92
+ * one tool was stripped.
93
+ */
94
+ export interface StripSpawnToolsResult {
95
+ /** Final tool list after thin-agent enforcement. */
96
+ readonly tools: readonly string[];
97
+ /** Structured warning iff any tool was removed. */
98
+ readonly warning: ThinAgentToolsStrippedWarning | null;
99
+ }
58
100
  /**
59
101
  * Result of a spawn validation check.
60
102
  */
@@ -85,11 +127,39 @@ export declare function validateSpawnRequest(callerName: string, callerRole: Rol
85
127
  * Filter a tool list based on role constraints.
86
128
  *
87
129
  * 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.
130
+ * ULTRAPLAN sections 10.4 (LEAD-001) and 10.5 (ORCH-001). Workers have
131
+ * `Agent` and `Task` stripped per the thin-agent inversion-of-control rule
132
+ * (ORC-012, T931) — workers MUST NOT spawn subagents.
90
133
  *
91
134
  * @param tools - The full list of tool names available to the agent.
92
135
  * @param role - The agent's role in the hierarchy.
93
136
  * @returns The filtered tool list with forbidden tools removed.
94
137
  */
95
138
  export declare function filterToolsForRole(tools: string[], role: Role): string[];
139
+ /**
140
+ * Strip spawn-capable tools (`Agent`, `Task`) from a worker-role tool list at
141
+ * parse time. This is the parse-time half of the T931 thin-agent runtime
142
+ * enforcer — workers MUST NOT carry the Claude Code `Agent`/`Task` surfaces
143
+ * because doing so would let them spawn subagents and break the worker
144
+ * inversion-of-control contract (ORC-012).
145
+ *
146
+ * The runtime half (`enforceThinAgent` in `@cleocode/core/orchestration`)
147
+ * performs the same check immediately before dispatch as a defense-in-depth
148
+ * guard against misconfigured `.cant` sources or hand-written payloads that
149
+ * bypass the parser.
150
+ *
151
+ * @param tools - The flat tool allowlist declared on a worker agent. Missing
152
+ * or non-array inputs are coerced to an empty result for safety.
153
+ * @returns A {@link StripSpawnToolsResult} that always carries `tools` and
154
+ * carries a `warning` iff any tool was removed.
155
+ *
156
+ * @example
157
+ * ```typescript
158
+ * const result = stripSpawnToolsForWorker(['Agent', 'Read', 'Edit']);
159
+ * // result.tools → ['Read', 'Edit']
160
+ * // result.warning → { code: 'THIN_AGENT_TOOLS_STRIPPED', removed: ['Agent'] }
161
+ * ```
162
+ *
163
+ * @task T931 Thin-agent runtime enforcer
164
+ */
165
+ export declare function stripSpawnToolsForWorker(tools: readonly string[]): StripSpawnToolsResult;
package/dist/hierarchy.js CHANGED
@@ -13,13 +13,29 @@
13
13
  * @packageDocumentation
14
14
  */
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.ORCHESTRATOR_FORBIDDEN_TOOLS = exports.LEAD_FORBIDDEN_TOOLS = void 0;
16
+ exports.THIN_AGENT_TOOLS_STRIPPED = exports.WORKER_FORBIDDEN_SPAWN_TOOLS = exports.ORCHESTRATOR_FORBIDDEN_TOOLS = exports.LEAD_FORBIDDEN_TOOLS = void 0;
17
17
  exports.validateSpawnRequest = validateSpawnRequest;
18
18
  exports.filterToolsForRole = filterToolsForRole;
19
+ exports.stripSpawnToolsForWorker = stripSpawnToolsForWorker;
19
20
  /** Forbidden tools for lead-role agents (ULTRAPLAN section 10.4 LEAD-001). */
20
21
  exports.LEAD_FORBIDDEN_TOOLS = ['Edit', 'Write', 'Bash'];
21
22
  /** Forbidden tools for orchestrator-role agents (ULTRAPLAN section 10.5 ORCH-001). */
22
23
  exports.ORCHESTRATOR_FORBIDDEN_TOOLS = ['Edit', 'Write', 'Bash'];
24
+ /**
25
+ * Forbidden tools for worker-role agents per the thin-agent inversion-of-control
26
+ * rule (ORC-012, T931). Workers MUST NOT spawn subagents; these tool names are
27
+ * the canonical Claude Code spawn surfaces and are stripped at parse time.
28
+ *
29
+ * @task T931 Thin-agent runtime enforcer
30
+ * @task T907 Thin-agent enforcement
31
+ */
32
+ exports.WORKER_FORBIDDEN_SPAWN_TOOLS = ['Agent', 'Task'];
33
+ /**
34
+ * Diagnostic code emitted when {@link stripSpawnToolsForWorker} removed one or
35
+ * more tools from a worker-role tool list. Callers that want to surface the
36
+ * strip to audit logs should inspect the `removed` array on the warning.
37
+ */
38
+ exports.THIN_AGENT_TOOLS_STRIPPED = 'THIN_AGENT_TOOLS_STRIPPED';
23
39
  // ---------------------------------------------------------------------------
24
40
  // Spawn validation
25
41
  // ---------------------------------------------------------------------------
@@ -88,16 +104,71 @@ function validateSpawnRequest(callerName, callerRole, targetName, _targetRole, t
88
104
  * Filter a tool list based on role constraints.
89
105
  *
90
106
  * 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.
107
+ * ULTRAPLAN sections 10.4 (LEAD-001) and 10.5 (ORCH-001). Workers have
108
+ * `Agent` and `Task` stripped per the thin-agent inversion-of-control rule
109
+ * (ORC-012, T931) — workers MUST NOT spawn subagents.
93
110
  *
94
111
  * @param tools - The full list of tool names available to the agent.
95
112
  * @param role - The agent's role in the hierarchy.
96
113
  * @returns The filtered tool list with forbidden tools removed.
97
114
  */
98
115
  function filterToolsForRole(tools, role) {
99
- if (role === 'worker')
100
- return tools;
116
+ if (role === 'worker') {
117
+ return stripSpawnToolsForWorker(tools).tools.slice();
118
+ }
101
119
  const forbidden = role === 'lead' ? exports.LEAD_FORBIDDEN_TOOLS : exports.ORCHESTRATOR_FORBIDDEN_TOOLS;
102
120
  return tools.filter((t) => !forbidden.includes(t));
103
121
  }
122
+ /**
123
+ * Strip spawn-capable tools (`Agent`, `Task`) from a worker-role tool list at
124
+ * parse time. This is the parse-time half of the T931 thin-agent runtime
125
+ * enforcer — workers MUST NOT carry the Claude Code `Agent`/`Task` surfaces
126
+ * because doing so would let them spawn subagents and break the worker
127
+ * inversion-of-control contract (ORC-012).
128
+ *
129
+ * The runtime half (`enforceThinAgent` in `@cleocode/core/orchestration`)
130
+ * performs the same check immediately before dispatch as a defense-in-depth
131
+ * guard against misconfigured `.cant` sources or hand-written payloads that
132
+ * bypass the parser.
133
+ *
134
+ * @param tools - The flat tool allowlist declared on a worker agent. Missing
135
+ * or non-array inputs are coerced to an empty result for safety.
136
+ * @returns A {@link StripSpawnToolsResult} that always carries `tools` and
137
+ * carries a `warning` iff any tool was removed.
138
+ *
139
+ * @example
140
+ * ```typescript
141
+ * const result = stripSpawnToolsForWorker(['Agent', 'Read', 'Edit']);
142
+ * // result.tools → ['Read', 'Edit']
143
+ * // result.warning → { code: 'THIN_AGENT_TOOLS_STRIPPED', removed: ['Agent'] }
144
+ * ```
145
+ *
146
+ * @task T931 Thin-agent runtime enforcer
147
+ */
148
+ function stripSpawnToolsForWorker(tools) {
149
+ if (!Array.isArray(tools)) {
150
+ return { tools: [], warning: null };
151
+ }
152
+ const blocked = exports.WORKER_FORBIDDEN_SPAWN_TOOLS;
153
+ const removed = [];
154
+ const kept = [];
155
+ for (const tool of tools) {
156
+ if (blocked.includes(tool)) {
157
+ removed.push(tool);
158
+ }
159
+ else {
160
+ kept.push(tool);
161
+ }
162
+ }
163
+ if (removed.length === 0) {
164
+ return { tools: kept, warning: null };
165
+ }
166
+ return {
167
+ tools: kept,
168
+ warning: {
169
+ code: exports.THIN_AGENT_TOOLS_STRIPPED,
170
+ message: `Removed ${removed.length} spawn-capable tool(s) from worker role: ${removed.join(', ')}. Workers cannot spawn subagents (ORC-012).`,
171
+ removed,
172
+ },
173
+ };
174
+ }
package/dist/index.d.ts CHANGED
@@ -6,8 +6,8 @@ export { composeSpawnPayload, escalateTier, estimateTokens, TIER_CAPS } from './
6
6
  export { brainContextProvider } from './context-provider-brain.js';
7
7
  export type { CantDocumentResult, CantListResult, CantPipelineResult, CantValidationResult, SectionKind, } from './document';
8
8
  export { executePipeline, listSections, parseDocument, validateDocument, } from './document';
9
- export type { Role, SpawnValidation, TeamDefinition, TeamRouting } from './hierarchy.js';
10
- export { filterToolsForRole, LEAD_FORBIDDEN_TOOLS, ORCHESTRATOR_FORBIDDEN_TOOLS, validateSpawnRequest, } from './hierarchy.js';
9
+ export type { Role, SpawnValidation, StripSpawnToolsResult, TeamDefinition, TeamRouting, ThinAgentToolsStrippedWarning, } from './hierarchy.js';
10
+ export { filterToolsForRole, LEAD_FORBIDDEN_TOOLS, ORCHESTRATOR_FORBIDDEN_TOOLS, stripSpawnToolsForWorker, THIN_AGENT_TOOLS_STRIPPED, validateSpawnRequest, WORKER_FORBIDDEN_SPAWN_TOOLS, } from './hierarchy.js';
11
11
  export type { ConsolidateOptions, MentalModel, MentalModelObservation, MentalModelScope, MentalModelStore, ObservationTrigger, SessionOutput, } from './mental-model.js';
12
12
  export { consolidate, createEmptyModel, harvestObservations, renderMentalModel, } from './mental-model.js';
13
13
  export type { ConvertedFile, MigrationOptions, MigrationResult, UnconvertedSection, } from './migrate/index';
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // JIT Agent Composer (ULTRAPLAN Wave 5)
3
3
  // Wave 7a: BRAIN-backed ContextProvider (T432)
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.resolveWorktreeRoot = exports.mergeWorktree = exports.listWorktrees = exports.createWorktree = exports.isCantAgentV3 = 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.brainContextProvider = exports.TIER_CAPS = exports.estimateTokens = exports.escalateTier = exports.composeSpawnPayload = exports.toCantAgentV3 = exports.compileBundle = void 0;
5
+ exports.resolveWorktreeRoot = exports.mergeWorktree = exports.listWorktrees = exports.createWorktree = exports.isCantAgentV3 = 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.WORKER_FORBIDDEN_SPAWN_TOOLS = exports.validateSpawnRequest = exports.THIN_AGENT_TOOLS_STRIPPED = exports.stripSpawnToolsForWorker = exports.ORCHESTRATOR_FORBIDDEN_TOOLS = exports.LEAD_FORBIDDEN_TOOLS = exports.filterToolsForRole = exports.validateDocument = exports.parseDocument = exports.listSections = exports.executePipeline = exports.brainContextProvider = exports.TIER_CAPS = exports.estimateTokens = exports.escalateTier = exports.composeSpawnPayload = exports.toCantAgentV3 = exports.compileBundle = void 0;
6
6
  // Bundle compiler
7
7
  var bundle_1 = require("./bundle");
8
8
  Object.defineProperty(exports, "compileBundle", { enumerable: true, get: function () { return bundle_1.compileBundle; } });
@@ -20,12 +20,15 @@ Object.defineProperty(exports, "executePipeline", { enumerable: true, get: funct
20
20
  Object.defineProperty(exports, "listSections", { enumerable: true, get: function () { return document_1.listSections; } });
21
21
  Object.defineProperty(exports, "parseDocument", { enumerable: true, get: function () { return document_1.parseDocument; } });
22
22
  Object.defineProperty(exports, "validateDocument", { enumerable: true, get: function () { return document_1.validateDocument; } });
23
- // 3-tier hierarchy enforcement (ULTRAPLAN Wave 7)
23
+ // 3-tier hierarchy enforcement (ULTRAPLAN Wave 7) + T931 thin-agent strip.
24
24
  var hierarchy_js_1 = require("./hierarchy.js");
25
25
  Object.defineProperty(exports, "filterToolsForRole", { enumerable: true, get: function () { return hierarchy_js_1.filterToolsForRole; } });
26
26
  Object.defineProperty(exports, "LEAD_FORBIDDEN_TOOLS", { enumerable: true, get: function () { return hierarchy_js_1.LEAD_FORBIDDEN_TOOLS; } });
27
27
  Object.defineProperty(exports, "ORCHESTRATOR_FORBIDDEN_TOOLS", { enumerable: true, get: function () { return hierarchy_js_1.ORCHESTRATOR_FORBIDDEN_TOOLS; } });
28
+ Object.defineProperty(exports, "stripSpawnToolsForWorker", { enumerable: true, get: function () { return hierarchy_js_1.stripSpawnToolsForWorker; } });
29
+ Object.defineProperty(exports, "THIN_AGENT_TOOLS_STRIPPED", { enumerable: true, get: function () { return hierarchy_js_1.THIN_AGENT_TOOLS_STRIPPED; } });
28
30
  Object.defineProperty(exports, "validateSpawnRequest", { enumerable: true, get: function () { return hierarchy_js_1.validateSpawnRequest; } });
31
+ Object.defineProperty(exports, "WORKER_FORBIDDEN_SPAWN_TOOLS", { enumerable: true, get: function () { return hierarchy_js_1.WORKER_FORBIDDEN_SPAWN_TOOLS; } });
29
32
  // Mental Model Manager (ULTRAPLAN Wave 8)
30
33
  var mental_model_js_1 = require("./mental-model.js");
31
34
  Object.defineProperty(exports, "consolidate", { enumerable: true, get: function () { return mental_model_js_1.consolidate; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cleocode/cant",
3
- "version": "2026.4.92",
3
+ "version": "2026.4.94",
4
4
  "description": "CANT protocol parser and runtime for CLEO — wraps cant-core via napi-rs",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -9,9 +9,9 @@
9
9
  "napi/"
10
10
  ],
11
11
  "dependencies": {
12
- "@cleocode/contracts": "2026.4.92",
13
- "@cleocode/core": "2026.4.92",
14
- "@cleocode/lafs": "2026.4.92"
12
+ "@cleocode/contracts": "2026.4.94",
13
+ "@cleocode/lafs": "2026.4.94",
14
+ "@cleocode/core": "2026.4.94"
15
15
  },
16
16
  "devDependencies": {
17
17
  "typescript": "^6.0.2",