sentinelayer-cli 0.8.11 → 0.9.0

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.
Files changed (38) hide show
  1. package/package.json +10 -5
  2. package/src/agents/devtestbot/config/definition.js +100 -0
  3. package/src/agents/devtestbot/config/system-prompt.js +92 -0
  4. package/src/agents/devtestbot/index.js +9 -0
  5. package/src/agents/devtestbot/runner.js +769 -0
  6. package/src/agents/devtestbot/tool.js +707 -0
  7. package/src/agents/jules/stream.js +2 -12
  8. package/src/audit/orchestrator.js +471 -114
  9. package/src/audit/persona-loop.js +1342 -0
  10. package/src/audit/registry.js +58 -2
  11. package/src/commands/audit.js +42 -1
  12. package/src/commands/legacy-args.js +32 -1
  13. package/src/commands/omargate.js +4 -0
  14. package/src/commands/session.js +417 -89
  15. package/src/commands/swarm.js +11 -2
  16. package/src/cost/history.js +41 -21
  17. package/src/events/schema.js +27 -1
  18. package/src/guide/generator.js +14 -0
  19. package/src/legacy-cli.js +110 -18
  20. package/src/prompt/generator.js +4 -16
  21. package/src/review/ai-review.js +95 -6
  22. package/src/review/dd-report-email-client.js +148 -0
  23. package/src/review/investor-dd-devtestbot.js +599 -0
  24. package/src/review/investor-dd-orchestrator.js +135 -3
  25. package/src/review/omargate-cache.js +285 -0
  26. package/src/review/omargate-orchestrator.js +605 -4
  27. package/src/review/persona-prompts.js +34 -1
  28. package/src/review/report.js +189 -4
  29. package/src/session/coordination-guidance.js +48 -0
  30. package/src/session/daemon.js +3 -2
  31. package/src/session/listener.js +236 -0
  32. package/src/session/senti-naming.js +36 -0
  33. package/src/session/setup-guides.js +3 -15
  34. package/src/session/store.js +54 -5
  35. package/src/session/sync.js +23 -0
  36. package/src/spec/generator.js +8 -10
  37. package/src/swarm/registry.js +20 -0
  38. package/src/swarm/runtime.js +139 -1
@@ -7,6 +7,7 @@ import { rollupUsage } from "./tracker.js";
7
7
 
8
8
  const HISTORY_VERSION = 1;
9
9
  const HISTORY_FILE_NAME = "cost-history.json";
10
+ const costHistoryWriteQueues = new Map();
10
11
 
11
12
  function normalizeNumber(value, field) {
12
13
  const normalized = Number(value || 0);
@@ -76,6 +77,14 @@ export async function resolveCostHistoryPath({
76
77
 
77
78
  export async function loadCostHistory(options = {}) {
78
79
  const filePath = await resolveCostHistoryPath(options);
80
+ const history = await readCostHistoryFile(filePath);
81
+ return {
82
+ filePath,
83
+ history,
84
+ };
85
+ }
86
+
87
+ async function readCostHistoryFile(filePath) {
79
88
  try {
80
89
  const raw = await fsp.readFile(filePath, "utf-8");
81
90
  const parsed = JSON.parse(raw);
@@ -83,20 +92,14 @@ export async function loadCostHistory(options = {}) {
83
92
  throw new Error("Invalid cost history payload.");
84
93
  }
85
94
  return {
86
- filePath,
87
- history: {
88
- version: Number(parsed.version || HISTORY_VERSION),
89
- entries: parsed.entries,
90
- },
95
+ version: Number(parsed.version || HISTORY_VERSION),
96
+ entries: parsed.entries,
91
97
  };
92
98
  } catch (error) {
93
99
  if (error && typeof error === "object" && error.code === "ENOENT") {
94
100
  return {
95
- filePath,
96
- history: {
97
- version: HISTORY_VERSION,
98
- entries: [],
99
- },
101
+ version: HISTORY_VERSION,
102
+ entries: [],
100
103
  };
101
104
  }
102
105
  throw error;
@@ -114,17 +117,34 @@ export async function saveCostHistory({ filePath, history }) {
114
117
 
115
118
  export async function appendCostEntry(options = {}, entry = {}) {
116
119
  const normalizedEntry = normalizeEntry(entry);
117
- const { filePath, history } = await loadCostHistory(options);
118
- const nextHistory = {
119
- version: HISTORY_VERSION,
120
- entries: [...history.entries, normalizedEntry],
121
- };
122
- await saveCostHistory({ filePath, history: nextHistory });
123
- return {
124
- filePath,
125
- entry: normalizedEntry,
126
- history: nextHistory,
127
- };
120
+ const filePath = await resolveCostHistoryPath(options);
121
+ return withCostHistoryWriteQueue(filePath, async () => {
122
+ const history = await readCostHistoryFile(filePath);
123
+ const nextHistory = {
124
+ version: HISTORY_VERSION,
125
+ entries: [...history.entries, normalizedEntry],
126
+ };
127
+ await saveCostHistory({ filePath, history: nextHistory });
128
+ return {
129
+ filePath,
130
+ entry: normalizedEntry,
131
+ history: nextHistory,
132
+ };
133
+ });
134
+ }
135
+
136
+ async function withCostHistoryWriteQueue(filePath, fn) {
137
+ const previous = costHistoryWriteQueues.get(filePath) || Promise.resolve();
138
+ const next = previous.catch(() => {}).then(fn);
139
+ const queued = next.catch(() => {});
140
+ costHistoryWriteQueues.set(filePath, queued);
141
+ try {
142
+ return await next;
143
+ } finally {
144
+ if (costHistoryWriteQueues.get(filePath) === queued) {
145
+ costHistoryWriteQueues.delete(filePath);
146
+ }
147
+ }
128
148
  }
129
149
 
130
150
  function summarizeSessionEntries(entries) {
@@ -1,5 +1,31 @@
1
1
  const AGENT_EVENT_STREAM = "sl_event";
2
2
  const LEGACY_AGENT_ID = "legacy-emitter";
3
+ const AGENT_EVENT_TYPES = Object.freeze([
4
+ "agent_start",
5
+ "agent_complete",
6
+ "agent_abort",
7
+ "agent_error",
8
+ "progress",
9
+ "heartbeat",
10
+ "tool_call",
11
+ "tool_result",
12
+ "finding",
13
+ "reasoning",
14
+ "budget_warning",
15
+ "budget_stop",
16
+ "swarm_start",
17
+ "swarm_complete",
18
+ "phase_start",
19
+ "phase_complete",
20
+ "orchestrator_start",
21
+ "dispatch",
22
+ "reconcile_start",
23
+ "reconcile_complete",
24
+ "orchestrator_complete",
25
+ "convergence_expansion",
26
+ "coverage_gap",
27
+ "llm_error",
28
+ ]);
3
29
 
4
30
  function isPlainObject(value) {
5
31
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
@@ -187,4 +213,4 @@ export function validateAgentEvent(evt, options = {}) {
187
213
  return Boolean(normalizeAgentEvent(evt, options));
188
214
  }
189
215
 
190
- export { AGENT_EVENT_STREAM };
216
+ export { AGENT_EVENT_STREAM, AGENT_EVENT_TYPES };
@@ -1,3 +1,9 @@
1
+ import {
2
+ getCoordinationEtiquetteItems,
3
+ renderCoordinationMarkdownSection,
4
+ renderCoordinationTicketBlock,
5
+ } from "../session/coordination-guidance.js";
6
+
1
7
  export const SUPPORTED_GUIDE_EXPORT_FORMATS = Object.freeze([
2
8
  "jira",
3
9
  "linear",
@@ -167,6 +173,8 @@ function buildTicket(phase, index) {
167
173
  "",
168
174
  "Acceptance criteria:",
169
175
  acceptanceBlock || "1. Phase outcomes are verified by deterministic checks.",
176
+ "",
177
+ renderCoordinationTicketBlock(),
170
178
  ].join("\n"),
171
179
  };
172
180
  }
@@ -235,6 +243,8 @@ ${goal}
235
243
  ## Phase Execution Plan
236
244
  ${phaseMarkdown}
237
245
 
246
+ ${renderCoordinationMarkdownSection()}
247
+
238
248
  ## Suggested PR Sequence
239
249
  ${resolvedPhases
240
250
  .map((phase, index) => `${index + 1}. ${phase.title} (${phase.effort.label})`)
@@ -246,6 +256,7 @@ ${resolvedPhases
246
256
  goal,
247
257
  phases: resolvedPhases,
248
258
  tickets,
259
+ coordinationRules: getCoordinationEtiquetteItems(),
249
260
  markdown,
250
261
  };
251
262
  }
@@ -256,12 +267,14 @@ export function renderGuideExport({ format, guide }) {
256
267
  project: guide.projectName,
257
268
  generated_at: new Date().toISOString(),
258
269
  issues: guide.tickets,
270
+ coordination_rules: Array.isArray(guide.coordinationRules) ? guide.coordinationRules : [],
259
271
  };
260
272
 
261
273
  if (normalized === "jira") {
262
274
  return JSON.stringify(
263
275
  {
264
276
  format: "jira",
277
+ coordination_rules: payload.coordination_rules,
265
278
  issues: payload.issues.map((issue) => ({
266
279
  summary: issue.title,
267
280
  description: issue.description,
@@ -279,6 +292,7 @@ export function renderGuideExport({ format, guide }) {
279
292
  return JSON.stringify(
280
293
  {
281
294
  format: "linear",
295
+ coordination_rules: payload.coordination_rules,
282
296
  issues: payload.issues.map((issue, index) => ({
283
297
  title: issue.title,
284
298
  description: issue.description,
package/src/legacy-cli.js CHANGED
@@ -25,6 +25,10 @@ import { normalizeAgentEvent } from "./events/schema.js";
25
25
  import { collectCodebaseIngest, formatIngestSummary } from "./ingest/engine.js";
26
26
  import { getExpressTemplate, getPackageJsonTemplate, buildReadmeContent } from "./scaffold/templates.js";
27
27
  import { generateScaffold } from "./scaffold/generator.js";
28
+ import {
29
+ getCoordinationEtiquetteItems,
30
+ renderCoordinationNumberedList,
31
+ } from "./session/coordination-guidance.js";
28
32
 
29
33
  let DEFAULT_API_URL = process.env.SENTINELAYER_API_URL || "https://api.sentinelayer.com";
30
34
  let DEFAULT_WEB_URL = process.env.SENTINELAYER_WEB_URL || "https://sentinelayer.com";
@@ -1121,6 +1125,10 @@ async function runLocalOmarGateCommand(args) {
1121
1125
  const maxParallel =
1122
1126
  parseInt(getCommandOptionValue(args, "--max-parallel") || "3", 10) || 3;
1123
1127
  const streamEnabled = hasCommandOption(args, "--stream");
1128
+ const devTestBotEnabled = !hasCommandOption(args, "--no-devtestbot");
1129
+ const devTestBotBaseUrl = getCommandOptionValue(args, "--devtestbot-base-url") || "";
1130
+ const devTestBotScope = getCommandOptionValue(args, "--devtestbot-scope") || "";
1131
+ const emailOnComplete = getCommandOptionValue(args, "--email-on-complete") || "";
1124
1132
 
1125
1133
  const targetPath = path.resolve(process.cwd(), pathArg);
1126
1134
  if (!fs.existsSync(targetPath) || !fs.statSync(targetPath).isDirectory()) {
@@ -1128,6 +1136,9 @@ async function runLocalOmarGateCommand(args) {
1128
1136
  }
1129
1137
 
1130
1138
  const { runInvestorDd } = await import("./review/investor-dd-orchestrator.js");
1139
+ const reportEmailClient = emailOnComplete
1140
+ ? await import("./review/dd-report-email-client.js")
1141
+ : null;
1131
1142
  if (!asJson) {
1132
1143
  printSection("Investor-DD Audit");
1133
1144
  printInfo(`Target: ${targetPath}`);
@@ -1141,6 +1152,24 @@ async function runLocalOmarGateCommand(args) {
1141
1152
  outputDir: outputDirArg,
1142
1153
  budgetOptions: { maxCostUsd, maxRuntimeMinutes, maxParallel },
1143
1154
  dryRun,
1155
+ devTestBot: {
1156
+ enabled: devTestBotEnabled,
1157
+ baseUrl: devTestBotBaseUrl,
1158
+ scope: devTestBotScope,
1159
+ },
1160
+ reportEmail: emailOnComplete
1161
+ ? {
1162
+ to: emailOnComplete,
1163
+ client: {
1164
+ send: ({ runId, to }) => reportEmailClient.sendDdReportEmail({
1165
+ runId,
1166
+ to,
1167
+ cwd: targetPath,
1168
+ env: process.env,
1169
+ }),
1170
+ },
1171
+ }
1172
+ : null,
1144
1173
  onEvent: streamEnabled
1145
1174
  ? (event) => process.stdout.write(`${JSON.stringify(event)}\n`)
1146
1175
  : () => {},
@@ -1237,7 +1266,10 @@ async function runLocalOmarGateCommand(args) {
1237
1266
  deterministic: {
1238
1267
  summary: detSummary,
1239
1268
  findings: detFindings,
1269
+ scope: deterministic.scope || {},
1270
+ layers: deterministic.layers || {},
1240
1271
  metadata: deterministic.metadata || {},
1272
+ artifacts: deterministic.artifacts || {},
1241
1273
  },
1242
1274
  onEvent: streamHandler,
1243
1275
  includeOnly: includeOnly.length > 0 ? includeOnly : null,
@@ -1331,6 +1363,7 @@ async function runLocalOmarGateCommand(args) {
1331
1363
  const combinedP1 = combinedSummary.P1 || 0;
1332
1364
  const combinedP2 = combinedSummary.P2 || 0;
1333
1365
  const combinedP3 = combinedSummary.P3 || 0;
1366
+ const omargateRunId = orchestratorResult?.runId || deterministic.runId;
1334
1367
 
1335
1368
  // Write per-phase artifacts alongside REVIEW_DETERMINISTIC so post-mortems
1336
1369
  // can inspect exactly what each layer contributed.
@@ -1376,6 +1409,7 @@ async function runLocalOmarGateCommand(args) {
1376
1409
  const report = `# Local Omar Gate Deep Scan
1377
1410
 
1378
1411
  Generated: ${nowIso()}
1412
+ Run ID: ${omargateRunId}
1379
1413
  Target: ${targetPath}
1380
1414
  Elapsed: ${totalElapsed}
1381
1415
 
@@ -1395,12 +1429,24 @@ ${formatFindingsMarkdown(allFindings)}
1395
1429
  const reportPath = await writeLocalCommandReport(targetPath, "omargate-deep", report, {
1396
1430
  outputDir: outputDirArg,
1397
1431
  });
1432
+ const { writeOmarGateDeterministicCache } = await import("./review/omargate-cache.js");
1433
+ const deterministicCache = await writeOmarGateDeterministicCache({
1434
+ targetPath,
1435
+ outputDir: outputDirArg,
1436
+ runId: omargateRunId,
1437
+ deterministic,
1438
+ reportPath,
1439
+ });
1440
+ artifactPaths.deterministicCache = deterministicCache.artifactPath;
1441
+ artifactPaths.latestOmarGate = deterministicCache.latestPath;
1442
+
1398
1443
  if (asJson) {
1399
1444
  console.log(
1400
1445
  JSON.stringify(
1401
1446
  {
1402
1447
  command: "/omargate deep",
1403
1448
  targetPath,
1449
+ runId: omargateRunId,
1404
1450
  reportPath,
1405
1451
  scannedFiles,
1406
1452
  p0: combinedP0,
@@ -1457,6 +1503,7 @@ async function runLocalAuditCommand(args) {
1457
1503
  const asJson = hasCommandOption(args, "--json");
1458
1504
  const pathArg = getCommandOptionValue(args, "--path") || ".";
1459
1505
  const outputDirArg = getCommandOptionValue(args, "--output-dir") || "";
1506
+ const reuseOmarGate = getCommandOptionValue(args, "--reuse-omargate") || "";
1460
1507
  const targetPath = path.resolve(process.cwd(), pathArg);
1461
1508
  if (!fs.existsSync(targetPath) || !fs.statSync(targetPath).isDirectory()) {
1462
1509
  throw new Error(`Invalid --path target: ${targetPath}`);
@@ -1467,6 +1514,16 @@ async function runLocalAuditCommand(args) {
1467
1514
  printInfo(`Target: ${targetPath}`);
1468
1515
  }
1469
1516
 
1517
+ const buildScanFromOmarGateCache = (cache) => {
1518
+ const findings = Array.isArray(cache?.findings) ? cache.findings : [];
1519
+ return {
1520
+ scannedFiles: Number(cache?.scope?.scannedFiles || findings.length || 0),
1521
+ findings,
1522
+ p1: findings.filter((item) => item.severity === "P1").length,
1523
+ p2: findings.filter((item) => item.severity === "P2").length,
1524
+ };
1525
+ };
1526
+
1470
1527
  const requiredChecks = [
1471
1528
  {
1472
1529
  key: ".github/workflows/omar-gate.yml",
@@ -1488,7 +1545,44 @@ async function runLocalAuditCommand(args) {
1488
1545
  },
1489
1546
  ];
1490
1547
 
1491
- const scan = await runCredentialScan(targetPath);
1548
+ let omargateReuse = {
1549
+ requested: reuseOmarGate || "",
1550
+ used: false,
1551
+ runId: "",
1552
+ artifactPath: "",
1553
+ reason: reuseOmarGate ? "not_found" : "not_requested",
1554
+ };
1555
+ let scan = null;
1556
+ if (reuseOmarGate) {
1557
+ const { loadOmarGateDeterministicCache } = await import("./review/omargate-cache.js");
1558
+ const reused = await loadOmarGateDeterministicCache({
1559
+ targetPath,
1560
+ outputDir: outputDirArg,
1561
+ runIdOrLatest: reuseOmarGate,
1562
+ });
1563
+ if (reused.found) {
1564
+ scan = buildScanFromOmarGateCache(reused.cache);
1565
+ omargateReuse = {
1566
+ requested: reuseOmarGate,
1567
+ used: true,
1568
+ runId: reused.runId,
1569
+ deterministicRunId: reused.cache?.deterministicRunId || "",
1570
+ artifactPath: reused.artifactPath,
1571
+ reason: "",
1572
+ };
1573
+ } else {
1574
+ omargateReuse = {
1575
+ requested: reuseOmarGate,
1576
+ used: false,
1577
+ runId: "",
1578
+ artifactPath: "",
1579
+ reason: reused.reason || "not_found",
1580
+ };
1581
+ }
1582
+ }
1583
+ if (!scan) {
1584
+ scan = await runCredentialScan(targetPath);
1585
+ }
1492
1586
  const failedP1Checks = requiredChecks.filter((item) => !item.ok && item.severity === "P1").length;
1493
1587
  const failedP2Checks = requiredChecks.filter((item) => !item.ok && item.severity === "P2").length;
1494
1588
  const totalP1 = scan.p1 + failedP1Checks;
@@ -1506,11 +1600,13 @@ async function runLocalAuditCommand(args) {
1506
1600
  Generated: ${nowIso()}
1507
1601
  Target: ${targetPath}
1508
1602
  Overall status: ${overallStatus}
1603
+ OmarGate reuse: ${omargateReuse.used ? `yes (${omargateReuse.runId})` : omargateReuse.requested ? `requested ${omargateReuse.requested} (${omargateReuse.reason})` : "no"}
1509
1604
 
1510
1605
  Readiness checks:
1511
1606
  ${checkText}
1512
1607
 
1513
1608
  Scan summary:
1609
+ - Reused OmarGate run: ${omargateReuse.used ? omargateReuse.runId : "n/a"}
1514
1610
  - Files scanned: ${scan.scannedFiles}
1515
1611
  - P1 findings: ${scan.p1}
1516
1612
  - P2 findings: ${scan.p2}
@@ -1536,6 +1632,9 @@ ${formatFindingsMarkdown(scan.findings)}
1536
1632
  p1Total: totalP1,
1537
1633
  p2Total: totalP2,
1538
1634
  blocking: totalP1 > 0,
1635
+ omargateReuse,
1636
+ reusedOmarGateRunId: omargateReuse.used ? omargateReuse.runId : "",
1637
+ reusedOmarGateDeterministicPath: omargateReuse.used ? omargateReuse.artifactPath : "",
1539
1638
  },
1540
1639
  null,
1541
1640
  2
@@ -1543,6 +1642,11 @@ ${formatFindingsMarkdown(scan.findings)}
1543
1642
  );
1544
1643
  } else {
1545
1644
  console.log(pc.cyan(`Report: ${reportPath}`));
1645
+ if (omargateReuse.used) {
1646
+ console.log(pc.gray(`Reused OmarGate run: ${omargateReuse.runId}`));
1647
+ } else if (omargateReuse.requested) {
1648
+ console.log(pc.gray(`OmarGate reuse unavailable: ${omargateReuse.reason}`));
1649
+ }
1546
1650
  console.log(`Overall status: ${overallStatus}`);
1547
1651
  console.log(`P1 total: ${totalP1}`);
1548
1652
  console.log(`P2 total: ${totalP2}`);
@@ -1875,6 +1979,7 @@ function buildCodingAgentConfigTemplate({ agentProfile, projectName }) {
1875
1979
  "Read docs/spec.md, docs/build-guide.md, tasks/todo.md, and AGENT_HANDOFF_PROMPT.md in order.",
1876
1980
  "Work one PR scope at a time and keep changes deterministic.",
1877
1981
  "Run local checks before push: /omargate deep and /audit.",
1982
+ ...getCoordinationEtiquetteItems(),
1878
1983
  ];
1879
1984
 
1880
1985
  if (agentProfile.id === "aider") {
@@ -1885,7 +1990,7 @@ read:
1885
1990
  - tasks/todo.md
1886
1991
  - AGENT_HANDOFF_PROMPT.md
1887
1992
  notes:
1888
- - ${commonChecklist.join("\n - ")}
1993
+ - ${commonChecklist.map((item) => JSON.stringify(item)).join("\n - ")}
1889
1994
  `;
1890
1995
  }
1891
1996
 
@@ -2111,16 +2216,7 @@ Repo context:
2111
2216
 
2112
2217
  ## Multi-Agent Coordination (if session active)
2113
2218
 
2114
- If a SentinelLayer session is active (check \`sl session list\`):
2115
-
2116
- 1. **On start:** Run \`sl session join <id> --name <your-name> --role coder\`
2117
- 2. **Periodically:** Run \`sl session say <id> "status: working on <file>"\` so others know what you're doing
2118
- 3. **On unexpected file change:** Instead of stopping, run:
2119
- \`sl session say <id> "help: unexpected change in <file> - who modified this?"\`
2120
- Then check \`sl session read <id> --tail 5\` for the answer and continue.
2121
- 4. **On findings:** Run \`sl session say <id> "finding: [P2] <title> in <file>:<line>"\`
2122
- 5. **On completion:** Run \`sl session leave <id>\`
2123
- 6. **Before modifying a file:** Check \`sl session read <id> --tail 10\` for recent activity on that file
2219
+ ${renderCoordinationNumberedList()}
2124
2220
 
2125
2221
  Start now and continue autonomously.
2126
2222
  `;
@@ -2129,12 +2225,8 @@ Start now and continue autonomously.
2129
2225
  export function buildAgentsSessionGuideContent() {
2130
2226
  return `# SentinelLayer Session Guide for AI Agents
2131
2227
 
2132
- ## Quick Start
2133
- 1. Check: \`sl session list\` - is there an active session?
2134
- 2. Join: \`sl session join <id> --name <your-short-name> --role <coder|reviewer|tester>\`
2135
- 3. Read context: \`sl session read <id> --tail 20\` — see what others are doing
2136
- 4. Work: emit status every 5 min, post findings, ask for help instead of stopping
2137
- 5. Leave: \`sl session leave <id>\` when done
2228
+ ## Required Etiquette
2229
+ ${renderCoordinationNumberedList()}
2138
2230
 
2139
2231
  ## Why This Matters
2140
2232
  - Other agents can see what you're working on and avoid file conflicts
@@ -1,3 +1,5 @@
1
+ import { getCoordinationEtiquetteItems } from "../session/coordination-guidance.js";
2
+
1
3
  export const SUPPORTED_PROMPT_TARGETS = Object.freeze([
2
4
  "claude",
3
5
  "cursor",
@@ -34,11 +36,7 @@ const TARGET_GUIDANCE = Object.freeze({
34
36
  ],
35
37
  });
36
38
 
37
- const SESSION_COORDINATION_GUIDANCE = Object.freeze([
38
- "Multi-agent coordination: use `sl session` commands to communicate with other agents.",
39
- "Always update the session chat room with your current activity so joining agents have context.",
40
- "Never break your autonomous loop on unexpected file changes; ask in the session first.",
41
- ]);
39
+ const SESSION_COORDINATION_GUIDANCE = Object.freeze(getCoordinationEtiquetteItems());
42
40
 
43
41
  function normalizeTarget(target) {
44
42
  const normalized = String(target || "generic").trim().toLowerCase();
@@ -61,14 +59,6 @@ function buildAgentHeader(target) {
61
59
  return headers[target] || headers.generic;
62
60
  }
63
61
 
64
- function shouldAppendSessionGuidance(specMarkdown) {
65
- const normalized = String(specMarkdown || "").toLowerCase();
66
- if (!normalized) {
67
- return false;
68
- }
69
- return normalized.includes("coordination protocol") || normalized.includes("session");
70
- }
71
-
72
62
  export function resolvePromptTarget(target) {
73
63
  return normalizeTarget(target);
74
64
  }
@@ -96,9 +86,7 @@ export function generateExecutionPrompt({
96
86
  }
97
87
 
98
88
  const operatingRules = [...guidance];
99
- if (shouldAppendSessionGuidance(specText)) {
100
- operatingRules.push(...SESSION_COORDINATION_GUIDANCE);
101
- }
89
+ operatingRules.push(...SESSION_COORDINATION_GUIDANCE);
102
90
  const guidanceMarkdown = operatingRules.map((item, index) => `${index + 1}. ${item}`).join("\n");
103
91
 
104
92
  const hasAidenId = specText.toLowerCase().includes("aidenid");