@williambeto/ai-workflow 2.8.0 → 2.8.2

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.
@@ -1,7 +1,8 @@
1
1
  import {
2
2
  ArtifactFidelityGate,
3
- DeliveryDecisionEngine
4
- } from "./chunk-XW747GIG.js";
3
+ DeliveryDecisionEngine,
4
+ RequestRouter
5
+ } from "./chunk-H7GIKXFO.js";
5
6
 
6
7
  // src/core/package-assets.ts
7
8
  import { fileURLToPath } from "url";
@@ -94,21 +95,145 @@ var AGENT_PROMPT_CONTRACTS = {
94
95
  Sage: "Act as Sage, the independent validation agent. Audit the implementation against requirements, inspect evidence, and report concrete pass/fail findings.",
95
96
  Phoenix: "Act as Phoenix, the bounded remediation agent. Remediate only concrete findings, keep attempts bounded, and stop when no progress is made."
96
97
  };
98
+ function buildPromptFallback(agent, message, orchestratedChild = false) {
99
+ if (agent === "Astra") {
100
+ return [
101
+ "You are Atlas, the AI Workflow Kit coordinator running under Atlas permissions.",
102
+ "Delegate the implementation exactly once to the configured Astra agent using the task tool. Do not edit workspace files yourself and do not pretend to be Astra.",
103
+ orchestratedChild ? "The parent execute process already owns branch safety, validation, evidence, and finalization. Do not run git branch/status, collect-evidence, handoff, or another execute workflow." : "Keep coordination minimal and let Astra own every requested workspace mutation.",
104
+ "Ask Astra to make the smallest scoped change and run only proportional validation: docs integrity for docs, a focused behavior test for simple code, and tests plus build for normal React work. Screenshots, README changes, Sage, and Phoenix are conditional, not default.",
105
+ "Return Astra's observed result without adding a second implementation or finalization pass.",
106
+ "",
107
+ "User request:",
108
+ message
109
+ ].join("\n");
110
+ }
111
+ return `${AGENT_PROMPT_CONTRACTS[agent]}
112
+
113
+ Runtime agent selection note: requested actor '${agent}' could not be applied with --agent because this OpenCode runtime does not support it. Continue under this bounded prompt contract and preserve all AI Workflow Kit safety rules.
114
+
115
+ User request:
116
+ ${message}`;
117
+ }
97
118
  function extractSdkExecution(data, requestedAgent) {
98
119
  const parts = data?.parts || [];
99
120
  const output = parts.filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text).join("");
100
121
  const commandsRun = parts.filter((part) => part.type === "tool").map((part) => part.state?.input?.command ?? part.state?.input?.CommandLine).filter((command) => typeof command === "string");
122
+ const commandEvents = commandsRun.map((command) => ({
123
+ command,
124
+ tool: "sdk-tool",
125
+ status: "UNKNOWN",
126
+ exitCode: null,
127
+ source: "sdk"
128
+ }));
101
129
  const appliedAgent = typeof data?.info?.agent === "string" ? data.info.agent : null;
102
130
  const confirmed = appliedAgent === requestedAgent;
103
131
  return {
104
132
  success: !data?.info?.error && confirmed,
105
133
  commandsRun,
134
+ commandEvents,
106
135
  eventCount: parts.length,
107
136
  runtimeAgentApplied: appliedAgent,
108
137
  runtimeActorConfirmation: confirmed ? "confirmed" : "unavailable",
109
138
  output
110
139
  };
111
140
  }
141
+ function extractCommandEvents(event) {
142
+ const calls = Array.isArray(event?.part?.toolCalls) ? event.part.toolCalls : event?.type === "tool" || event?.type === "tool_use" || event?.type === "tool_result" ? [event.part || event] : [];
143
+ return calls.flatMap((call) => {
144
+ const input = call?.args || call?.input || call?.state?.input || call?.part?.state?.input || {};
145
+ const command = input.command ?? input.CommandLine ?? call?.command;
146
+ if (typeof command !== "string" || !command.trim()) return [];
147
+ const rawExit = call?.exitCode ?? call?.output?.exitCode ?? call?.state?.metadata?.exitCode ?? event?.exitCode;
148
+ const exitCode = typeof rawExit === "number" ? rawExit : null;
149
+ return [{
150
+ command: command.trim(),
151
+ tool: String(call?.name || call?.tool || event?.tool || "command"),
152
+ status: exitCode === null ? "UNKNOWN" : exitCode === 0 ? "PASS" : "FAIL",
153
+ exitCode,
154
+ source: "json-event"
155
+ }];
156
+ });
157
+ }
158
+ function extractDelegatedAgents(event) {
159
+ const calls = Array.isArray(event?.part?.toolCalls) ? event.part.toolCalls : ["tool", "tool_use"].includes(event?.type) ? [event?.part || event] : [];
160
+ const delegatedAgents = [];
161
+ for (const call of calls) {
162
+ const tool = String(call?.name || call?.tool || event?.tool || "").toLowerCase();
163
+ if (tool !== "task") continue;
164
+ const input = call?.args || call?.input || call?.state?.input || call?.part?.state?.input || {};
165
+ const delegated = input.subagent_type ?? input.agent ?? input.subagent ?? input.agentName;
166
+ if (typeof delegated === "string" && delegated.trim()) delegatedAgents.push(delegated.trim());
167
+ }
168
+ return delegatedAgents;
169
+ }
170
+ function countAstraCompletionMarkers(value) {
171
+ return [...String(value || "").matchAll(/(?:✓|✔).*\bAstra Agent\b/gi)].length;
172
+ }
173
+ var MUTATING_GIT_COMMANDS = /* @__PURE__ */ new Set([
174
+ "add",
175
+ "am",
176
+ "apply",
177
+ "bisect",
178
+ "checkout",
179
+ "cherry-pick",
180
+ "clean",
181
+ "clone",
182
+ "commit",
183
+ "fetch",
184
+ "gc",
185
+ "init",
186
+ "merge",
187
+ "mv",
188
+ "notes",
189
+ "pull",
190
+ "push",
191
+ "rebase",
192
+ "reset",
193
+ "restore",
194
+ "revert",
195
+ "rm",
196
+ "stash",
197
+ "switch",
198
+ "update-index"
199
+ ]);
200
+ function gitCommandIsDestructive(command) {
201
+ for (const match of command.matchAll(/\bgit\b([^;&|]*)/gi)) {
202
+ const tokens = match[1].trim().split(/\s+/).filter(Boolean);
203
+ while (tokens[0]?.startsWith("-")) {
204
+ const option = tokens.shift();
205
+ if (["-C", "--git-dir", "--work-tree", "--namespace"].includes(option || "")) tokens.shift();
206
+ }
207
+ const subcommand = tokens.shift()?.toLowerCase();
208
+ if (!subcommand) continue;
209
+ if (MUTATING_GIT_COMMANDS.has(subcommand)) return true;
210
+ if (subcommand === "branch") {
211
+ if (tokens.length === 0) continue;
212
+ const mutatingBranchFlag = tokens.some(
213
+ (token) => /^(?:-[dDmMcC]|--(?:delete|move|copy|edit-description|set-upstream-to|unset-upstream))(?:=|$)/.test(token)
214
+ );
215
+ if (mutatingBranchFlag) return true;
216
+ const first = tokens[0];
217
+ const readOnlyBranchFlag = /^(?:--(?:show-current|list|all|remotes|contains|no-contains|merged|no-merged|points-at|format|sort|column|no-column|color|no-color)(?:=|$)|-[larv](?:$|v$))/.test(first);
218
+ if (!readOnlyBranchFlag) return true;
219
+ }
220
+ if (subcommand === "tag") {
221
+ if (tokens.length === 0) continue;
222
+ const first = tokens[0];
223
+ if (!/^(?:--(?:list|contains|no-contains|merged|no-merged|points-at|format|sort)(?:=|$)|-[ln])/.test(first)) return true;
224
+ }
225
+ if (subcommand === "remote" && tokens.length > 0 && !/^(?:-v|--verbose|show|get-url)$/.test(tokens[0])) return true;
226
+ if (subcommand === "worktree" && tokens[0] !== "list") return true;
227
+ }
228
+ return false;
229
+ }
230
+ function isDestructiveCommand(command) {
231
+ if (/(^|[^<])(?:>>|>)/.test(command)) return true;
232
+ if (/\b(?:touch|rm|mkdir|cp|mv|chmod|chown|tee)\b/.test(command)) return true;
233
+ if (/\bsed\b[^;&|]*(?:\s-i[^\s]*|\s--in-place(?:=|\s|$))/.test(command)) return true;
234
+ if (/\bawk\b[^;&|]*\s-i\s*inplace\b/.test(command)) return true;
235
+ return gitCommandIsDestructive(command);
236
+ }
112
237
  var OpenCodeAdapter = class {
113
238
  cwd;
114
239
  constructor({ cwd = process.cwd() } = {}) {
@@ -148,7 +273,7 @@ var OpenCodeAdapter = class {
148
273
  /**
149
274
  * Runs opencode with a prompt and options.
150
275
  */
151
- async execute(message, { agent = null, model = null, readOnly = false, fastTrack = false, requireActorConfirmation = false } = {}) {
276
+ async execute(message, { agent = null, model = null, readOnly = false, fastTrack = false, requireActorConfirmation = false, orchestratedChild = false } = {}) {
152
277
  const inspection = await this.inspect();
153
278
  const isSpecializedAgent = agent && ["Nexus", "Orion", "Astra", "Sage", "Phoenix"].includes(agent);
154
279
  if (!readOnly) {
@@ -194,13 +319,11 @@ var OpenCodeAdapter = class {
194
319
  };
195
320
  }
196
321
  const usePromptFallback = Boolean(isSpecializedAgent && !inspection.supports.agent && agent);
197
- const runtimeMessage = usePromptFallback ? `${AGENT_PROMPT_CONTRACTS[agent]}
198
-
199
- Runtime agent selection note: requested actor '${agent}' could not be applied with --agent because this OpenCode runtime does not support it. Continue as that actor by prompt contract and preserve all AI Workflow Kit safety rules.
322
+ const agentMessage = usePromptFallback ? buildPromptFallback(agent, message, orchestratedChild) : message;
323
+ const runtimeMessage = orchestratedChild ? `${agentMessage}
200
324
 
201
- User request:
202
- ${message}` : message;
203
- const runtimeAgentSelectionMode = isSpecializedAgent ? usePromptFallback ? "prompt-fallback" : "explicit" : "default";
325
+ AIWK_ORCHESTRATED_CHILD: The parent execute process exclusively owns collect-evidence, handoff, and finalization. Do not invoke those operations or start another execute workflow.` : agentMessage;
326
+ const runtimeAgentSelectionMode = isSpecializedAgent ? usePromptFallback && agent === "Astra" ? "atlas-delegation-fallback" : usePromptFallback ? "prompt-fallback" : "explicit" : "default";
204
327
  const runWithSdk = async (targetCwd) => {
205
328
  if (!agent) {
206
329
  return {
@@ -220,7 +343,15 @@ ${message}` : message;
220
343
  let server;
221
344
  try {
222
345
  const { createOpencode } = await import("@opencode-ai/sdk/v2");
223
- const runtime = await createOpencode({ port: 0, timeout: 1e4 });
346
+ const previousFinalizationOwner = process.env.AI_WORKFLOW_FINALIZATION_OWNER;
347
+ if (orchestratedChild) process.env.AI_WORKFLOW_FINALIZATION_OWNER = "execute";
348
+ let runtime;
349
+ try {
350
+ runtime = await createOpencode({ port: 0, timeout: 1e4 });
351
+ } finally {
352
+ if (previousFinalizationOwner === void 0) delete process.env.AI_WORKFLOW_FINALIZATION_OWNER;
353
+ else process.env.AI_WORKFLOW_FINALIZATION_OWNER = previousFinalizationOwner;
354
+ }
224
355
  server = runtime.server;
225
356
  const permissions = readOnly ? [
226
357
  { permission: "edit", pattern: "*", action: "deny" },
@@ -238,7 +369,7 @@ ${message}` : message;
238
369
  sessionID: session.data.id,
239
370
  directory: targetCwd,
240
371
  agent,
241
- parts: [{ type: "text", text: message }]
372
+ parts: [{ type: "text", text: runtimeMessage }]
242
373
  }, { throwOnError: true });
243
374
  const extracted = extractSdkExecution(response.data, agent);
244
375
  if (extracted.output) process.stdout.write(extracted.output);
@@ -283,6 +414,7 @@ ${message}` : message;
283
414
  console.log(`[RUNTIME] Delegating to OpenCode: opencode ${args.map((a) => a.includes(" ") ? `"${a}"` : a).join(" ")}`);
284
415
  const child = spawn("opencode", args, {
285
416
  cwd: targetCwd,
417
+ env: orchestratedChild ? { ...process.env, AI_WORKFLOW_FINALIZATION_OWNER: "execute" } : process.env,
286
418
  stdio: ["ignore", "pipe", "inherit"]
287
419
  // Inherit stderr to show warnings directly
288
420
  });
@@ -291,35 +423,65 @@ ${message}` : message;
291
423
  terminal: false
292
424
  });
293
425
  const commandsRun = [];
426
+ const commandEvents = [];
294
427
  let eventCount = 0;
295
428
  let output = "";
296
429
  let confirmedAgent = null;
430
+ let delegatedAgent = null;
431
+ let astraTaskInvocations = 0;
432
+ let astraCompletionMarkers = 0;
297
433
  rl.on("line", (line) => {
298
434
  const trimmed = line.trim();
299
435
  if (!trimmed) return;
300
436
  try {
301
437
  const event = JSON.parse(trimmed);
302
438
  eventCount++;
439
+ const observedDelegations = extractDelegatedAgents(event);
440
+ astraTaskInvocations += observedDelegations.filter((value) => value === "Astra").length;
441
+ if (observedDelegations.includes("Astra")) delegatedAgent = "Astra";
442
+ for (const observation of extractCommandEvents(event)) {
443
+ commandEvents.push(observation);
444
+ if (!commandsRun.includes(observation.command)) commandsRun.push(observation.command);
445
+ }
446
+ if (readOnly) {
447
+ const unsafe = commandEvents.find((item) => isDestructiveCommand(item.command));
448
+ if (unsafe) {
449
+ child.kill();
450
+ resolve2({
451
+ success: false,
452
+ commandsRun,
453
+ commandEvents,
454
+ eventCount,
455
+ error: `Read-only confinement violation: command '${unsafe.command}' attempted write/modify operation during read-only task.`,
456
+ runtimeAgentRequested: agent || null,
457
+ runtimeAgentApplied: null,
458
+ runtimeAgentSupported: inspection.supports.agent,
459
+ runtimeAgentSelectionMode,
460
+ runtimeActorConfirmation: confirmedAgent ? "confirmed" : "unavailable",
461
+ output
462
+ });
463
+ return;
464
+ }
465
+ }
303
466
  if (event.type === "agent_identity" && event.confirmed === true && typeof event.agent === "string") {
304
467
  confirmedAgent = event.agent;
305
468
  }
306
469
  if (event.type === "text" && event.part?.text) {
307
470
  process.stdout.write(event.part.text);
308
471
  output += event.part.text;
472
+ const markers = countAstraCompletionMarkers(event.part.text);
473
+ astraCompletionMarkers += markers;
474
+ if (markers > 0) delegatedAgent = "Astra";
309
475
  }
310
476
  if (event.type === "step_start" && event.part?.toolCalls) {
311
477
  for (const call of event.part.toolCalls) {
312
- if (call.name === "run_command" && call.args?.CommandLine) {
313
- commandsRun.push(call.args.CommandLine);
314
- }
315
478
  if (readOnly) {
316
479
  const name = call.name || "";
317
480
  const isWriteTool = name.startsWith("write_") || name.includes("write") || name.includes("replace") || name.includes("edit") || name.includes("delete") || name.includes("remove");
318
481
  let isDestructiveGit = false;
319
482
  if (name === "run_command" && call.args?.CommandLine) {
320
483
  const cmd = call.args.CommandLine;
321
- const destPattern = /\b(git\s+(commit|add|push|merge|reset|rm|branch|checkout|switch|stash|init)|touch|rm|mkdir|cp|mv|chmod|chown|tee|sed|awk)\b|>>|>/;
322
- if (destPattern.test(cmd)) {
484
+ if (isDestructiveCommand(cmd)) {
323
485
  isDestructiveGit = true;
324
486
  }
325
487
  }
@@ -346,19 +508,38 @@ ${message}` : message;
346
508
  }
347
509
  } catch {
348
510
  console.log(trimmed);
511
+ output += `${trimmed}
512
+ `;
513
+ const shellCommand = trimmed.match(/^\$\s+(.+)$/)?.[1];
514
+ if (shellCommand) {
515
+ const observation = { command: shellCommand, tool: "shell", status: "UNKNOWN", exitCode: null, source: "text-output" };
516
+ commandEvents.push(observation);
517
+ if (!commandsRun.includes(shellCommand)) commandsRun.push(shellCommand);
518
+ }
519
+ const markers = usePromptFallback && agent === "Astra" ? countAstraCompletionMarkers(trimmed) : 0;
520
+ if (markers > 0) {
521
+ astraCompletionMarkers += markers;
522
+ delegatedAgent = "Astra";
523
+ }
349
524
  }
350
525
  });
351
526
  child.on("close", (code) => {
352
527
  console.log("\n[RUNTIME] OpenCode finished execution.");
528
+ const fallbackDelegationRequired = usePromptFallback && agent === "Astra";
529
+ const observedAstraDelegations = astraTaskInvocations > 0 ? astraTaskInvocations : astraCompletionMarkers;
530
+ const fallbackDelegationConfirmed = !fallbackDelegationRequired || delegatedAgent === "Astra" && observedAstraDelegations === 1;
531
+ const appliedAgent = fallbackDelegationRequired ? delegatedAgent : isSpecializedAgent ? confirmedAgent : null;
353
532
  resolve2({
354
- success: code === 0,
533
+ success: code === 0 && fallbackDelegationConfirmed,
355
534
  commandsRun,
535
+ commandEvents,
356
536
  eventCount,
537
+ error: code === 0 && !fallbackDelegationConfirmed ? `OpenCode fallback requires exactly one observed Atlas-to-Astra delegation; observed ${observedAstraDelegations}.` : void 0,
357
538
  runtimeAgentRequested: agent || null,
358
- runtimeAgentApplied: isSpecializedAgent ? confirmedAgent : null,
539
+ runtimeAgentApplied: appliedAgent,
359
540
  runtimeAgentSupported: inspection.supports.agent,
360
541
  runtimeAgentSelectionMode,
361
- runtimeActorConfirmation: confirmedAgent ? "confirmed" : "unavailable",
542
+ runtimeActorConfirmation: appliedAgent ? "confirmed" : "unavailable",
362
543
  output
363
544
  });
364
545
  });
@@ -368,6 +549,7 @@ ${message}` : message;
368
549
  resolve2({
369
550
  success: false,
370
551
  commandsRun,
552
+ commandEvents,
371
553
  eventCount: 0,
372
554
  error: err.message,
373
555
  runtimeAgentRequested: agent || null,
@@ -788,7 +970,7 @@ var EvidenceLedger = class {
788
970
  observed,
789
971
  runtime
790
972
  }) {
791
- const uniqueTerminalEvents = /* @__PURE__ */ new Set(["specification_complete", "planning_complete", "implementation_complete"]);
973
+ const uniqueTerminalEvents = /* @__PURE__ */ new Set(["specification_complete", "planning_complete", "implementation_complete", "finalization_completed"]);
792
974
  if (uniqueTerminalEvents.has(eventType) && this.events.some((event2) => event2.eventType === eventType)) {
793
975
  throw new Error(`Conflicting ledger record: terminal event '${eventType}' already exists for workflow '${this.workflowId}'.`);
794
976
  }
@@ -1005,7 +1187,7 @@ var DelegationController = class {
1005
1187
  runtimeActorConfirmation: "unavailable"
1006
1188
  };
1007
1189
  } else {
1008
- runResult = await this.adapter.execute(prompt, { agent: actor, readOnly, requireActorConfirmation: true });
1190
+ runResult = await this.adapter.execute(prompt, { agent: actor, readOnly, requireActorConfirmation: true, orchestratedChild: true });
1009
1191
  }
1010
1192
  if (runResult.success && requireExplicitActor) {
1011
1193
  if (!["explicit", "sdk-session"].includes(runResult.runtimeAgentSelectionMode || "") || runResult.runtimeAgentApplied !== actor || runResult.runtimeActorConfirmation !== "confirmed") {
@@ -1048,6 +1230,8 @@ var DelegationController = class {
1048
1230
  runtimeActorConfirmation: runResult.runtimeActorConfirmation || "unavailable",
1049
1231
  artifactPath: runResult.artifact?.artifactPath || null,
1050
1232
  artifactHash: runResult.artifact?.artifactHash || null,
1233
+ commandsRun: runResult.commandsRun || [],
1234
+ commandEvents: runResult.commandEvents || [],
1051
1235
  status: runResult.success ? "COMPLETED" : "BLOCKED"
1052
1236
  }
1053
1237
  });
@@ -1060,6 +1244,9 @@ var DelegationController = class {
1060
1244
  const decision = this.lastRoutingDecision ?? {
1061
1245
  operationType: "explain",
1062
1246
  mutationIntent: "readonly",
1247
+ deliveryMode: "answer-only",
1248
+ mutationOwner: null,
1249
+ capabilityRoles: [],
1063
1250
  permissionDecision: "allowed",
1064
1251
  reason: "no routing decision",
1065
1252
  workflowPath: []
@@ -1106,6 +1293,7 @@ var DelegationController = class {
1106
1293
  data: {
1107
1294
  success: runResult.success,
1108
1295
  commandsRun: runResult.commandsRun || [],
1296
+ commandEvents: runResult.commandEvents || [],
1109
1297
  eventCount: runResult.eventCount || 0,
1110
1298
  error: runResult.error,
1111
1299
  event: "implementation.completed",
@@ -1133,6 +1321,9 @@ var DelegationController = class {
1133
1321
  const decision = this.lastRoutingDecision ?? {
1134
1322
  operationType: "explain",
1135
1323
  mutationIntent: "readonly",
1324
+ deliveryMode: "answer-only",
1325
+ mutationOwner: null,
1326
+ capabilityRoles: [],
1136
1327
  permissionDecision: "allowed",
1137
1328
  reason: "no routing decision",
1138
1329
  workflowPath: []
@@ -1214,7 +1405,7 @@ ${JSON.stringify(result, null, 2)}`;
1214
1405
  import fs5 from "fs/promises";
1215
1406
  import path4 from "path";
1216
1407
  import crypto2 from "crypto";
1217
- import { execSync as execSync3 } from "child_process";
1408
+ import { execFileSync, execSync as execSync3 } from "child_process";
1218
1409
  var WorkspaceSnapshot = class {
1219
1410
  cwd;
1220
1411
  fastTrack;
@@ -1237,7 +1428,12 @@ var WorkspaceSnapshot = class {
1237
1428
  const files = {};
1238
1429
  const isTest = process.env.NODE_ENV === "test" || process.env.VITEST === "true";
1239
1430
  if (!this.fastTrack || isTest) {
1240
- await this.scanDir(this.cwd, files);
1431
+ const gitVisibleFiles = this.listGitVisibleFiles();
1432
+ if (gitVisibleFiles) {
1433
+ await this.scanFiles(gitVisibleFiles, files);
1434
+ } else {
1435
+ await this.scanDir(this.cwd, files);
1436
+ }
1241
1437
  }
1242
1438
  const execGit = (args) => {
1243
1439
  try {
@@ -1265,6 +1461,40 @@ var WorkspaceSnapshot = class {
1265
1461
  untrackedFilesHash: hashString(untrackedFiles)
1266
1462
  };
1267
1463
  }
1464
+ listGitVisibleFiles() {
1465
+ try {
1466
+ execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
1467
+ cwd: this.cwd,
1468
+ encoding: "utf8",
1469
+ stdio: "pipe"
1470
+ });
1471
+ const output = execFileSync("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
1472
+ cwd: this.cwd,
1473
+ encoding: "utf8",
1474
+ stdio: "pipe"
1475
+ });
1476
+ return [...new Set(output.split("\0").filter(Boolean))];
1477
+ } catch {
1478
+ return null;
1479
+ }
1480
+ }
1481
+ async scanFiles(relativePaths, filesMap) {
1482
+ for (const relativePath of relativePaths) {
1483
+ const normalizedPath = relativePath.replaceAll("\\", "/");
1484
+ if (this.isIgnored(normalizedPath)) continue;
1485
+ try {
1486
+ const fullPath = path4.join(this.cwd, relativePath);
1487
+ const stat = await fs5.stat(fullPath);
1488
+ if (!stat.isFile()) continue;
1489
+ const content = await fs5.readFile(fullPath);
1490
+ const sha256 = crypto2.createHash("sha256").update(content).digest("hex");
1491
+ const isExecutable = (stat.mode & 73) !== 0;
1492
+ const mode = isExecutable ? "100755" : "100644";
1493
+ filesMap[normalizedPath] = { sha256, mode };
1494
+ } catch {
1495
+ }
1496
+ }
1497
+ }
1268
1498
  async scanDir(dir, filesMap) {
1269
1499
  try {
1270
1500
  const entries = await fs5.readdir(dir, { withFileTypes: true });
@@ -1539,271 +1769,6 @@ function resolveWorkflowProfile({ request = "", explicitProfile = null } = {}) {
1539
1769
  return "generic";
1540
1770
  }
1541
1771
 
1542
- // src/core/request-router.ts
1543
- import path6 from "path";
1544
- import fs7 from "fs";
1545
- var RequestRouter = class {
1546
- cwd;
1547
- constructor({ cwd = process.cwd() } = {}) {
1548
- this.cwd = cwd;
1549
- }
1550
- /**
1551
- * Understands and classifies a natural language request.
1552
- */
1553
- understand(request) {
1554
- const text = String(request).trim();
1555
- if (!text) {
1556
- throw new Error("Cannot classify an empty request.");
1557
- }
1558
- const { operationType, requestedCapability } = this.extractCapability(text);
1559
- const mutationIntent = this.determineMutationIntent(text, operationType);
1560
- const riskAssessment = this.determineRiskAssessment(text, mutationIntent);
1561
- const riskLevel = riskAssessment.riskLevel;
1562
- const { safetyConstraints, requiredEvidence } = this.determineSafetyAndEvidence(text, mutationIntent);
1563
- return {
1564
- rawRequest: text,
1565
- taskGoal: `Perform ${operationType} operation under ${mutationIntent} intent`,
1566
- requestedActor: void 0,
1567
- invalidActor: void 0,
1568
- requestedCapability,
1569
- operationType,
1570
- mutationIntent,
1571
- riskLevel,
1572
- requiredEvidence,
1573
- safetyConstraints,
1574
- specPolicy: riskAssessment.specPolicy,
1575
- matchedSignals: riskAssessment.matchedSignals,
1576
- riskDrivers: riskAssessment.riskDrivers
1577
- };
1578
- }
1579
- extractCapability(text) {
1580
- let operationType = "explain";
1581
- let requestedCapability = void 0;
1582
- const capabilityMappings = [
1583
- { op: "discover", pattern: /\b(discover|inspect|map|scan|structure|architecture|repo|search|find)\b/i },
1584
- { op: "validate", pattern: /\b(validate|audit|review|check|verify|test)\b/i },
1585
- { op: "implement", pattern: /\b(implement|implementa|build|create|crie|cria|add|develop|write|change|modify|atualiza|atualizar|refatora|refatorar)\b/i },
1586
- { op: "fix", pattern: /\b(fix|repair|correct|corrige|corrigir|heal|solve|remediate)\b/i },
1587
- { op: "release", pattern: /\b(release|tag|prepare release|prepara(?:r)?\s+(?:a\s+)?release)\b/i },
1588
- { op: "deploy", pattern: /\b(deploy|publish|push|produção|producao)\b/i }
1589
- ];
1590
- for (const mapping of capabilityMappings) {
1591
- const match = text.match(mapping.pattern);
1592
- if (match) {
1593
- operationType = mapping.op;
1594
- requestedCapability = match[1];
1595
- break;
1596
- }
1597
- }
1598
- return { operationType, requestedCapability };
1599
- }
1600
- determineMutationIntent(text, operationType) {
1601
- const writePatterns = /\b(implement|implementa|build|fix|corrige|write|change|modify|create|crie|cria|add|refactor|refatora|refatorar|update|atualiza|atualizar)\b/i;
1602
- const readOnlyPatterns = /\b(analyze|check|inspect|review|find|search|show|list|read|view|verify|validate|discover|map)\b/i;
1603
- let mutationIntent = "write";
1604
- if (/\b(read-only|readonly)\b/i.test(text)) {
1605
- mutationIntent = "readonly";
1606
- } else if (writePatterns.test(text)) {
1607
- mutationIntent = "write";
1608
- } else if (readOnlyPatterns.test(text) || ["discover", "validate"].includes(operationType)) {
1609
- mutationIntent = "readonly";
1610
- }
1611
- if (/\b(publish|release|deploy|produção|producao|push to npm|push to remote)\b/i.test(text)) {
1612
- mutationIntent = "publish";
1613
- }
1614
- return mutationIntent;
1615
- }
1616
- determineRiskAssessment(text, mutationIntent) {
1617
- const matchedSignals = [];
1618
- const add = (signal, pattern) => {
1619
- if (pattern.test(text)) {
1620
- matchedSignals.push(signal);
1621
- return true;
1622
- }
1623
- return false;
1624
- };
1625
- const taskTypeSignals = {
1626
- docsCopy: add("task:docs-copy", /\b(README|docs?|documenta[cç][aã]o|typo|copy|frase|comment|coment[áa]rio)\b/i),
1627
- refactor: add("task:refactor", /\b(refactor|refatora|refatorar|simplificar|mais simples de manter|manuten[cç][aã]o)\b/i),
1628
- noBehaviorChange: add("scope:no-behavior-change", /\b(sem alterar (?:o )?comportamento|without changing behavior|without behaviour change|no behavior change|comportamento existente)\b/i),
1629
- landingPage: add("task:landing-page", /\b(landing page|hotsite|p[áa]gina de venda|p[áa]gina comercial)\b/i),
1630
- dashboard: add("task:dashboard", /\b(dashboard|painel)\b/i),
1631
- bugfix: add("task:bugfix", /\b(bug|fix|corrige|corrigir|erro|falha|n[aã]o envia|não envia)\b/i),
1632
- release: add("task:release", /\b(release|deploy|produ[cç][aã]o|publish|publicar|lan[cç]amento)\b/i)
1633
- };
1634
- const domainRiskSignals = {
1635
- adult: add("domain:adult", /\b(er[óo]tic[oa]s?|adult[oa]s?|sexual|sexo|porn(?:o|ografia)?|18\+)\b/i),
1636
- auth: add("domain:auth", /\b(login|auth|autentica[cç][aã]o|autoriza[cç][aã]o|permiss(?:ão|oes|ões)|admin|administrador|roles?|rbac)\b/i),
1637
- payment: add("domain:payment", /\b(pagamento|checkout|billing|cobran[cç]a|fatura[cç][aã]o|assinatura|cart[aã]o|stripe|paypal)\b/i),
1638
- securityGovernance: add("domain:security-governance", /\b(SECURITY\.md|seguran[cç]a|security policy|pol[ií]tica de seguran[cç]a|governance|governan[cç]a|compliance|branch safety)\b/i)
1639
- };
1640
- const impactSignals = {
1641
- personalData: add("impact:personal-data", /\b(leads?|dados pessoais|personal data|crm|contatos?|contactos?|clientes?|exporta[cç][aã]o|exportar|armazenamento|armazenar|integra[cç][aã]o|integrar)\b/i),
1642
- productionRelease: mutationIntent === "publish" || add("impact:production-release", /\b(produ[cç][aã]o|release|deploy|publish|publicar|npm)\b/i),
1643
- adminPermissions: add("impact:admin-permissions", /\b(admin|administrador|permiss(?:ão|oes|ões)|roles?|rbac)\b/i)
1644
- };
1645
- if (mutationIntent === "publish" && !matchedSignals.includes("impact:production-release")) {
1646
- matchedSignals.push("impact:production-release");
1647
- }
1648
- const scopeSignals = {
1649
- localized: add("scope:localized", /\b(localizado|uma frase|typo|pequeno|small|tiny|simples|README)\b/i),
1650
- broad: add("scope:broad", /\b(plataforma|sistema|completo|premium|end-to-end|amplo|v[aá]rias|m[úu]ltiplas|empresa)\b/i),
1651
- architectural: add("scope:architectural", /\b(deep|architectural|arquitetural|migration|migra[cç][aã]o|major|full|\[deep\])\b/i),
1652
- explicitHigh: add("scope:explicit-high", /\b(high-risk|high risk|alto risco)\b/i),
1653
- explicitRequiredSpec: add("scope:explicit-required-spec", /\b(formal spec|required spec|especifica[cç][aã]o formal)\b/i),
1654
- explicitRequiredEvidence: add("scope:explicit-required-evidence", /\b(required evidence|evidence required|evid[eê]ncia obrigat[óo]ria)\b/i)
1655
- };
1656
- let riskLevel = "medium";
1657
- const riskDrivers = [];
1658
- const highDrivers = [
1659
- ["scope:explicit-high", scopeSignals.explicitHigh],
1660
- ["scope:architectural", scopeSignals.architectural],
1661
- ["domain:adult", domainRiskSignals.adult],
1662
- ["domain:auth", domainRiskSignals.auth],
1663
- ["domain:payment", domainRiskSignals.payment],
1664
- ["domain:security-governance", domainRiskSignals.securityGovernance],
1665
- ["impact:production-release", impactSignals.productionRelease],
1666
- ["impact:admin-permissions", impactSignals.adminPermissions],
1667
- ["impact:personal-data", taskTypeSignals.dashboard && impactSignals.personalData]
1668
- ];
1669
- for (const [signal, active] of highDrivers) {
1670
- if (active) {
1671
- riskDrivers.push(signal);
1672
- }
1673
- }
1674
- if (riskDrivers.length > 0) {
1675
- riskLevel = "high";
1676
- } else if (mutationIntent === "readonly") {
1677
- riskLevel = "low";
1678
- riskDrivers.push("intent:readonly");
1679
- } else if (taskTypeSignals.docsCopy && scopeSignals.localized) {
1680
- riskLevel = "low";
1681
- riskDrivers.push("task:docs-copy");
1682
- } else if (taskTypeSignals.refactor || taskTypeSignals.bugfix || taskTypeSignals.landingPage || taskTypeSignals.dashboard) {
1683
- riskLevel = "medium";
1684
- if (taskTypeSignals.refactor) riskDrivers.push("task:refactor");
1685
- if (taskTypeSignals.bugfix) riskDrivers.push("task:bugfix");
1686
- if (taskTypeSignals.landingPage) riskDrivers.push("task:landing-page");
1687
- if (taskTypeSignals.dashboard) riskDrivers.push("task:dashboard");
1688
- }
1689
- const specPolicy = riskLevel === "high" || scopeSignals.explicitRequiredSpec || taskTypeSignals.landingPage && scopeSignals.broad ? "required" : "none";
1690
- return {
1691
- riskLevel,
1692
- specPolicy,
1693
- matchedSignals: [...new Set(matchedSignals)],
1694
- riskDrivers: [...new Set(riskDrivers)]
1695
- };
1696
- }
1697
- determineSafetyAndEvidence(text, mutationIntent) {
1698
- const safetyConstraints = [];
1699
- const requiredEvidence = [];
1700
- if (mutationIntent === "publish") {
1701
- safetyConstraints.push("Require release gate", "Awaiting user authorization");
1702
- requiredEvidence.push("release-decision", "tarball-hash");
1703
- } else if (mutationIntent === "write") {
1704
- safetyConstraints.push("Require branch gate", "No direct commits to main");
1705
- requiredEvidence.push("tests", "commit-hash");
1706
- } else {
1707
- safetyConstraints.push("No workspace mutations allowed");
1708
- }
1709
- if (/\b(bypass|force|direct)\b/i.test(text)) {
1710
- safetyConstraints.push("Block bypass attempts");
1711
- }
1712
- if (/\b(required evidence|evidence required)\b/i.test(text)) {
1713
- requiredEvidence.push("user-required-evidence");
1714
- }
1715
- if (/\b(formal spec|required spec)\b/i.test(text)) {
1716
- safetyConstraints.push("Require formal specification");
1717
- }
1718
- return { safetyConstraints, requiredEvidence };
1719
- }
1720
- /**
1721
- * Decides the routing decision.
1722
- */
1723
- route(understanding) {
1724
- const { operationType, mutationIntent, rawRequest } = understanding;
1725
- const requestedActor = void 0;
1726
- if (/\b(bypass safety|bypass validation|force push|directly to main|push to remote without gate)\b/i.test(rawRequest)) {
1727
- return {
1728
- requestedActor,
1729
- selectedActor: "Atlas",
1730
- operationType,
1731
- mutationIntent,
1732
- permissionDecision: "blocked",
1733
- reason: "Security block: Attempt to bypass safety or push directly is forbidden.",
1734
- workflowPath: ["Atlas"]
1735
- };
1736
- }
1737
- let { selectedActor, permissionDecision, reason } = this.evaluateActorPermission(operationType, mutationIntent);
1738
- let workflowPath = ["Atlas"];
1739
- if (selectedActor === "Phoenix") {
1740
- const requestPath = path6.join(this.cwd, ".ai-workflow/remediation-request.json");
1741
- let hasFindings = false;
1742
- if (fs7.existsSync(requestPath)) {
1743
- try {
1744
- const reqData = JSON.parse(fs7.readFileSync(requestPath, "utf8"));
1745
- if (reqData.affectedChecks && reqData.affectedChecks.length > 0) {
1746
- hasFindings = true;
1747
- }
1748
- } catch {
1749
- }
1750
- }
1751
- if (!hasFindings) {
1752
- permissionDecision = "blocked";
1753
- reason = "Blocked: Phoenix acts only with concrete findings and bounded remediation.";
1754
- }
1755
- }
1756
- if (understanding.specPolicy === "required") {
1757
- workflowPath.push("Nexus", "Orion");
1758
- }
1759
- if (selectedActor && !workflowPath.includes(selectedActor)) {
1760
- workflowPath.push(selectedActor);
1761
- }
1762
- if (selectedActor === "Atlas" && ["discover", "validate", "fix", "release", "deploy"].includes(operationType)) {
1763
- permissionDecision = "blocked";
1764
- reason = "Atlas self-execution guard: Atlas cannot silently execute specialized actor or capability requests.";
1765
- }
1766
- if (selectedActor === "Astra" && understanding.riskLevel === "high") {
1767
- workflowPath.push("Sage", "Phoenix", "Sage");
1768
- }
1769
- if (understanding.specPolicy === "none") {
1770
- workflowPath = workflowPath.filter((actor) => {
1771
- if (actor === "Nexus" && selectedActor !== "Nexus") return false;
1772
- if (actor === "Orion" && selectedActor !== "Orion") return false;
1773
- return true;
1774
- });
1775
- }
1776
- return {
1777
- requestedActor,
1778
- selectedActor,
1779
- operationType,
1780
- mutationIntent,
1781
- permissionDecision,
1782
- reason,
1783
- workflowPath
1784
- };
1785
- }
1786
- evaluateActorPermission(operationType, mutationIntent) {
1787
- let selectedActor;
1788
- let permissionDecision = "allowed";
1789
- let reason = "Routed successfully.";
1790
- if (operationType === "discover") {
1791
- selectedActor = "Nexus";
1792
- } else if (operationType === "validate") {
1793
- selectedActor = "Sage";
1794
- } else if (operationType === "fix") {
1795
- selectedActor = "Phoenix";
1796
- } else if (operationType === "implement") {
1797
- selectedActor = "Astra";
1798
- } else if (operationType === "release" || operationType === "deploy") {
1799
- selectedActor = "Orion";
1800
- } else {
1801
- selectedActor = mutationIntent === "write" ? "Astra" : "Atlas";
1802
- }
1803
- return { selectedActor, permissionDecision, reason };
1804
- }
1805
- };
1806
-
1807
1772
  // src/core/request-classifier.ts
1808
1773
  var RequestClassifier = class {
1809
1774
  /**
@@ -1817,12 +1782,10 @@ var RequestClassifier = class {
1817
1782
  const cwd = options.cwd || process.cwd();
1818
1783
  const profile = resolveWorkflowProfile({ request: text });
1819
1784
  const profileDef = getWorkflowProfile(profile);
1820
- const readOnlyPatterns = /\b(analyze|check|inspect|review|find|search|show|list|read|view|verify)\b/i;
1821
- const isReadOnly = readOnlyPatterns.test(text);
1822
- const intent = isReadOnly ? "read-only" : "write";
1823
1785
  const router = new RequestRouter({ cwd });
1824
1786
  const requestUnderstanding = router.understand(text);
1825
1787
  const routingDecision = router.route(requestUnderstanding);
1788
+ const intent = requestUnderstanding.mutationIntent === "readonly" || requestUnderstanding.deliveryMode === "answer-only" ? "read-only" : "write";
1826
1789
  const risk = requestUnderstanding.riskLevel;
1827
1790
  const explicitRequiredEvidence = /\b(required evidence|evidence required)\b/i.test(text);
1828
1791
  const explicitRequiredSpec = /\b(formal spec|required spec)\b/i.test(text);
@@ -1846,7 +1809,7 @@ var RequestClassifier = class {
1846
1809
  request: text,
1847
1810
  profile,
1848
1811
  owner: routingDecision.selectedActor || profileDef.owner,
1849
- intent: requestUnderstanding.mutationIntent === "readonly" ? "read-only" : "write",
1812
+ intent,
1850
1813
  mode: legacyMode,
1851
1814
  risk,
1852
1815
  specPolicy,
@@ -1864,7 +1827,7 @@ var RequestClassifier = class {
1864
1827
  };
1865
1828
 
1866
1829
  // src/core/execution-planner.ts
1867
- import path7 from "path";
1830
+ import path6 from "path";
1868
1831
  var ExecutionPlanner = class {
1869
1832
  cwd;
1870
1833
  constructor({ cwd = process.cwd() } = {}) {
@@ -1876,8 +1839,8 @@ var ExecutionPlanner = class {
1876
1839
  plan(classification, taskSlug = "task") {
1877
1840
  const remediationLimit = classification.remediationLimit;
1878
1841
  const branchNeeded = classification.intent === "write";
1879
- const specPath = classification.specNeeded ? path7.join("docs/workflows", taskSlug, "spec.md") : null;
1880
- const owner = classification.routingDecision?.selectedActor || (classification.intent === "write" ? "Astra" : classification.owner);
1842
+ const specPath = classification.specNeeded ? path6.join("docs/workflows", taskSlug, "spec.md") : null;
1843
+ const owner = classification.routingDecision?.mutationOwner === "Astra" ? "Astra" : classification.routingDecision?.selectedActor || (classification.intent === "write" ? "Astra" : classification.owner);
1881
1844
  const validationsExpected = [];
1882
1845
  if (classification.validationNeeded) {
1883
1846
  validationsExpected.push("test");
@@ -1981,4 +1944,4 @@ export {
1981
1944
  ExecutionPlanner,
1982
1945
  WorkflowStateMachine
1983
1946
  };
1984
- //# sourceMappingURL=chunk-LI76KI7C.js.map
1947
+ //# sourceMappingURL=chunk-4FI5ODAM.js.map