@cleocode/cleo 2026.4.15 → 2026.4.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -9205,6 +9205,9 @@ var init_brain_schema = __esm({
9205
9205
  sourceSessionId: text("source_session_id"),
9206
9206
  // soft FK to sessions
9207
9207
  sourceType: text("source_type", { enum: BRAIN_OBSERVATION_SOURCE_TYPES }).notNull().default("agent"),
9208
+ /** T383/T417: agent provenance — identifies the spawned agent that produced this observation. Null for legacy entries. */
9209
+ agent: text("agent"),
9210
+ // nullable — null for legacy observations
9208
9211
  contentHash: text("content_hash"),
9209
9212
  // SHA-256 prefix for dedup
9210
9213
  discoveryTokens: integer("discovery_tokens"),
@@ -9221,7 +9224,9 @@ var init_brain_schema = __esm({
9221
9224
  // T033: composite replaces single-col content_hash; see brain migration
9222
9225
  index("idx_brain_observations_content_hash_created_at").on(table.contentHash, table.createdAt),
9223
9226
  // T033: type + project compound filter optimization
9224
- index("idx_brain_observations_type_project").on(table.type, table.project)
9227
+ index("idx_brain_observations_type_project").on(table.type, table.project),
9228
+ // T417: agent provenance index for memory.find --agent filter
9229
+ index("idx_brain_observations_agent").on(table.agent)
9225
9230
  ]
9226
9231
  );
9227
9232
  brainStickyNotes = sqliteTable(
@@ -10795,6 +10800,7 @@ var init_platform_paths = __esm({
10795
10800
  });
10796
10801
 
10797
10802
  // packages/core/src/paths.ts
10803
+ import { AsyncLocalStorage } from "node:async_hooks";
10798
10804
  import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
10799
10805
  import { homedir } from "node:os";
10800
10806
  import { dirname as dirname2, join as join4, resolve as resolve2 } from "node:path";
@@ -10826,6 +10832,10 @@ function getCleoDirAbsolute(cwd) {
10826
10832
  return resolve2(cwd ?? process.cwd(), cleoDir);
10827
10833
  }
10828
10834
  function getProjectRoot(cwd) {
10835
+ const scope = worktreeScope.getStore();
10836
+ if (scope !== void 0) {
10837
+ return scope.worktreeRoot;
10838
+ }
10829
10839
  if (process.env["CLEO_ROOT"]) {
10830
10840
  return process.env["CLEO_ROOT"];
10831
10841
  }
@@ -10988,13 +10998,14 @@ function getClaudeAgentsDir() {
10988
10998
  function getClaudeMemDbPath() {
10989
10999
  return process.env["CLAUDE_MEM_DB"] ?? join4(homedir(), ".claude-mem", "claude-mem.db");
10990
11000
  }
10991
- var DEFAULT_AGENT_OUTPUTS_DIR;
11001
+ var worktreeScope, DEFAULT_AGENT_OUTPUTS_DIR;
10992
11002
  var init_paths = __esm({
10993
11003
  "packages/core/src/paths.ts"() {
10994
11004
  "use strict";
10995
11005
  init_src();
10996
11006
  init_errors3();
10997
11007
  init_platform_paths();
11008
+ worktreeScope = new AsyncLocalStorage();
10998
11009
  DEFAULT_AGENT_OUTPUTS_DIR = ".cleo/agent-outputs";
10999
11010
  }
11000
11011
  });
@@ -17548,6 +17559,9 @@ var init_brain_accessor = __esm({
17548
17559
  if (params.sourceSessionId) {
17549
17560
  conditions.push(eq7(brainObservations.sourceSessionId, params.sourceSessionId));
17550
17561
  }
17562
+ if (params.agent) {
17563
+ conditions.push(eq7(brainObservations.agent, params.agent));
17564
+ }
17551
17565
  let query = this.db.select().from(brainObservations).orderBy(desc2(brainObservations.createdAt));
17552
17566
  if (conditions.length > 0) {
17553
17567
  query = query.where(and5(...conditions));
@@ -25412,44 +25426,51 @@ __export(brain_retrieval_exports, {
25412
25426
  });
25413
25427
  import { createHash as createHash3 } from "node:crypto";
25414
25428
  async function searchBrainCompact(projectRoot, params) {
25415
- const { query, limit, tables, dateStart, dateEnd } = params;
25429
+ const { query, limit, tables, dateStart, dateEnd, agent } = params;
25416
25430
  if (!query || !query.trim()) {
25417
25431
  return { results: [], total: 0, tokensEstimated: 0 };
25418
25432
  }
25433
+ const effectiveTables = agent !== void 0 && agent !== null ? ["observations"] : tables;
25419
25434
  const searchResult = await searchBrain(projectRoot, query, {
25420
25435
  limit: limit ?? 10,
25421
- tables
25436
+ tables: effectiveTables
25422
25437
  });
25423
25438
  let results = [];
25424
- for (const d of searchResult.decisions) {
25425
- const raw = d;
25426
- results.push({
25427
- id: d.id,
25428
- type: "decision",
25429
- title: d.decision.slice(0, 80),
25430
- date: (d.createdAt ?? raw["created_at"]) || ""
25431
- });
25432
- }
25433
- for (const p2 of searchResult.patterns) {
25434
- const raw = p2;
25435
- results.push({
25436
- id: p2.id,
25437
- type: "pattern",
25438
- title: p2.pattern.slice(0, 80),
25439
- date: (p2.extractedAt ?? raw["extracted_at"]) || ""
25440
- });
25441
- }
25442
- for (const l of searchResult.learnings) {
25443
- const raw = l;
25444
- results.push({
25445
- id: l.id,
25446
- type: "learning",
25447
- title: l.insight.slice(0, 80),
25448
- date: (l.createdAt ?? raw["created_at"]) || ""
25449
- });
25439
+ if (!agent) {
25440
+ for (const d of searchResult.decisions) {
25441
+ const raw = d;
25442
+ results.push({
25443
+ id: d.id,
25444
+ type: "decision",
25445
+ title: d.decision.slice(0, 80),
25446
+ date: (d.createdAt ?? raw["created_at"]) || ""
25447
+ });
25448
+ }
25449
+ for (const p2 of searchResult.patterns) {
25450
+ const raw = p2;
25451
+ results.push({
25452
+ id: p2.id,
25453
+ type: "pattern",
25454
+ title: p2.pattern.slice(0, 80),
25455
+ date: (p2.extractedAt ?? raw["extracted_at"]) || ""
25456
+ });
25457
+ }
25458
+ for (const l of searchResult.learnings) {
25459
+ const raw = l;
25460
+ results.push({
25461
+ id: l.id,
25462
+ type: "learning",
25463
+ title: l.insight.slice(0, 80),
25464
+ date: (l.createdAt ?? raw["created_at"]) || ""
25465
+ });
25466
+ }
25450
25467
  }
25451
25468
  for (const o of searchResult.observations) {
25452
25469
  const raw = o;
25470
+ if (agent) {
25471
+ const rowAgent = o.agent ?? raw["agent"] ?? null;
25472
+ if (rowAgent !== agent) continue;
25473
+ }
25453
25474
  results.push({
25454
25475
  id: o.id,
25455
25476
  type: "observation",
@@ -25663,7 +25684,15 @@ function classifyObservationType(text3) {
25663
25684
  return "discovery";
25664
25685
  }
25665
25686
  async function observeBrain(projectRoot, params) {
25666
- const { text: text3, title: titleParam, type: typeParam, project, sourceSessionId, sourceType } = params;
25687
+ const {
25688
+ text: text3,
25689
+ title: titleParam,
25690
+ type: typeParam,
25691
+ project,
25692
+ sourceSessionId,
25693
+ sourceType,
25694
+ agent
25695
+ } = params;
25667
25696
  if (!text3 || !text3.trim()) {
25668
25697
  throw new Error("Observation text is required");
25669
25698
  }
@@ -25712,6 +25741,7 @@ async function observeBrain(projectRoot, params) {
25712
25741
  project: project ?? null,
25713
25742
  sourceSessionId: validSessionId,
25714
25743
  sourceType: sourceType ?? "agent",
25744
+ agent: agent ?? null,
25715
25745
  createdAt: now2
25716
25746
  });
25717
25747
  if (isEmbeddingAvailable()) {
@@ -78101,6 +78131,116 @@ var init_claude_mem_migration = __esm({
78101
78131
  }
78102
78132
  });
78103
78133
 
78134
+ // packages/core/src/memory/mental-model-queue.ts
78135
+ async function drainQueue() {
78136
+ if (_queue.length === 0) return 0;
78137
+ const batch = _queue.splice(0, _queue.length);
78138
+ let count4 = 0;
78139
+ const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
78140
+ for (const entry of batch) {
78141
+ try {
78142
+ const result = await observeBrain2(entry.projectRoot, entry.params);
78143
+ entry.resolve(result);
78144
+ count4++;
78145
+ } catch (err) {
78146
+ const error48 = err instanceof Error ? err : new Error(String(err));
78147
+ entry.reject(error48);
78148
+ }
78149
+ }
78150
+ return count4;
78151
+ }
78152
+ function exitFlush() {
78153
+ if (_queue.length === 0) return;
78154
+ drainQueue().catch(() => {
78155
+ });
78156
+ }
78157
+ function registerExitHooks() {
78158
+ if (_hooksRegistered) return;
78159
+ _hooksRegistered = true;
78160
+ process.on("exit", exitFlush);
78161
+ process.once("SIGINT", () => {
78162
+ drainQueue().catch(() => {
78163
+ }).finally(() => {
78164
+ process.exit(130);
78165
+ });
78166
+ });
78167
+ process.once("SIGTERM", () => {
78168
+ drainQueue().catch(() => {
78169
+ }).finally(() => {
78170
+ process.exit(143);
78171
+ });
78172
+ });
78173
+ }
78174
+ function ensureTimer() {
78175
+ if (_timer !== void 0) return;
78176
+ _timer = setInterval(() => {
78177
+ if (_queue.length === 0) return;
78178
+ if (_flushing) return;
78179
+ _flushing = true;
78180
+ drainQueue().catch(() => {
78181
+ }).finally(() => {
78182
+ _flushing = false;
78183
+ });
78184
+ }, FLUSH_INTERVAL_MS);
78185
+ if (typeof _timer.unref === "function") {
78186
+ _timer.unref();
78187
+ }
78188
+ }
78189
+ function isMentalModelObservation(params) {
78190
+ if (!params.agent) return false;
78191
+ const type = params.type ?? "discovery";
78192
+ return MENTAL_MODEL_TYPES.has(type);
78193
+ }
78194
+ var FLUSH_INTERVAL_MS, FLUSH_WATERMARK, MENTAL_MODEL_TYPES, _queue, _flushing, _hooksRegistered, _timer, mentalModelQueue;
78195
+ var init_mental_model_queue = __esm({
78196
+ "packages/core/src/memory/mental-model-queue.ts"() {
78197
+ "use strict";
78198
+ FLUSH_INTERVAL_MS = 5e3;
78199
+ FLUSH_WATERMARK = 50;
78200
+ MENTAL_MODEL_TYPES = /* @__PURE__ */ new Set([
78201
+ "discovery",
78202
+ "change",
78203
+ "feature",
78204
+ "decision",
78205
+ "bugfix",
78206
+ "refactor"
78207
+ ]);
78208
+ _queue = [];
78209
+ _flushing = false;
78210
+ _hooksRegistered = false;
78211
+ mentalModelQueue = {
78212
+ enqueue(projectRoot, params) {
78213
+ registerExitHooks();
78214
+ ensureTimer();
78215
+ return new Promise((resolve16, reject) => {
78216
+ _queue.push({ projectRoot, params, resolve: resolve16, reject });
78217
+ if (_queue.length >= FLUSH_WATERMARK && !_flushing) {
78218
+ _flushing = true;
78219
+ drainQueue().catch(() => {
78220
+ }).finally(() => {
78221
+ _flushing = false;
78222
+ });
78223
+ }
78224
+ });
78225
+ },
78226
+ async flush() {
78227
+ if (_flushing) {
78228
+ await new Promise((r) => setTimeout(r, 50));
78229
+ }
78230
+ _flushing = true;
78231
+ try {
78232
+ return await drainQueue();
78233
+ } finally {
78234
+ _flushing = false;
78235
+ }
78236
+ },
78237
+ size() {
78238
+ return _queue.length;
78239
+ }
78240
+ };
78241
+ }
78242
+ });
78243
+
78104
78244
  // packages/core/src/memory/brain-reasoning.ts
78105
78245
  var brain_reasoning_exports = {};
78106
78246
  __export(brain_reasoning_exports, {
@@ -78476,7 +78616,8 @@ async function memoryFind(params, projectRoot) {
78476
78616
  limit: params.limit,
78477
78617
  tables: params.tables,
78478
78618
  dateStart: params.dateStart,
78479
- dateEnd: params.dateEnd
78619
+ dateEnd: params.dateEnd,
78620
+ agent: params.agent
78480
78621
  });
78481
78622
  return { success: true, data: result };
78482
78623
  } catch (error48) {
@@ -78526,14 +78667,24 @@ async function memoryFetch(params, projectRoot) {
78526
78667
  async function memoryObserve(params, projectRoot) {
78527
78668
  try {
78528
78669
  const root = resolveRoot(projectRoot);
78529
- const result = await observeBrain(root, {
78670
+ const observeParams = {
78530
78671
  text: params.text,
78531
78672
  title: params.title,
78532
78673
  type: params.type,
78533
78674
  project: params.project,
78534
78675
  sourceSessionId: params.sourceSessionId,
78535
- sourceType: params.sourceType
78536
- });
78676
+ sourceType: params.sourceType,
78677
+ agent: params.agent
78678
+ };
78679
+ let result;
78680
+ if (isMentalModelObservation(observeParams) && observeParams.agent) {
78681
+ result = await mentalModelQueue.enqueue(root, {
78682
+ ...observeParams,
78683
+ agent: observeParams.agent
78684
+ });
78685
+ } else {
78686
+ result = await observeBrain(root, observeParams);
78687
+ }
78537
78688
  return { success: true, data: result };
78538
78689
  } catch (error48) {
78539
78690
  return {
@@ -79133,6 +79284,7 @@ var init_engine_compat = __esm({
79133
79284
  init_brain_links();
79134
79285
  init_brain_retrieval();
79135
79286
  init_learnings();
79287
+ init_mental_model_queue();
79136
79288
  init_patterns();
79137
79289
  }
79138
79290
  });
@@ -101465,11 +101617,31 @@ var init_registry5 = __esm({
101465
101617
  gateway: "query",
101466
101618
  domain: "orchestrate",
101467
101619
  operation: "analyze",
101468
- description: "orchestrate.analyze (query) \u2014 absorbs critical.path via mode param",
101620
+ description: 'orchestrate.analyze (query) \u2014 absorbs critical.path via mode param; Wave 7a adds mode="parallel-safety"',
101469
101621
  tier: 1,
101470
101622
  idempotent: true,
101471
101623
  sessionRequired: false,
101472
- requiredParams: []
101624
+ requiredParams: [],
101625
+ params: [
101626
+ {
101627
+ name: "epicId",
101628
+ type: "string",
101629
+ required: false,
101630
+ description: "Epic ID for standard analysis (required unless mode=critical-path or parallel-safety)"
101631
+ },
101632
+ {
101633
+ name: "mode",
101634
+ type: "string",
101635
+ required: false,
101636
+ description: 'Analysis mode: omit for standard analysis, "critical-path" for CPM, "parallel-safety" for dep-graph grouping'
101637
+ },
101638
+ {
101639
+ name: "taskIds",
101640
+ type: "array",
101641
+ required: false,
101642
+ description: 'Task ID list for mode="parallel-safety" \u2014 returns {parallelSafe, groups} dep-graph analysis'
101643
+ }
101644
+ ]
101473
101645
  },
101474
101646
  {
101475
101647
  gateway: "query",
@@ -101524,7 +101696,16 @@ var init_registry5 = __esm({
101524
101696
  tier: 0,
101525
101697
  idempotent: true,
101526
101698
  sessionRequired: false,
101527
- requiredParams: ["query"]
101699
+ requiredParams: ["query"],
101700
+ params: [
101701
+ {
101702
+ name: "agent",
101703
+ type: "string",
101704
+ required: false,
101705
+ description: "Filter results to observations produced by the named agent (T418 mental models)",
101706
+ cli: { flag: "--agent <name>" }
101707
+ }
101708
+ ]
101528
101709
  },
101529
101710
  {
101530
101711
  gateway: "query",
@@ -104053,6 +104234,70 @@ var init_registry5 = __esm({
104053
104234
  sessionRequired: false,
104054
104235
  requiredParams: ["templateId", "epicId"]
104055
104236
  },
104237
+ // orchestrate — Wave 7a dispatch ops (T408, T409, T410, T415)
104238
+ {
104239
+ gateway: "query",
104240
+ domain: "orchestrate",
104241
+ operation: "classify",
104242
+ description: "orchestrate.classify (query) \u2014 classify a request against CANT team registry to route to correct team/lead/protocol",
104243
+ tier: 2,
104244
+ idempotent: true,
104245
+ sessionRequired: false,
104246
+ requiredParams: ["request"],
104247
+ params: [
104248
+ {
104249
+ name: "request",
104250
+ type: "string",
104251
+ required: true,
104252
+ description: "The request or task description to classify against team consult-when hints"
104253
+ },
104254
+ {
104255
+ name: "context",
104256
+ type: "string",
104257
+ required: false,
104258
+ description: "Optional additional context to improve classification accuracy"
104259
+ }
104260
+ ]
104261
+ },
104262
+ {
104263
+ gateway: "query",
104264
+ domain: "orchestrate",
104265
+ operation: "fanout.status",
104266
+ description: "orchestrate.fanout.status (query) \u2014 get status of a running fanout by its manifest entry ID",
104267
+ tier: 2,
104268
+ idempotent: true,
104269
+ sessionRequired: false,
104270
+ requiredParams: ["manifestEntryId"],
104271
+ params: [
104272
+ {
104273
+ name: "manifestEntryId",
104274
+ type: "string",
104275
+ required: true,
104276
+ description: "Manifest entry ID returned by orchestrate.fanout"
104277
+ }
104278
+ ]
104279
+ },
104280
+ {
104281
+ gateway: "mutate",
104282
+ domain: "orchestrate",
104283
+ operation: "fanout",
104284
+ description: "orchestrate.fanout (mutate) \u2014 fan out N spawn requests in parallel via Promise.allSettled",
104285
+ tier: 2,
104286
+ idempotent: false,
104287
+ sessionRequired: false,
104288
+ requiredParams: ["items"],
104289
+ params: [
104290
+ {
104291
+ name: "items",
104292
+ type: "array",
104293
+ required: true,
104294
+ description: "Array of {team, taskId, skill?} objects to fan out"
104295
+ }
104296
+ ]
104297
+ },
104298
+ // orchestrate — Wave 7a: analyze parallel-safety mode (T410)
104299
+ // Note: orchestrate.analyze already registered above; this documents the new
104300
+ // mode="parallel-safety" variant via the existing analyze operation's params.
104056
104301
  // conduit — agent messaging operations (replaces standalone clawmsgr scripts)
104057
104302
  {
104058
104303
  gateway: "query",
@@ -105631,13 +105876,27 @@ async function orchestrateSpawnExecute(taskId, adapterId, protocolType, projectR
105631
105876
  }
105632
105877
  };
105633
105878
  }
105879
+ let composedPrompt = spawnContext.prompt;
105880
+ try {
105881
+ const { composeSpawnPayload } = await import("@cleocode/cant");
105882
+ const { brainContextProvider } = await import("@cleocode/cant");
105883
+ const { createHash: createHash14 } = await import("node:crypto");
105884
+ const projectHash = createHash14("sha256").update(cwd).digest("hex").slice(0, 12);
105885
+ const agentDef = spawnContext.agentDef;
105886
+ if (agentDef) {
105887
+ const provider2 = brainContextProvider(cwd);
105888
+ const payload = await composeSpawnPayload(agentDef, provider2, projectHash);
105889
+ composedPrompt = payload.systemPrompt;
105890
+ }
105891
+ } catch {
105892
+ }
105634
105893
  const cleoSpawnContext = {
105635
105894
  taskId: spawnContext.taskId,
105636
105895
  protocol: protocolType || spawnContext.protocol,
105637
- prompt: spawnContext.prompt,
105896
+ prompt: composedPrompt,
105638
105897
  provider: provider.id,
105639
105898
  options: {
105640
- prompt: spawnContext.prompt
105899
+ prompt: composedPrompt
105641
105900
  },
105642
105901
  workingDirectory: cwd,
105643
105902
  tokenResolution: {
@@ -110231,7 +110490,9 @@ var init_memory2 = __esm({
110231
110490
  limit: params?.limit,
110232
110491
  tables: params?.tables,
110233
110492
  dateStart: params?.dateStart,
110234
- dateEnd: params?.dateEnd
110493
+ dateEnd: params?.dateEnd,
110494
+ // T418: optional agent filter for per-agent mental model retrieval
110495
+ agent: params?.agent
110235
110496
  },
110236
110497
  projectRoot
110237
110498
  );
@@ -110440,7 +110701,9 @@ var init_memory2 = __esm({
110440
110701
  type: params?.type,
110441
110702
  project: params?.project,
110442
110703
  sourceSessionId: params?.sourceSessionId,
110443
- sourceType: params?.sourceType
110704
+ sourceType: params?.sourceType,
110705
+ // T417: optional agent provenance for mental model observations
110706
+ agent: params?.agent
110444
110707
  },
110445
110708
  projectRoot
110446
110709
  );
@@ -111241,6 +111504,216 @@ var init_nexus2 = __esm({
111241
111504
  });
111242
111505
 
111243
111506
  // packages/cleo/src/dispatch/domains/orchestrate.ts
111507
+ async function orchestrateClassify(request, context, projectRoot) {
111508
+ try {
111509
+ const { getCleoCantWorkflowsDir: getCleoCantWorkflowsDir2 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
111510
+ const { readFileSync: readFileSync101, readdirSync: readdirSync42, existsSync: existsSync131 } = await import("node:fs");
111511
+ const { join: join131 } = await import("node:path");
111512
+ const workflowsDir = getCleoCantWorkflowsDir2();
111513
+ const combined = `${request} ${context ?? ""}`.toLowerCase();
111514
+ const matches = [];
111515
+ if (existsSync131(workflowsDir)) {
111516
+ const files = readdirSync42(workflowsDir).filter((f2) => f2.endsWith(".cant"));
111517
+ for (const file2 of files) {
111518
+ try {
111519
+ const src = readFileSync101(join131(workflowsDir, file2), "utf-8");
111520
+ const teamMatch = /^team\s+(\S+):/m.exec(src);
111521
+ if (!teamMatch) continue;
111522
+ const teamName = teamMatch[1];
111523
+ const cwMatch = /consult-when:\s*["']?(.+?)["']?\s*$/m.exec(src);
111524
+ const consultWhen = cwMatch ? cwMatch[1].trim() : "";
111525
+ const stagesMatch = /stages:\s*\[([^\]]+)\]/.exec(src);
111526
+ const stages = stagesMatch ? stagesMatch[1].split(",").map((s3) => s3.trim()) : [];
111527
+ const hintWords = consultWhen.toLowerCase().split(/\s+/);
111528
+ const score = hintWords.filter((w2) => combined.includes(w2)).length;
111529
+ matches.push({ team: teamName, score, consultWhen, stages });
111530
+ } catch {
111531
+ }
111532
+ }
111533
+ }
111534
+ const localCantDir = join131(projectRoot, ".cleo", "workflows");
111535
+ if (existsSync131(localCantDir)) {
111536
+ const files = readdirSync42(localCantDir).filter((f2) => f2.endsWith(".cant"));
111537
+ for (const file2 of files) {
111538
+ try {
111539
+ const src = readFileSync101(join131(localCantDir, file2), "utf-8");
111540
+ const teamMatch = /^team\s+(\S+):/m.exec(src);
111541
+ if (!teamMatch) continue;
111542
+ const teamName = teamMatch[1];
111543
+ const cwMatch = /consult-when:\s*["']?(.+?)["']?\s*$/m.exec(src);
111544
+ const consultWhen = cwMatch ? cwMatch[1].trim() : "";
111545
+ const stagesMatch = /stages:\s*\[([^\]]+)\]/.exec(src);
111546
+ const stages = stagesMatch ? stagesMatch[1].split(",").map((s3) => s3.trim()) : [];
111547
+ const hintWords = consultWhen.toLowerCase().split(/\s+/);
111548
+ const score = hintWords.filter((w2) => combined.includes(w2)).length;
111549
+ matches.push({ team: teamName, score, consultWhen, stages });
111550
+ } catch {
111551
+ }
111552
+ }
111553
+ }
111554
+ if (matches.length === 0) {
111555
+ return {
111556
+ success: true,
111557
+ data: {
111558
+ team: null,
111559
+ lead: null,
111560
+ protocol: null,
111561
+ stage: null,
111562
+ confidence: 0,
111563
+ reasoning: "No CANT team definitions found. Seed teams.cant in the global workflows dir (W7b runtime enforcement) to enable team routing."
111564
+ }
111565
+ };
111566
+ }
111567
+ matches.sort((a, b2) => b2.score - a.score);
111568
+ const best = matches[0];
111569
+ return {
111570
+ success: true,
111571
+ data: {
111572
+ team: best.team,
111573
+ lead: null,
111574
+ // lead resolution requires W7b runtime bridge
111575
+ protocol: "base-subagent",
111576
+ // default protocol stub
111577
+ stage: best.stages[0] ?? null,
111578
+ confidence: best.score > 0 ? 0.5 : 0.1,
111579
+ reasoning: best.score > 0 ? `Matched team '${best.team}' via consult-when hint: "${best.consultWhen}"` : `No strong match found; defaulting to first registered team '${best.team}'`
111580
+ }
111581
+ };
111582
+ } catch (error48) {
111583
+ getLogger("domain:orchestrate").error(
111584
+ { operation: "classify", err: error48 },
111585
+ error48 instanceof Error ? error48.message : String(error48)
111586
+ );
111587
+ return {
111588
+ success: false,
111589
+ error: {
111590
+ code: "E_CLASSIFY_FAILED",
111591
+ message: error48 instanceof Error ? error48.message : String(error48)
111592
+ }
111593
+ };
111594
+ }
111595
+ }
111596
+ async function orchestrateFanout(items, projectRoot) {
111597
+ const manifestEntryId = `fanout-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
111598
+ try {
111599
+ const settled = await Promise.allSettled(
111600
+ items.map(async (item) => {
111601
+ void projectRoot;
111602
+ return { taskId: item.taskId, status: "queued" };
111603
+ })
111604
+ );
111605
+ const results = settled.map((outcome, i) => {
111606
+ if (outcome.status === "fulfilled") {
111607
+ return outcome.value;
111608
+ }
111609
+ return {
111610
+ taskId: items[i].taskId,
111611
+ status: "failed",
111612
+ error: outcome.reason instanceof Error ? outcome.reason.message : String(outcome.reason)
111613
+ };
111614
+ });
111615
+ return {
111616
+ success: true,
111617
+ data: {
111618
+ manifestEntryId,
111619
+ results,
111620
+ total: items.length,
111621
+ queued: results.filter((r) => r.status === "queued").length,
111622
+ failed: results.filter((r) => r.status === "failed").length,
111623
+ note: "fanout spawn execution is a W7b stub \u2014 real Pi adapter wiring lands with runtime enforcement"
111624
+ }
111625
+ };
111626
+ } catch (error48) {
111627
+ getLogger("domain:orchestrate").error(
111628
+ { operation: "fanout", err: error48 },
111629
+ error48 instanceof Error ? error48.message : String(error48)
111630
+ );
111631
+ return {
111632
+ success: false,
111633
+ error: {
111634
+ code: "E_FANOUT_FAILED",
111635
+ message: error48 instanceof Error ? error48.message : String(error48)
111636
+ }
111637
+ };
111638
+ }
111639
+ }
111640
+ async function orchestrateAnalyzeParallelSafety(taskIds, projectRoot) {
111641
+ if (taskIds.length === 0) {
111642
+ return {
111643
+ success: true,
111644
+ data: {
111645
+ parallelSafe: true,
111646
+ groups: [],
111647
+ note: "No tasks provided \u2014 trivially parallel-safe"
111648
+ }
111649
+ };
111650
+ }
111651
+ try {
111652
+ let transitiveClose2 = function(id, visited = /* @__PURE__ */ new Set()) {
111653
+ if (visited.has(id)) return visited;
111654
+ visited.add(id);
111655
+ const deps = depMap.get(id) ?? [];
111656
+ for (const dep of deps) {
111657
+ transitiveClose2(dep, visited);
111658
+ }
111659
+ return visited;
111660
+ }, parallelSafe2 = function(a, b2) {
111661
+ const closureA = closures.get(a) ?? /* @__PURE__ */ new Set();
111662
+ const closureB = closures.get(b2) ?? /* @__PURE__ */ new Set();
111663
+ return !closureA.has(b2) && !closureB.has(a);
111664
+ };
111665
+ var transitiveClose = transitiveClose2, parallelSafe = parallelSafe2;
111666
+ const { getAccessor: getAccessor2 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
111667
+ const accessor = await getAccessor2(projectRoot);
111668
+ const result = await accessor.queryTasks({});
111669
+ const allTasks = result?.tasks ?? [];
111670
+ const depMap = /* @__PURE__ */ new Map();
111671
+ for (const t of allTasks) {
111672
+ const deps = t.blockers ?? [];
111673
+ depMap.set(t.id, deps);
111674
+ }
111675
+ const closures = /* @__PURE__ */ new Map();
111676
+ for (const id of taskIds) {
111677
+ closures.set(id, transitiveClose2(id));
111678
+ }
111679
+ const groups = [];
111680
+ for (const id of taskIds) {
111681
+ let placed = false;
111682
+ for (const group of groups) {
111683
+ if (group.every((member) => parallelSafe2(id, member))) {
111684
+ group.push(id);
111685
+ placed = true;
111686
+ break;
111687
+ }
111688
+ }
111689
+ if (!placed) {
111690
+ groups.push([id]);
111691
+ }
111692
+ }
111693
+ const isFullyParallelSafe = groups.length <= 1;
111694
+ return {
111695
+ success: true,
111696
+ data: {
111697
+ parallelSafe: isFullyParallelSafe,
111698
+ groups,
111699
+ taskCount: taskIds.length,
111700
+ groupCount: groups.length
111701
+ }
111702
+ };
111703
+ } catch (error48) {
111704
+ getLogger("domain:orchestrate").error(
111705
+ { operation: "analyze/parallel-safety", err: error48 },
111706
+ error48 instanceof Error ? error48.message : String(error48)
111707
+ );
111708
+ return {
111709
+ success: false,
111710
+ error: {
111711
+ code: "E_ANALYZE_FAILED",
111712
+ message: error48 instanceof Error ? error48.message : String(error48)
111713
+ }
111714
+ };
111715
+ }
111716
+ }
111244
111717
  var OrchestrateHandler;
111245
111718
  var init_orchestrate2 = __esm({
111246
111719
  "packages/cleo/src/dispatch/domains/orchestrate.ts"() {
@@ -111297,9 +111770,55 @@ var init_orchestrate2 = __esm({
111297
111770
  case "analyze": {
111298
111771
  const epicId = params?.epicId;
111299
111772
  const mode = params?.mode;
111773
+ if (mode === "parallel-safety") {
111774
+ const taskIds = params?.taskIds;
111775
+ const result2 = await orchestrateAnalyzeParallelSafety(taskIds ?? [], projectRoot);
111776
+ return wrapResult(result2, "query", "orchestrate", "analyze", startTime);
111777
+ }
111300
111778
  const result = await orchestrateAnalyze(epicId, projectRoot, mode);
111301
111779
  return wrapResult(result, "query", "orchestrate", "analyze", startTime);
111302
111780
  }
111781
+ case "classify": {
111782
+ const request = params?.request;
111783
+ if (!request) {
111784
+ return errorResult(
111785
+ "query",
111786
+ "orchestrate",
111787
+ operation,
111788
+ "E_INVALID_INPUT",
111789
+ "request is required",
111790
+ startTime
111791
+ );
111792
+ }
111793
+ const context = params?.context;
111794
+ const result = await orchestrateClassify(request, context, projectRoot);
111795
+ return wrapResult(result, "query", "orchestrate", operation, startTime);
111796
+ }
111797
+ case "fanout.status": {
111798
+ const manifestEntryId = params?.manifestEntryId;
111799
+ if (!manifestEntryId) {
111800
+ return errorResult(
111801
+ "query",
111802
+ "orchestrate",
111803
+ operation,
111804
+ "E_INVALID_INPUT",
111805
+ "manifestEntryId is required",
111806
+ startTime
111807
+ );
111808
+ }
111809
+ return {
111810
+ meta: dispatchMeta("query", "orchestrate", operation, startTime),
111811
+ success: true,
111812
+ data: {
111813
+ manifestEntryId,
111814
+ pending: [],
111815
+ running: [],
111816
+ complete: [],
111817
+ failed: [],
111818
+ note: "fanout.status tracking is a W7b stub \u2014 live tracking lands with runtime enforcement"
111819
+ }
111820
+ };
111821
+ }
111303
111822
  case "context": {
111304
111823
  const epicId = params?.epicId;
111305
111824
  const result = await orchestrateContext(epicId, projectRoot);
@@ -111552,6 +112071,21 @@ var init_orchestrate2 = __esm({
111552
112071
  }
111553
112072
  });
111554
112073
  }
112074
+ case "fanout": {
112075
+ const items = params?.items;
112076
+ if (!items || !Array.isArray(items) || items.length === 0) {
112077
+ return errorResult(
112078
+ "mutate",
112079
+ "orchestrate",
112080
+ operation,
112081
+ "E_INVALID_INPUT",
112082
+ "items array is required and must be non-empty",
112083
+ startTime
112084
+ );
112085
+ }
112086
+ const result = await orchestrateFanout(items, projectRoot);
112087
+ return wrapResult(result, "mutate", "orchestrate", operation, startTime);
112088
+ }
111555
112089
  case "tessera.instantiate": {
111556
112090
  const templateId = params?.templateId;
111557
112091
  const epicId = params?.epicId;
@@ -111627,7 +112161,10 @@ var init_orchestrate2 = __esm({
111627
112161
  "waves",
111628
112162
  "bootstrap",
111629
112163
  "unblock.opportunities",
111630
- "tessera.list"
112164
+ "tessera.list",
112165
+ // Wave 7a (T379)
112166
+ "classify",
112167
+ "fanout.status"
111631
112168
  ],
111632
112169
  mutate: [
111633
112170
  "start",
@@ -111636,7 +112173,9 @@ var init_orchestrate2 = __esm({
111636
112173
  "spawn.execute",
111637
112174
  "validate",
111638
112175
  "parallel",
111639
- "tessera.instantiate"
112176
+ "tessera.instantiate",
112177
+ // Wave 7a (T379)
112178
+ "fanout"
111640
112179
  ]
111641
112180
  };
111642
112181
  }
@@ -121160,7 +121699,7 @@ function registerMemoryBrainCommand(program) {
121160
121699
  memory.command("find <query>").description("Search BRAIN memory (all tables, or filter by --type pattern|learning)").option("--type <type>", "Filter by memory type: pattern or learning (default: all)").option(
121161
121700
  "--pattern-type <type>",
121162
121701
  "Filter patterns by type: workflow, blocker, success, failure, optimization"
121163
- ).option("--min-confidence <n>", "Minimum confidence for learnings", parseFloat).option("--actionable", "Only show actionable learnings").option("--limit <n>", "Maximum results", parseInt).option("--json", "Output as JSON").action(async (query, opts) => {
121702
+ ).option("--min-confidence <n>", "Minimum confidence for learnings", parseFloat).option("--actionable", "Only show actionable learnings").option("--limit <n>", "Maximum results", parseInt).option("--agent <name>", "Filter observations by agent provenance name (Wave 8 mental models)").option("--json", "Output as JSON").action(async (query, opts) => {
121164
121703
  const memType = opts["type"];
121165
121704
  if (memType === "pattern") {
121166
121705
  await dispatchFromCli(
@@ -121194,7 +121733,9 @@ function registerMemoryBrainCommand(program) {
121194
121733
  "find",
121195
121734
  {
121196
121735
  query,
121197
- limit: opts["limit"]
121736
+ limit: opts["limit"],
121737
+ // T418: forward agent filter when provided
121738
+ ...opts["agent"] !== void 0 && { agent: opts["agent"] }
121198
121739
  },
121199
121740
  { command: "memory", operation: "memory.find" }
121200
121741
  );
@@ -121218,14 +121759,18 @@ function registerMemoryBrainCommand(program) {
121218
121759
  }
121219
121760
  cliOutput(result, { command: "memory", operation: "memory.stats" });
121220
121761
  });
121221
- memory.command("observe <text>").description("Save an observation to brain.db").option("--title <title>", "Short title for the observation").action(async (text3, opts) => {
121762
+ memory.command("observe <text>").description("Save an observation to brain.db").option("--title <title>", "Short title for the observation").option(
121763
+ "--agent <name>",
121764
+ "Tag this observation with the producing agent name (Wave 8 mental models)"
121765
+ ).action(async (text3, opts) => {
121222
121766
  await dispatchFromCli(
121223
121767
  "mutate",
121224
121768
  "memory",
121225
121769
  "observe",
121226
121770
  {
121227
121771
  text: text3,
121228
- title: opts["title"]
121772
+ title: opts["title"],
121773
+ ...opts["agent"] !== void 0 && { agent: opts["agent"] }
121229
121774
  },
121230
121775
  { command: "memory", operation: "memory.observe" }
121231
121776
  );