chainlesschain 0.45.63 → 0.45.65

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.
@@ -0,0 +1,354 @@
1
+ "use strict";
2
+
3
+ const RISK_LEVELS = Object.freeze({
4
+ LOW: "low",
5
+ MEDIUM: "medium",
6
+ HIGH: "high",
7
+ });
8
+
9
+ const TOOL_CATEGORIES = Object.freeze({
10
+ READ: "read",
11
+ SEARCH: "search",
12
+ ANALYZE: "analyze",
13
+ WRITE: "write",
14
+ EXECUTE: "execute",
15
+ DELETE: "delete",
16
+ SKILL: "skill",
17
+ AGENT: "agent",
18
+ });
19
+
20
+ const TOOL_DECISIONS = Object.freeze({
21
+ ALLOW: "allow",
22
+ REQUIRE_PLAN: "require_plan",
23
+ REQUIRE_CONFIRMATION: "require_confirmation",
24
+ });
25
+
26
+ const PLAN_APPROVED_STATES = Object.freeze(["approved", "executing", "completed"]);
27
+
28
+ const READ_ONLY_GIT_SUBCOMMANDS = Object.freeze([
29
+ "status",
30
+ "diff",
31
+ "log",
32
+ "show",
33
+ "rev-parse",
34
+ ]);
35
+
36
+ const TOOL_POLICY_METADATA = Object.freeze({
37
+ read_file: {
38
+ riskLevel: RISK_LEVELS.LOW,
39
+ category: TOOL_CATEGORIES.READ,
40
+ availableInPlanMode: true,
41
+ planModeBehavior: "allow",
42
+ requiresPlanApproval: false,
43
+ requiresConfirmation: false,
44
+ approvalFlow: "auto",
45
+ isReadOnly: true,
46
+ },
47
+ list_dir: {
48
+ riskLevel: RISK_LEVELS.LOW,
49
+ category: TOOL_CATEGORIES.READ,
50
+ availableInPlanMode: true,
51
+ planModeBehavior: "allow",
52
+ requiresPlanApproval: false,
53
+ requiresConfirmation: false,
54
+ approvalFlow: "auto",
55
+ isReadOnly: true,
56
+ },
57
+ search_files: {
58
+ riskLevel: RISK_LEVELS.LOW,
59
+ category: TOOL_CATEGORIES.SEARCH,
60
+ availableInPlanMode: true,
61
+ planModeBehavior: "allow",
62
+ requiresPlanApproval: false,
63
+ requiresConfirmation: false,
64
+ approvalFlow: "auto",
65
+ isReadOnly: true,
66
+ },
67
+ edit_file: {
68
+ riskLevel: RISK_LEVELS.MEDIUM,
69
+ category: TOOL_CATEGORIES.WRITE,
70
+ availableInPlanMode: false,
71
+ planModeBehavior: "blocked",
72
+ requiresPlanApproval: true,
73
+ requiresConfirmation: false,
74
+ approvalFlow: "plan",
75
+ isReadOnly: false,
76
+ },
77
+ write_file: {
78
+ riskLevel: RISK_LEVELS.MEDIUM,
79
+ category: TOOL_CATEGORIES.WRITE,
80
+ availableInPlanMode: false,
81
+ planModeBehavior: "blocked",
82
+ requiresPlanApproval: true,
83
+ requiresConfirmation: false,
84
+ approvalFlow: "plan",
85
+ isReadOnly: false,
86
+ },
87
+ run_shell: {
88
+ riskLevel: RISK_LEVELS.HIGH,
89
+ category: TOOL_CATEGORIES.EXECUTE,
90
+ availableInPlanMode: false,
91
+ planModeBehavior: "blocked",
92
+ requiresPlanApproval: true,
93
+ requiresConfirmation: true,
94
+ approvalFlow: "policy",
95
+ isReadOnly: false,
96
+ },
97
+ git: {
98
+ riskLevel: RISK_LEVELS.HIGH,
99
+ category: TOOL_CATEGORIES.EXECUTE,
100
+ availableInPlanMode: false,
101
+ planModeBehavior: "readonly-conditional",
102
+ requiresPlanApproval: true,
103
+ requiresConfirmation: true,
104
+ approvalFlow: "policy",
105
+ isReadOnly: false,
106
+ readOnlySubcommands: READ_ONLY_GIT_SUBCOMMANDS,
107
+ },
108
+ list_skills: {
109
+ riskLevel: RISK_LEVELS.LOW,
110
+ category: TOOL_CATEGORIES.SKILL,
111
+ availableInPlanMode: true,
112
+ planModeBehavior: "allow",
113
+ requiresPlanApproval: false,
114
+ requiresConfirmation: false,
115
+ approvalFlow: "auto",
116
+ isReadOnly: true,
117
+ },
118
+ run_skill: {
119
+ riskLevel: RISK_LEVELS.MEDIUM,
120
+ category: TOOL_CATEGORIES.SKILL,
121
+ availableInPlanMode: false,
122
+ planModeBehavior: "blocked",
123
+ requiresPlanApproval: true,
124
+ requiresConfirmation: false,
125
+ approvalFlow: "policy",
126
+ isReadOnly: false,
127
+ },
128
+ run_code: {
129
+ riskLevel: RISK_LEVELS.HIGH,
130
+ category: TOOL_CATEGORIES.EXECUTE,
131
+ availableInPlanMode: false,
132
+ planModeBehavior: "blocked",
133
+ requiresPlanApproval: true,
134
+ requiresConfirmation: true,
135
+ approvalFlow: "policy",
136
+ isReadOnly: false,
137
+ },
138
+ spawn_sub_agent: {
139
+ riskLevel: RISK_LEVELS.HIGH,
140
+ category: TOOL_CATEGORIES.AGENT,
141
+ availableInPlanMode: false,
142
+ planModeBehavior: "blocked",
143
+ requiresPlanApproval: true,
144
+ requiresConfirmation: true,
145
+ approvalFlow: "policy",
146
+ isReadOnly: false,
147
+ },
148
+ });
149
+
150
+ function normalizeGitCommand(command) {
151
+ const trimmed = String(command || "").trim();
152
+ if (!trimmed) return "";
153
+ return trimmed.replace(/^git\s+/i, "").trim();
154
+ }
155
+
156
+ function isReadOnlyGitCommand(command) {
157
+ const normalized = normalizeGitCommand(command);
158
+ if (!normalized) return false;
159
+ const [subcommand] = normalized.split(/\s+/);
160
+ return READ_ONLY_GIT_SUBCOMMANDS.includes((subcommand || "").toLowerCase());
161
+ }
162
+
163
+ function normalizeRiskLevel(value, fallback = RISK_LEVELS.MEDIUM) {
164
+ if (value === RISK_LEVELS.LOW) return RISK_LEVELS.LOW;
165
+ if (value === RISK_LEVELS.MEDIUM) return RISK_LEVELS.MEDIUM;
166
+ if (value === RISK_LEVELS.HIGH) return RISK_LEVELS.HIGH;
167
+ return fallback;
168
+ }
169
+
170
+ function clonePolicy(policy) {
171
+ return JSON.parse(JSON.stringify(policy));
172
+ }
173
+
174
+ function getToolPolicyMetadata(toolName) {
175
+ return TOOL_POLICY_METADATA[toolName] || null;
176
+ }
177
+
178
+ function resolveToolPolicy(toolName, descriptor = null) {
179
+ const base = clonePolicy(
180
+ getToolPolicyMetadata(toolName) || {
181
+ riskLevel: RISK_LEVELS.MEDIUM,
182
+ category: TOOL_CATEGORIES.EXECUTE,
183
+ availableInPlanMode: false,
184
+ planModeBehavior: "blocked",
185
+ requiresPlanApproval: false,
186
+ requiresConfirmation: false,
187
+ approvalFlow: "policy",
188
+ isReadOnly: false,
189
+ },
190
+ );
191
+
192
+ if (descriptor?.riskLevel) {
193
+ base.riskLevel = normalizeRiskLevel(descriptor.riskLevel, base.riskLevel);
194
+ }
195
+
196
+ if (descriptor?.isReadOnly === true) {
197
+ base.isReadOnly = true;
198
+ base.riskLevel = RISK_LEVELS.LOW;
199
+ base.availableInPlanMode = true;
200
+ base.planModeBehavior = "allow";
201
+ base.requiresPlanApproval = false;
202
+ base.requiresConfirmation = false;
203
+ base.approvalFlow = "auto";
204
+ }
205
+
206
+ return base;
207
+ }
208
+
209
+ function evaluateToolPolicy(options = {}) {
210
+ const {
211
+ toolName,
212
+ toolDescriptor = null,
213
+ planModeState = "inactive",
214
+ confirmed = false,
215
+ toolArgs = null,
216
+ } = options;
217
+
218
+ if (!toolName) {
219
+ throw new Error("evaluateToolPolicy requires toolName");
220
+ }
221
+
222
+ const policy = resolveToolPolicy(toolName, toolDescriptor);
223
+ const readOnlyGitAllowed =
224
+ toolName === "git" &&
225
+ policy.planModeBehavior === "readonly-conditional" &&
226
+ isReadOnlyGitCommand(toolArgs?.command);
227
+ const planApproved = PLAN_APPROVED_STATES.includes(planModeState);
228
+
229
+ if (policy.isReadOnly || policy.riskLevel === RISK_LEVELS.LOW) {
230
+ return {
231
+ toolName,
232
+ allowed: true,
233
+ decision: TOOL_DECISIONS.ALLOW,
234
+ requiresPlanApproval: false,
235
+ requiresConfirmation: false,
236
+ reason: "Read-only tools are allowed without plan approval.",
237
+ riskLevel: policy.riskLevel,
238
+ category: policy.category,
239
+ planModeState,
240
+ planModeBehavior: policy.planModeBehavior,
241
+ readOnlySubcommands: policy.readOnlySubcommands || [],
242
+ };
243
+ }
244
+
245
+ if (readOnlyGitAllowed) {
246
+ return {
247
+ toolName,
248
+ allowed: true,
249
+ decision: TOOL_DECISIONS.ALLOW,
250
+ requiresPlanApproval: false,
251
+ requiresConfirmation: false,
252
+ reason: "Read-only git commands are allowed during plan mode.",
253
+ riskLevel: RISK_LEVELS.LOW,
254
+ category: TOOL_CATEGORIES.READ,
255
+ planModeState,
256
+ planModeBehavior: policy.planModeBehavior,
257
+ readOnlySubcommands: policy.readOnlySubcommands || [],
258
+ };
259
+ }
260
+
261
+ if (policy.riskLevel === RISK_LEVELS.MEDIUM) {
262
+ if (planApproved) {
263
+ return {
264
+ toolName,
265
+ allowed: true,
266
+ decision: TOOL_DECISIONS.ALLOW,
267
+ requiresPlanApproval: false,
268
+ requiresConfirmation: false,
269
+ reason: "Plan-approved write tool is allowed.",
270
+ riskLevel: policy.riskLevel,
271
+ category: policy.category,
272
+ planModeState,
273
+ planModeBehavior: policy.planModeBehavior,
274
+ readOnlySubcommands: policy.readOnlySubcommands || [],
275
+ };
276
+ }
277
+
278
+ return {
279
+ toolName,
280
+ allowed: false,
281
+ decision: TOOL_DECISIONS.REQUIRE_PLAN,
282
+ requiresPlanApproval: true,
283
+ requiresConfirmation: false,
284
+ reason: "Write tools require an approved plan before execution.",
285
+ riskLevel: policy.riskLevel,
286
+ category: policy.category,
287
+ planModeState,
288
+ planModeBehavior: policy.planModeBehavior,
289
+ readOnlySubcommands: policy.readOnlySubcommands || [],
290
+ };
291
+ }
292
+
293
+ if (!planApproved) {
294
+ return {
295
+ toolName,
296
+ allowed: false,
297
+ decision: TOOL_DECISIONS.REQUIRE_PLAN,
298
+ requiresPlanApproval: true,
299
+ requiresConfirmation: false,
300
+ reason: "High-risk tools require an approved plan first.",
301
+ riskLevel: policy.riskLevel,
302
+ category: policy.category,
303
+ planModeState,
304
+ planModeBehavior: policy.planModeBehavior,
305
+ readOnlySubcommands: policy.readOnlySubcommands || [],
306
+ };
307
+ }
308
+
309
+ if (policy.requiresConfirmation && !confirmed) {
310
+ return {
311
+ toolName,
312
+ allowed: false,
313
+ decision: TOOL_DECISIONS.REQUIRE_CONFIRMATION,
314
+ requiresPlanApproval: false,
315
+ requiresConfirmation: true,
316
+ reason: "High-risk tools require an explicit second confirmation.",
317
+ riskLevel: policy.riskLevel,
318
+ category: policy.category,
319
+ planModeState,
320
+ planModeBehavior: policy.planModeBehavior,
321
+ readOnlySubcommands: policy.readOnlySubcommands || [],
322
+ };
323
+ }
324
+
325
+ return {
326
+ toolName,
327
+ allowed: true,
328
+ decision: TOOL_DECISIONS.ALLOW,
329
+ requiresPlanApproval: false,
330
+ requiresConfirmation: false,
331
+ reason: policy.requiresConfirmation
332
+ ? "High-risk tool confirmed after plan approval."
333
+ : "Tool allowed after plan approval.",
334
+ riskLevel: policy.riskLevel,
335
+ category: policy.category,
336
+ planModeState,
337
+ planModeBehavior: policy.planModeBehavior,
338
+ readOnlySubcommands: policy.readOnlySubcommands || [],
339
+ };
340
+ }
341
+
342
+ module.exports = {
343
+ PLAN_APPROVED_STATES,
344
+ READ_ONLY_GIT_SUBCOMMANDS,
345
+ RISK_LEVELS,
346
+ TOOL_CATEGORIES,
347
+ TOOL_DECISIONS,
348
+ TOOL_POLICY_METADATA,
349
+ evaluateToolPolicy,
350
+ getToolPolicyMetadata,
351
+ isReadOnlyGitCommand,
352
+ normalizeGitCommand,
353
+ resolveToolPolicy,
354
+ };
@@ -0,0 +1,233 @@
1
+ "use strict";
2
+
3
+ const SHELL_POLICY_DECISIONS = Object.freeze({
4
+ ALLOW: "allow",
5
+ DENY: "deny",
6
+ WARN: "warn",
7
+ REROUTE: "reroute",
8
+ });
9
+
10
+ const BLOCKED_SHELL_RULES = Object.freeze([
11
+ {
12
+ id: "git-tool-reroute",
13
+ decision: SHELL_POLICY_DECISIONS.REROUTE,
14
+ test: ({ firstToken }) => firstToken === "git",
15
+ reason:
16
+ "Use the dedicated git tool instead of run_shell for repository operations.",
17
+ },
18
+ {
19
+ id: "dangerous-delete",
20
+ decision: SHELL_POLICY_DECISIONS.DENY,
21
+ test: ({ firstToken }) =>
22
+ ["rm", "del", "erase", "rmdir", "rd"].includes(firstToken),
23
+ reason:
24
+ "Destructive delete commands are blocked by the coding-agent shell policy.",
25
+ },
26
+ {
27
+ id: "dangerous-git-reset",
28
+ decision: SHELL_POLICY_DECISIONS.DENY,
29
+ test: ({ firstToken, secondToken }) =>
30
+ firstToken === "git" && secondToken === "reset",
31
+ reason:
32
+ "Potentially destructive git reset operations are blocked by the coding-agent shell policy.",
33
+ },
34
+ {
35
+ id: "dangerous-git-checkout-discard",
36
+ decision: SHELL_POLICY_DECISIONS.DENY,
37
+ test: ({ firstToken, tokens }) =>
38
+ firstToken === "git" &&
39
+ secondTokenIs(tokens, "checkout") &&
40
+ tokens.includes("--"),
41
+ reason:
42
+ "Discard-style git checkout commands are blocked by the coding-agent shell policy.",
43
+ },
44
+ {
45
+ id: "dangerous-git-clean",
46
+ decision: SHELL_POLICY_DECISIONS.DENY,
47
+ test: ({ firstToken, secondToken }) =>
48
+ firstToken === "git" && secondToken === "clean",
49
+ reason:
50
+ "git clean is blocked by the coding-agent shell policy.",
51
+ },
52
+ {
53
+ id: "network-download",
54
+ decision: SHELL_POLICY_DECISIONS.DENY,
55
+ test: ({ firstToken }) =>
56
+ ["curl", "wget", "invoke-webrequest", "iwr"].includes(firstToken),
57
+ reason:
58
+ "Network download commands are blocked by the coding-agent shell policy.",
59
+ },
60
+ {
61
+ id: "powershell-encoded-command",
62
+ decision: SHELL_POLICY_DECISIONS.DENY,
63
+ test: ({ firstToken, tokens }) =>
64
+ ["powershell", "powershell.exe", "pwsh", "pwsh.exe"].includes(
65
+ firstToken,
66
+ ) &&
67
+ tokens.some((token) =>
68
+ ["-encodedcommand", "-enc"].includes(token.toLowerCase()),
69
+ ),
70
+ reason:
71
+ "Encoded PowerShell commands are blocked by the coding-agent shell policy.",
72
+ },
73
+ ]);
74
+
75
+ const ALLOWLISTED_SHELL_RULES = Object.freeze([
76
+ {
77
+ id: "npm-test",
78
+ test: ({ normalized }) =>
79
+ /^(npm|npm\.cmd)\s+run\s+test(?::[\w:-]+)?(?:\s|$)/i.test(normalized),
80
+ reason: "Package test runs are allowlisted verification commands.",
81
+ },
82
+ {
83
+ id: "npm-lint",
84
+ test: ({ normalized }) =>
85
+ /^(npm|npm\.cmd)\s+run\s+lint(?:\s|$)/i.test(normalized),
86
+ reason: "Package lint runs are allowlisted verification commands.",
87
+ },
88
+ {
89
+ id: "npm-build",
90
+ test: ({ normalized }) =>
91
+ /^(npm|npm\.cmd)\s+run\s+build(?::[\w:-]+)?(?:\s|$)/i.test(normalized),
92
+ reason: "Package build runs are allowlisted verification commands.",
93
+ },
94
+ {
95
+ id: "playwright-single-file",
96
+ test: ({ normalized }) =>
97
+ /^npx\s+playwright\s+test\s+\S+(?:\s|$)/i.test(normalized),
98
+ reason: "Single-file Playwright runs are allowlisted verification commands.",
99
+ },
100
+ {
101
+ id: "ripgrep-search",
102
+ test: ({ firstToken }) => firstToken === "rg",
103
+ reason: "ripgrep search commands are allowlisted read-style commands.",
104
+ },
105
+ ]);
106
+
107
+ function secondTokenIs(tokens, value) {
108
+ return (tokens[1] || "").toLowerCase() === value;
109
+ }
110
+
111
+ function splitFirstCommandSegment(command) {
112
+ return String(command || "")
113
+ .split(/(?:\|\||&&|[|;])/)[0]
114
+ .trim();
115
+ }
116
+
117
+ function tokenizeShellCommand(command) {
118
+ const tokens = [];
119
+ let current = "";
120
+ let inDouble = false;
121
+ let inSingle = false;
122
+ let escaping = false;
123
+
124
+ for (const ch of String(command || "")) {
125
+ if (escaping) {
126
+ current += ch;
127
+ escaping = false;
128
+ continue;
129
+ }
130
+
131
+ if (ch === "\\" && !inSingle) {
132
+ escaping = true;
133
+ continue;
134
+ }
135
+
136
+ if (ch === '"' && !inSingle) {
137
+ inDouble = !inDouble;
138
+ continue;
139
+ }
140
+
141
+ if (ch === "'" && !inDouble) {
142
+ inSingle = !inSingle;
143
+ continue;
144
+ }
145
+
146
+ if ((ch === " " || ch === "\t") && !inDouble && !inSingle) {
147
+ if (current) {
148
+ tokens.push(current);
149
+ current = "";
150
+ }
151
+ continue;
152
+ }
153
+
154
+ current += ch;
155
+ }
156
+
157
+ if (current) {
158
+ tokens.push(current);
159
+ }
160
+
161
+ return tokens;
162
+ }
163
+
164
+ function normalizeShellCommand(command) {
165
+ return splitFirstCommandSegment(command).replace(/\s+/g, " ").trim();
166
+ }
167
+
168
+ function evaluateShellCommandPolicy(command) {
169
+ const normalized = normalizeShellCommand(command);
170
+ const tokens = tokenizeShellCommand(normalized);
171
+ const firstToken = (tokens[0] || "").toLowerCase();
172
+ const secondToken = (tokens[1] || "").toLowerCase();
173
+ const context = {
174
+ command: String(command || ""),
175
+ normalized,
176
+ tokens,
177
+ firstToken,
178
+ secondToken,
179
+ };
180
+
181
+ if (!normalized) {
182
+ return {
183
+ allowed: false,
184
+ decision: SHELL_POLICY_DECISIONS.DENY,
185
+ reason: "Shell command is required.",
186
+ ruleId: "empty-command",
187
+ normalizedCommand: normalized,
188
+ };
189
+ }
190
+
191
+ const blockedRule = BLOCKED_SHELL_RULES.find((rule) => rule.test(context));
192
+ if (blockedRule) {
193
+ return {
194
+ allowed: false,
195
+ decision: blockedRule.decision,
196
+ reason: blockedRule.reason,
197
+ ruleId: blockedRule.id,
198
+ normalizedCommand: normalized,
199
+ };
200
+ }
201
+
202
+ const allowlistedRule = ALLOWLISTED_SHELL_RULES.find((rule) =>
203
+ rule.test(context),
204
+ );
205
+ if (allowlistedRule) {
206
+ return {
207
+ allowed: true,
208
+ decision: SHELL_POLICY_DECISIONS.ALLOW,
209
+ reason: allowlistedRule.reason,
210
+ ruleId: allowlistedRule.id,
211
+ normalizedCommand: normalized,
212
+ };
213
+ }
214
+
215
+ return {
216
+ allowed: true,
217
+ decision: SHELL_POLICY_DECISIONS.WARN,
218
+ reason:
219
+ "Command is not on the preferred verification allowlist, but it is not explicitly blocked.",
220
+ ruleId: "unclassified-command",
221
+ normalizedCommand: normalized,
222
+ };
223
+ }
224
+
225
+ module.exports = {
226
+ ALLOWLISTED_SHELL_RULES,
227
+ BLOCKED_SHELL_RULES,
228
+ SHELL_POLICY_DECISIONS,
229
+ evaluateShellCommandPolicy,
230
+ normalizeShellCommand,
231
+ splitFirstCommandSegment,
232
+ tokenizeShellCommand,
233
+ };
@@ -12,8 +12,21 @@ export function createSessionRecord(session = {}, extras = {}) {
12
12
  type: session.type || extras.sessionType || null,
13
13
  provider: session.provider || extras.provider || null,
14
14
  model: session.model || extras.model || null,
15
+ baseUrl: session.baseUrl || extras.baseUrl || null,
16
+ enabledToolNames: Array.isArray(extras.enabledToolNames)
17
+ ? extras.enabledToolNames
18
+ : Array.isArray(session.enabledToolNames)
19
+ ? session.enabledToolNames
20
+ : [],
15
21
  projectRoot: session.projectRoot || extras.projectRoot || null,
16
22
  baseProjectRoot: session.baseProjectRoot || extras.baseProjectRoot || null,
23
+ planModeState:
24
+ extras.planModeState ||
25
+ session.planManager?.state ||
26
+ session.planModeState ||
27
+ null,
28
+ hasHostManagedToolPolicy:
29
+ extras.hasHostManagedToolPolicy ?? !!session.hostManagedToolPolicy,
17
30
  worktreeIsolation:
18
31
  session.worktreeIsolation === true || extras.worktreeIsolation === true,
19
32
  worktree:
@@ -11,3 +11,17 @@ export { createSessionRecord } from "./contracts/session-record.js";
11
11
  export { createTaskRecord } from "./contracts/task-record.js";
12
12
  export { createWorktreeRecord } from "./contracts/worktree-record.js";
13
13
  export { createTelemetryRecord } from "./contracts/telemetry-record.js";
14
+ export {
15
+ CODING_AGENT_MVP_TOOL_NAMES,
16
+ CODING_AGENT_EXTENSION_TOOL_NAMES,
17
+ createCodingAgentToolRegistry,
18
+ getCodingAgentRuntimeDescriptor,
19
+ getCodingAgentToolContract,
20
+ getCodingAgentToolContracts,
21
+ getCodingAgentToolPolicy,
22
+ isCodingAgentMvpTool,
23
+ listCodingAgentToolNames,
24
+ mapCodingAgentToolDefinition,
25
+ } from "./coding-agent-contract.js";
26
+ export { default as codingAgentManagedToolPolicy } from "./coding-agent-managed-tool-policy.cjs";
27
+ export { default as codingAgentShellPolicy } from "./coding-agent-shell-policy.cjs";
@@ -1,4 +1,31 @@
1
1
  import { EventEmitter } from "node:events";
2
+ import { createRequire } from "node:module";
3
+
4
+ const requireCjs = createRequire(import.meta.url);
5
+ const codingAgentEventsCjs = requireCjs("./coding-agent-events.cjs");
6
+
7
+ /**
8
+ * Canonical Coding Agent event protocol — re-exported from the shared CJS
9
+ * module so the CLI runtime, the Desktop Main process and any future host
10
+ * all consume from the same source of truth.
11
+ *
12
+ * The legacy `RUNTIME_EVENTS` constants below remain for internal runtime
13
+ * bookkeeping (turn:start, server:start, etc.). They are NOT the wire
14
+ * protocol — when emitting events that cross the CLI/Desktop boundary,
15
+ * use `createCodingAgentEvent` and the `CODING_AGENT_EVENT_TYPES` enum.
16
+ */
17
+ export const {
18
+ CODING_AGENT_EVENT_VERSION,
19
+ CODING_AGENT_EVENT_CHANNEL,
20
+ CODING_AGENT_EVENT_TYPES,
21
+ LEGACY_TO_UNIFIED_TYPE,
22
+ CodingAgentSequenceTracker,
23
+ defaultSequenceTracker,
24
+ createCodingAgentEvent,
25
+ wrapLegacyMessage,
26
+ validateCodingAgentEvent,
27
+ mapLegacyType,
28
+ } = codingAgentEventsCjs;
2
29
 
3
30
  export const RUNTIME_EVENTS = {
4
31
  RUNTIME_START: "runtime:start",
@@ -20,3 +20,15 @@ export {
20
20
  listLegacyAgentToolNames,
21
21
  getRuntimeToolDescriptor,
22
22
  } from "./legacy-agent-tools.js";
23
+ export {
24
+ CODING_AGENT_MVP_TOOL_NAMES,
25
+ CODING_AGENT_EXTENSION_TOOL_NAMES,
26
+ createCodingAgentToolRegistry,
27
+ getCodingAgentRuntimeDescriptor,
28
+ getCodingAgentToolContract,
29
+ getCodingAgentToolContracts,
30
+ getCodingAgentToolPolicy,
31
+ isCodingAgentMvpTool,
32
+ listCodingAgentToolNames,
33
+ mapCodingAgentToolDefinition,
34
+ } from "../runtime/coding-agent-contract.js";