@cleocode/cleo 2026.4.60 → 2026.4.62

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
@@ -1931,7 +1931,7 @@ var init_sql = __esm({
1931
1931
  return new SQL([new StringChunk(str)]);
1932
1932
  }
1933
1933
  _sql.raw = raw;
1934
- function join135(chunks, separator) {
1934
+ function join136(chunks, separator) {
1935
1935
  const result = [];
1936
1936
  for (const [i, chunk] of chunks.entries()) {
1937
1937
  if (i > 0 && separator !== void 0) result.push(separator);
@@ -1939,7 +1939,7 @@ var init_sql = __esm({
1939
1939
  }
1940
1940
  return new SQL(result);
1941
1941
  }
1942
- _sql.join = join135;
1942
+ _sql.join = join136;
1943
1943
  function identifier(value) {
1944
1944
  return new Name(value);
1945
1945
  }
@@ -6713,7 +6713,7 @@ var init_select2 = __esm({
6713
6713
  const baseTableName = this.tableName;
6714
6714
  const tableName = getTableLikeName(table);
6715
6715
  for (const item of extractUsedTable(table)) this.usedTables.add(item);
6716
- if (typeof tableName === "string" && this.config.joins?.some((join135) => join135.alias === tableName)) throw new Error(`Alias "${tableName}" is already used in this query`);
6716
+ if (typeof tableName === "string" && this.config.joins?.some((join136) => join136.alias === tableName)) throw new Error(`Alias "${tableName}" is already used in this query`);
6717
6717
  if (!this.isPartialSelect) {
6718
6718
  if (Object.keys(this.joinsNotNullableMap).length === 1 && typeof baseTableName === "string") this.config.fields = { [baseTableName]: this.config.fields };
6719
6719
  if (typeof tableName === "string" && !is(table, SQL)) {
@@ -7998,7 +7998,7 @@ var init_dialect = __esm({
7998
7998
  if (!joins2) return;
7999
7999
  const withEntries = Object.entries(joins2).filter(([_2, v2]) => v2);
8000
8000
  if (!withEntries.length) return;
8001
- return sql.join(withEntries.map(([k2, join135]) => {
8001
+ return sql.join(withEntries.map(([k2, join136]) => {
8002
8002
  const relation = tableConfig.relations[k2];
8003
8003
  const isSingle2 = is(relation, One3);
8004
8004
  const targetTable = aliasedTable(relation.targetTable, `d${currentDepth + 1}`);
@@ -8009,7 +8009,7 @@ var init_dialect = __esm({
8009
8009
  table: targetTable,
8010
8010
  mode: isSingle2 ? "first" : "many",
8011
8011
  schema,
8012
- queryConfig: join135,
8012
+ queryConfig: join136,
8013
8013
  tableConfig: schema[relation.targetTableName],
8014
8014
  relationWhere: filter,
8015
8015
  isNested: true,
@@ -8023,7 +8023,7 @@ var init_dialect = __esm({
8023
8023
  key: k2,
8024
8024
  selection: innerQuery.selection,
8025
8025
  isArray: !isSingle2,
8026
- isOptional: (relation.optional ?? false) || join135 !== true && !!join135.where
8026
+ isOptional: (relation.optional ?? false) || join136 !== true && !!join136.where
8027
8027
  });
8028
8028
  const jsonColumns = sql.join(innerQuery.selection.map((s3) => {
8029
8029
  return sql`${sql.raw(this.escapeString(s3.key))}, ${s3.selection ? sql`${jsonb2}(${sql.identifier(s3.key)})` : sql.identifier(s3.key)}`;
@@ -8422,7 +8422,7 @@ var init_update = __esm({
8422
8422
  createJoin(joinType) {
8423
8423
  return ((table, on) => {
8424
8424
  const tableName = getTableLikeName(table);
8425
- if (typeof tableName === "string" && this.config.joins.some((join135) => join135.alias === tableName)) throw new Error(`Alias "${tableName}" is already used in this query`);
8425
+ if (typeof tableName === "string" && this.config.joins.some((join136) => join136.alias === tableName)) throw new Error(`Alias "${tableName}" is already used in this query`);
8426
8426
  if (typeof on === "function") {
8427
8427
  const from = this.config.from ? is(table, SQLiteTable) ? table[Table.Symbol.Columns] : is(table, Subquery) ? table._.selectedFields : is(table, SQLiteViewBase) ? table[ViewBaseConfig].selectedFields : void 0 : void 0;
8428
8428
  on = on(new Proxy(this.config.table[Table.Symbol.Columns], new SelectionProxyHandler({
@@ -15271,48 +15271,6 @@ function runBrainMigrations(nativeDb, db) {
15271
15271
  }
15272
15272
  reconcileJournal(nativeDb, migrationsFolder, "brain_decisions", "brain");
15273
15273
  migrateWithRetry(db, migrationsFolder, nativeDb, "brain_decisions", "brain");
15274
- ensureColumns(
15275
- nativeDb,
15276
- "brain_page_nodes",
15277
- [
15278
- { name: "quality_score", ddl: "real DEFAULT 0.5" },
15279
- { name: "content_hash", ddl: "text" },
15280
- { name: "last_activity_at", ddl: "text" },
15281
- { name: "updated_at", ddl: "text" }
15282
- ],
15283
- "brain"
15284
- );
15285
- for (const table of [
15286
- "brain_decisions",
15287
- "brain_patterns",
15288
- "brain_learnings",
15289
- "brain_observations"
15290
- ]) {
15291
- ensureColumns(nativeDb, table, [{ name: "quality_score", ddl: "real" }], "brain");
15292
- }
15293
- for (const table of [
15294
- "brain_decisions",
15295
- "brain_patterns",
15296
- "brain_learnings",
15297
- "brain_observations"
15298
- ]) {
15299
- ensureColumns(
15300
- nativeDb,
15301
- table,
15302
- [
15303
- { name: "memory_tier", ddl: "text DEFAULT 'short'" },
15304
- { name: "memory_type", ddl: "text DEFAULT 'episodic'" },
15305
- { name: "verified", ddl: "integer NOT NULL DEFAULT 0" },
15306
- // valid_at uses nullable text (no datetime('now') default) because
15307
- // ALTER TABLE ADD COLUMN with non-constant defaults fails on non-empty tables.
15308
- { name: "valid_at", ddl: "text" },
15309
- { name: "invalid_at", ddl: "text" },
15310
- { name: "source_confidence", ddl: "text DEFAULT 'agent'" },
15311
- { name: "citation_count", ddl: "integer NOT NULL DEFAULT 0" }
15312
- ],
15313
- "brain"
15314
- );
15315
- }
15316
15274
  if (tableExists(nativeDb, "brain_page_edges")) {
15317
15275
  nativeDb.prepare(
15318
15276
  `UPDATE brain_page_edges
@@ -16567,7 +16525,8 @@ async function createSqliteDataAccessor(cwd) {
16567
16525
  ["createdBy", "createdBy"],
16568
16526
  ["modifiedBy", "modifiedBy"],
16569
16527
  ["sessionId", "sessionId"],
16570
- ["assignee", "assignee"]
16528
+ ["assignee", "assignee"],
16529
+ ["pipelineStage", "pipelineStage"]
16571
16530
  ];
16572
16531
  for (const [key, col] of fieldMap) {
16573
16532
  if (fields[key] !== void 0) {
@@ -46914,13 +46873,147 @@ var init_graph_memory_bridge = __esm({
46914
46873
  }
46915
46874
  });
46916
46875
 
46876
+ // packages/core/src/memory/brain-plasticity-class.ts
46877
+ function upgradePlasticityClass(currentClass, event) {
46878
+ const current = currentClass ?? "static";
46879
+ if (current === "stdp") return "stdp";
46880
+ if (event === "stdp") return "stdp";
46881
+ if (event === "hebbian") {
46882
+ return current === "static" ? "hebbian" : current;
46883
+ }
46884
+ return current;
46885
+ }
46886
+ function computeStabilityScore(reinforcementCount, lastReinforcedAt, now2 = Date.now()) {
46887
+ if (!reinforcementCount || reinforcementCount <= 0) return null;
46888
+ if (!lastReinforcedAt) return null;
46889
+ const rcTerm = Math.tanh(reinforcementCount / 10);
46890
+ const lastReinforcedMs = new Date(lastReinforcedAt).getTime();
46891
+ const ageMs = now2 - lastReinforcedMs;
46892
+ const ageDays = ageMs / (24 * 60 * 60 * 1e3);
46893
+ const decayTerm = Math.exp(-ageDays / 30);
46894
+ const stability = rcTerm * decayTerm;
46895
+ return Math.max(0, Math.min(1, stability));
46896
+ }
46897
+ var init_brain_plasticity_class = __esm({
46898
+ "packages/core/src/memory/brain-plasticity-class.ts"() {
46899
+ "use strict";
46900
+ }
46901
+ });
46902
+
46917
46903
  // packages/core/src/memory/brain-stdp.ts
46918
46904
  var brain_stdp_exports = {};
46919
46905
  __export(brain_stdp_exports, {
46906
+ applyHomeostaticDecay: () => applyHomeostaticDecay,
46920
46907
  applyStdpPlasticity: () => applyStdpPlasticity,
46921
- getPlasticityStats: () => getPlasticityStats
46922
- });
46923
- async function applyStdpPlasticity(projectRoot, sessionWindowMs = 5 * 60 * 1e3) {
46908
+ backfillRewardSignals: () => backfillRewardSignals,
46909
+ computeTau: () => computeTau,
46910
+ getPlasticityStats: () => getPlasticityStats,
46911
+ shouldRunPlasticity: () => shouldRunPlasticity
46912
+ });
46913
+ function computeTau(deltaT) {
46914
+ if (deltaT <= TAU_NEAR_THRESHOLD_MS) return TAU_NEAR_MS;
46915
+ if (deltaT <= TAU_SESSION_THRESHOLD_MS) return TAU_SESSION_MS;
46916
+ return TAU_EPISODIC_MS;
46917
+ }
46918
+ function isPlasticityEventDuplicate(nativeDb, sourceNode, targetNode, kind, sessionId, withinHours = 1) {
46919
+ try {
46920
+ const cutoffIso = new Date(Date.now() - withinHours * 60 * 60 * 1e3).toISOString().replace("T", " ").slice(0, 19);
46921
+ const db = nativeDb;
46922
+ const stmt = db.prepare(
46923
+ `SELECT 1 FROM brain_plasticity_events
46924
+ WHERE source_node = ? AND target_node = ? AND kind = ?
46925
+ AND session_id = ?
46926
+ AND timestamp > ?
46927
+ LIMIT 1`
46928
+ );
46929
+ const result = stmt.get(sourceNode, targetNode, kind, sessionId, cutoffIso);
46930
+ return result !== void 0;
46931
+ } catch {
46932
+ return false;
46933
+ }
46934
+ }
46935
+ function hasMinimumRetrievalsSinceLastPlasticity(nativeDb, minCount = 2, sessionId = null) {
46936
+ try {
46937
+ const db = nativeDb;
46938
+ const lastPlasticityStmt = db.prepare(
46939
+ `SELECT MAX(timestamp) as last_time FROM brain_plasticity_events
46940
+ WHERE session_id = ?`
46941
+ );
46942
+ const lastPlasticityRow = lastPlasticityStmt.get(sessionId);
46943
+ const lastTime = lastPlasticityRow?.last_time ?? null;
46944
+ let newRetrievalCount;
46945
+ if (lastTime === null) {
46946
+ const countRow = db.prepare(
46947
+ `SELECT COUNT(*) as cnt FROM brain_retrieval_log
46948
+ WHERE session_id = ?`
46949
+ ).get(sessionId);
46950
+ newRetrievalCount = countRow?.cnt ?? 0;
46951
+ } else {
46952
+ const countRow = db.prepare(
46953
+ `SELECT COUNT(*) as cnt FROM brain_retrieval_log
46954
+ WHERE session_id = ? AND created_at > ?`
46955
+ ).get(sessionId, lastTime);
46956
+ newRetrievalCount = countRow?.cnt ?? 0;
46957
+ }
46958
+ return newRetrievalCount >= minCount;
46959
+ } catch {
46960
+ return true;
46961
+ }
46962
+ }
46963
+ async function shouldRunPlasticity(projectRoot, sessionId = null, minRetrievalsForPlasticity = 2) {
46964
+ const { getBrainDb: getBrainDb2, getBrainNativeDb: getBrainNativeDb2 } = await Promise.resolve().then(() => (init_brain_sqlite(), brain_sqlite_exports));
46965
+ await getBrainDb2(projectRoot);
46966
+ const nativeDb = getBrainNativeDb2();
46967
+ if (!nativeDb) return true;
46968
+ const hasMinimum = hasMinimumRetrievalsSinceLastPlasticity(
46969
+ nativeDb,
46970
+ minRetrievalsForPlasticity,
46971
+ sessionId
46972
+ );
46973
+ if (!hasMinimum) {
46974
+ const count5 = (() => {
46975
+ try {
46976
+ const db = nativeDb;
46977
+ const lastPlasticityStmt = db.prepare(
46978
+ `SELECT MAX(timestamp) as last_time FROM brain_plasticity_events WHERE session_id = ?`
46979
+ );
46980
+ const lastPlasticityRow = lastPlasticityStmt.get(sessionId);
46981
+ const lastTime = lastPlasticityRow?.last_time ?? null;
46982
+ if (lastTime === null) {
46983
+ const countStmt = db.prepare(
46984
+ `SELECT COUNT(*) as cnt FROM brain_retrieval_log WHERE session_id = ?`
46985
+ );
46986
+ const countRow = countStmt.get(sessionId);
46987
+ return countRow?.cnt ?? 0;
46988
+ } else {
46989
+ const countStmt = db.prepare(
46990
+ `SELECT COUNT(*) as cnt FROM brain_retrieval_log WHERE session_id = ? AND created_at > ?`
46991
+ );
46992
+ const countRow = countStmt.get(sessionId, lastTime);
46993
+ return countRow?.cnt ?? 0;
46994
+ }
46995
+ } catch {
46996
+ return 0;
46997
+ }
46998
+ })();
46999
+ console.warn(
47000
+ `[plasticity] Minimum-pair gate: skipped STDP Step 9b (${count5} retrievals, need >=${minRetrievalsForPlasticity})`
47001
+ );
47002
+ }
47003
+ return hasMinimum;
47004
+ }
47005
+ async function applyStdpPlasticity(projectRoot, options) {
47006
+ let lookbackDays = DEFAULT_LOOKBACK_DAYS;
47007
+ let pairingWindowMs = DEFAULT_PAIRING_WINDOW_MS;
47008
+ if (typeof options === "number") {
47009
+ console.warn(
47010
+ "[brain-stdp] Deprecated: passing sessionWindowMs as a number. Use StdpPlasticityOptions { lookbackDays, pairingWindowMs } instead. (T679)"
47011
+ );
47012
+ pairingWindowMs = options;
47013
+ } else if (options !== void 0) {
47014
+ lookbackDays = options.lookbackDays ?? DEFAULT_LOOKBACK_DAYS;
47015
+ pairingWindowMs = options.pairingWindowMs ?? DEFAULT_PAIRING_WINDOW_MS;
47016
+ }
46924
47017
  const { getBrainDb: getBrainDb2, getBrainNativeDb: getBrainNativeDb2 } = await Promise.resolve().then(() => (init_brain_sqlite(), brain_sqlite_exports));
46925
47018
  await getBrainDb2(projectRoot);
46926
47019
  const nativeDb = getBrainNativeDb2();
@@ -46928,7 +47021,8 @@ async function applyStdpPlasticity(projectRoot, sessionWindowMs = 5 * 60 * 1e3)
46928
47021
  ltpEvents: 0,
46929
47022
  ltdEvents: 0,
46930
47023
  edgesCreated: 0,
46931
- pairsExamined: 0
47024
+ pairsExamined: 0,
47025
+ rewardModulatedEvents: 0
46932
47026
  };
46933
47027
  if (!nativeDb) return result;
46934
47028
  try {
@@ -46942,14 +47036,14 @@ async function applyStdpPlasticity(projectRoot, sessionWindowMs = 5 * 60 * 1e3)
46942
47036
  return result;
46943
47037
  }
46944
47038
  const now2 = Date.now();
46945
- const cutoffMs = now2 - sessionWindowMs;
47039
+ const cutoffMs = now2 - lookbackDays * 24 * 60 * 60 * 1e3;
46946
47040
  const cutoffIso = new Date(cutoffMs).toISOString().replace("T", " ").slice(0, 19);
46947
47041
  const nowIso = new Date(now2).toISOString().replace("T", " ").slice(0, 19);
46948
47042
  let logRows = [];
46949
47043
  try {
46950
47044
  logRows = typedAll(
46951
47045
  nativeDb.prepare(
46952
- `SELECT id, entry_ids, created_at, retrieval_order, delta_ms
47046
+ `SELECT id, entry_ids, created_at, retrieval_order, delta_ms, session_id, reward_signal
46953
47047
  FROM brain_retrieval_log
46954
47048
  WHERE created_at >= ?
46955
47049
  ORDER BY created_at ASC, id ASC
@@ -46970,68 +47064,318 @@ async function applyStdpPlasticity(projectRoot, sessionWindowMs = 5 * 60 * 1e3)
46970
47064
  } catch {
46971
47065
  continue;
46972
47066
  }
47067
+ if (!Array.isArray(ids)) continue;
46973
47068
  const rowTime = (/* @__PURE__ */ new Date(row.created_at.replace(" ", "T") + "Z")).getTime();
46974
47069
  for (const rawId of ids) {
47070
+ if (typeof rawId !== "string" || rawId.length === 0) continue;
46975
47071
  const entryId = rawId.includes(":") ? rawId : `observation:${rawId}`;
46976
47072
  spikes.push({
46977
47073
  entryId,
46978
47074
  rowId: row.id,
46979
47075
  retrievedAt: rowTime,
46980
- order: row.retrieval_order ?? globalOrder
47076
+ order: row.retrieval_order ?? globalOrder,
47077
+ sessionId: row.session_id ?? null,
47078
+ rewardSignal: row.reward_signal ?? null
46981
47079
  });
46982
47080
  globalOrder++;
46983
47081
  }
46984
47082
  }
47083
+ if (spikes.length < 2) return result;
46985
47084
  spikes.sort((a, b2) => a.retrievedAt - b2.retrievedAt || a.order - b2.order);
47085
+ const MAX_PAIRS_PER_SESSION = 50;
47086
+ const sessionBucketMap = /* @__PURE__ */ new Map();
47087
+ for (const spike of spikes) {
47088
+ const key = spike.sessionId ?? "null";
47089
+ const bucket = sessionBucketMap.get(key);
47090
+ if (bucket !== void 0) {
47091
+ bucket.push(spike);
47092
+ } else {
47093
+ sessionBucketMap.set(key, [spike]);
47094
+ }
47095
+ }
47096
+ const orderedBuckets = Array.from(sessionBucketMap.entries()).sort(
47097
+ ([, a], [, b2]) => (a[0]?.retrievedAt ?? 0) - (b2[0]?.retrievedAt ?? 0)
47098
+ );
47099
+ const workingSpikes = [];
47100
+ for (let bi2 = 0; bi2 < orderedBuckets.length; bi2++) {
47101
+ const [, bucketSpikes] = orderedBuckets[bi2];
47102
+ workingSpikes.push(...bucketSpikes);
47103
+ }
47104
+ const withinSessionIndex = /* @__PURE__ */ new Map();
47105
+ const sessionSizeMap = /* @__PURE__ */ new Map();
47106
+ for (const [, bucketSpikes] of orderedBuckets) {
47107
+ for (let idx = 0; idx < bucketSpikes.length; idx++) {
47108
+ const spike = bucketSpikes[idx];
47109
+ withinSessionIndex.set(spike, idx);
47110
+ sessionSizeMap.set(spike.sessionId ?? "null", bucketSpikes.length);
47111
+ }
47112
+ }
46986
47113
  const prepareGetEdge = nativeDb.prepare(
46987
- `SELECT weight FROM brain_page_edges
47114
+ `SELECT weight, reinforcement_count, last_reinforced_at, plasticity_class, depression_count, last_depressed_at
47115
+ FROM brain_page_edges
47116
+ WHERE from_id = ? AND to_id = ? AND edge_type = 'co_retrieved'`
47117
+ );
47118
+ const prepareUpdateEdgeLtp = nativeDb.prepare(
47119
+ `UPDATE brain_page_edges
47120
+ SET weight = MAX(?, MIN(?, weight + ?)),
47121
+ reinforcement_count = reinforcement_count + 1,
47122
+ last_reinforced_at = ?,
47123
+ plasticity_class = ?,
47124
+ stability_score = ?
46988
47125
  WHERE from_id = ? AND to_id = ? AND edge_type = 'co_retrieved'`
46989
47126
  );
46990
- const prepareUpdateEdge = nativeDb.prepare(
47127
+ const prepareUpdateEdgeLtd = nativeDb.prepare(
46991
47128
  `UPDATE brain_page_edges
46992
- SET weight = MAX(?, MIN(?, weight + ?))
47129
+ SET weight = MAX(?, MIN(?, weight + ?)),
47130
+ depression_count = depression_count + 1,
47131
+ last_depressed_at = ?,
47132
+ plasticity_class = ?,
47133
+ stability_score = ?
46993
47134
  WHERE from_id = ? AND to_id = ? AND edge_type = 'co_retrieved'`
46994
47135
  );
46995
47136
  const prepareInsertEdge = nativeDb.prepare(
46996
47137
  `INSERT OR IGNORE INTO brain_page_edges
46997
- (from_id, to_id, edge_type, weight, provenance, created_at)
46998
- VALUES (?, ?, 'co_retrieved', ?, 'plasticity:stdp-ltp', ?)`
47138
+ (from_id, to_id, edge_type, weight, provenance, reinforcement_count, last_reinforced_at, plasticity_class, stability_score, created_at)
47139
+ VALUES (?, ?, 'co_retrieved', ?, 'plasticity:stdp-ltp', 1, ?, 'stdp', ?, ?)`
46999
47140
  );
47000
47141
  const prepareLogEvent = nativeDb.prepare(
47001
47142
  `INSERT INTO brain_plasticity_events
47002
- (source_node, target_node, delta_w, kind, timestamp)
47003
- VALUES (?, ?, ?, ?, ?)`
47143
+ (source_node, target_node, delta_w, kind, timestamp,
47144
+ session_id, retrieval_log_id, weight_before, weight_after, delta_t_ms)
47145
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
47004
47146
  );
47005
- for (let i = 0; i < spikes.length; i++) {
47006
- const spikeA = spikes[i];
47007
- for (let j2 = i + 1; j2 < spikes.length; j2++) {
47008
- const spikeB = spikes[j2];
47147
+ let prepareLogWeightHistory = null;
47148
+ try {
47149
+ nativeDb.prepare("SELECT 1 FROM brain_weight_history LIMIT 1").get();
47150
+ prepareLogWeightHistory = nativeDb.prepare(
47151
+ `INSERT INTO brain_weight_history
47152
+ (edge_from_id, edge_to_id, edge_type, weight_before, weight_after,
47153
+ delta_weight, event_kind, source_plasticity_event_id, retrieval_log_id,
47154
+ reward_signal, changed_at)
47155
+ VALUES (?, ?, 'co_retrieved', ?, ?, ?, ?, ?, ?, ?, ?)`
47156
+ );
47157
+ } catch {
47158
+ }
47159
+ for (let i = 0; i < workingSpikes.length; i++) {
47160
+ const spikeA = workingSpikes[i];
47161
+ const sessionKeyA = spikeA.sessionId ?? "null";
47162
+ const sessionSizeA = sessionSizeMap.get(sessionKeyA) ?? 1;
47163
+ const posInSessionA = withinSessionIndex.get(spikeA) ?? 0;
47164
+ const canCrossSession = posInSessionA >= sessionSizeA - MAX_PAIRS_PER_SESSION;
47165
+ for (let j2 = i + 1; j2 < workingSpikes.length; j2++) {
47166
+ const spikeB = workingSpikes[j2];
47009
47167
  const deltaT = spikeB.retrievedAt - spikeA.retrievedAt;
47010
- if (deltaT > sessionWindowMs) break;
47168
+ if (deltaT > pairingWindowMs) break;
47011
47169
  if (spikeA.entryId === spikeB.entryId) continue;
47170
+ const sessionKeyB = spikeB.sessionId ?? "null";
47171
+ if (sessionKeyA !== sessionKeyB && !canCrossSession) {
47172
+ continue;
47173
+ }
47012
47174
  result.pairsExamined++;
47013
- const deltaW = A_PRE * Math.exp(-deltaT / TAU_PRE_MS);
47175
+ const tau = computeTau(deltaT);
47176
+ let deltaW = A_PRE * Math.exp(-deltaT / tau);
47014
47177
  if (deltaW < 1e-6) continue;
47178
+ const eventSessionId = spikeA.sessionId ?? null;
47179
+ const eventRewardSignal = spikeA.rewardSignal ?? null;
47180
+ let wasRewardModulated = false;
47181
+ if (eventRewardSignal !== null) {
47182
+ const r = eventRewardSignal;
47183
+ deltaW = Math.min(deltaW * (1 + r), 2 * A_PRE);
47184
+ deltaW = Math.max(deltaW, 0);
47185
+ wasRewardModulated = true;
47186
+ }
47187
+ if (deltaW < 1e-6) {
47188
+ if (wasRewardModulated) result.rewardModulatedEvents++;
47189
+ continue;
47190
+ }
47015
47191
  const existingEdge = prepareGetEdge.get(spikeA.entryId, spikeB.entryId);
47192
+ let ltpEventId = null;
47016
47193
  try {
47017
47194
  if (existingEdge !== void 0) {
47018
- prepareUpdateEdge.run(WEIGHT_MIN, WEIGHT_MAX, deltaW, spikeA.entryId, spikeB.entryId);
47195
+ const currentWeight = existingEdge.weight;
47196
+ const newWeight = Math.max(WEIGHT_MIN, Math.min(WEIGHT_MAX, currentWeight + deltaW));
47197
+ const upgradedClass = upgradePlasticityClass(existingEdge.plasticity_class, "stdp");
47198
+ const newRcCount = (existingEdge.reinforcement_count ?? 0) + 1;
47199
+ const stability = computeStabilityScore(newRcCount, nowIso, now2);
47200
+ prepareUpdateEdgeLtp.run(
47201
+ WEIGHT_MIN,
47202
+ WEIGHT_MAX,
47203
+ deltaW,
47204
+ nowIso,
47205
+ // last_reinforced_at
47206
+ upgradedClass,
47207
+ stability,
47208
+ spikeA.entryId,
47209
+ spikeB.entryId
47210
+ );
47211
+ const isDuplicate = isPlasticityEventDuplicate(
47212
+ nativeDb,
47213
+ spikeA.entryId,
47214
+ spikeB.entryId,
47215
+ "ltp",
47216
+ eventSessionId,
47217
+ 1
47218
+ // within 1 hour
47219
+ );
47220
+ if (isDuplicate) {
47221
+ result.ltpEvents++;
47222
+ if (wasRewardModulated) result.rewardModulatedEvents++;
47223
+ continue;
47224
+ }
47225
+ const evtStmt = prepareLogEvent.run(
47226
+ spikeA.entryId,
47227
+ spikeB.entryId,
47228
+ deltaW,
47229
+ "ltp",
47230
+ nowIso,
47231
+ eventSessionId,
47232
+ spikeA.rowId,
47233
+ currentWeight,
47234
+ newWeight,
47235
+ deltaT
47236
+ );
47237
+ ltpEventId = evtStmt.lastInsertRowid != null ? Number(evtStmt.lastInsertRowid) : null;
47238
+ if (prepareLogWeightHistory) {
47239
+ prepareLogWeightHistory.run(
47240
+ spikeA.entryId,
47241
+ spikeB.entryId,
47242
+ currentWeight,
47243
+ newWeight,
47244
+ deltaW,
47245
+ "ltp",
47246
+ ltpEventId,
47247
+ spikeA.rowId,
47248
+ eventRewardSignal,
47249
+ nowIso
47250
+ );
47251
+ }
47019
47252
  } else {
47020
- const initialWeight = Math.min(WEIGHT_MAX, deltaW);
47021
- prepareInsertEdge.run(spikeA.entryId, spikeB.entryId, initialWeight, nowIso);
47253
+ const noveltyBoostedWeight = deltaW * K_NOVELTY;
47254
+ const initialWeight = Math.min(
47255
+ WEIGHT_MAX,
47256
+ Math.min(A_PRE * K_NOVELTY, noveltyBoostedWeight)
47257
+ );
47258
+ const stability = computeStabilityScore(1, nowIso, now2);
47259
+ prepareInsertEdge.run(
47260
+ spikeA.entryId,
47261
+ spikeB.entryId,
47262
+ initialWeight,
47263
+ nowIso,
47264
+ // last_reinforced_at
47265
+ stability,
47266
+ nowIso
47267
+ );
47022
47268
  result.edgesCreated++;
47269
+ const evtStmt = prepareLogEvent.run(
47270
+ spikeA.entryId,
47271
+ spikeB.entryId,
47272
+ initialWeight,
47273
+ "ltp",
47274
+ nowIso,
47275
+ eventSessionId,
47276
+ spikeA.rowId,
47277
+ null,
47278
+ initialWeight,
47279
+ deltaT
47280
+ );
47281
+ ltpEventId = evtStmt.lastInsertRowid != null ? Number(evtStmt.lastInsertRowid) : null;
47282
+ if (prepareLogWeightHistory) {
47283
+ prepareLogWeightHistory.run(
47284
+ spikeA.entryId,
47285
+ spikeB.entryId,
47286
+ null,
47287
+ initialWeight,
47288
+ initialWeight,
47289
+ "ltp",
47290
+ ltpEventId,
47291
+ spikeA.rowId,
47292
+ eventRewardSignal,
47293
+ nowIso
47294
+ );
47295
+ }
47023
47296
  }
47024
- prepareLogEvent.run(spikeA.entryId, spikeB.entryId, deltaW, "ltp", nowIso);
47025
47297
  result.ltpEvents++;
47298
+ if (wasRewardModulated) result.rewardModulatedEvents++;
47026
47299
  } catch {
47027
47300
  }
47028
- const deltaWNeg = -(A_POST * Math.exp(-deltaT / TAU_POST_MS));
47301
+ let deltaWNeg = -(A_POST * Math.exp(-deltaT / tau));
47302
+ let ltdWasRewardModulated = false;
47303
+ if (eventRewardSignal !== null) {
47304
+ const r = eventRewardSignal;
47305
+ deltaWNeg = Math.max(deltaWNeg * (1 - r), -2 * A_POST);
47306
+ ltdWasRewardModulated = true;
47307
+ }
47029
47308
  const existingReverseEdge = prepareGetEdge.get(spikeB.entryId, spikeA.entryId);
47030
47309
  if (existingReverseEdge !== void 0 && Math.abs(deltaWNeg) >= 1e-6) {
47031
47310
  try {
47032
- prepareUpdateEdge.run(WEIGHT_MIN, WEIGHT_MAX, deltaWNeg, spikeB.entryId, spikeA.entryId);
47033
- prepareLogEvent.run(spikeB.entryId, spikeA.entryId, deltaWNeg, "ltd", nowIso);
47311
+ const currentReverseWeight = existingReverseEdge.weight;
47312
+ const newReverseWeight = Math.max(
47313
+ WEIGHT_MIN,
47314
+ Math.min(WEIGHT_MAX, currentReverseWeight + deltaWNeg)
47315
+ );
47316
+ const upgradedClass = upgradePlasticityClass(
47317
+ existingReverseEdge.plasticity_class,
47318
+ "stdp"
47319
+ );
47320
+ const stability = computeStabilityScore(
47321
+ existingReverseEdge.reinforcement_count,
47322
+ existingReverseEdge.last_reinforced_at,
47323
+ now2
47324
+ );
47325
+ prepareUpdateEdgeLtd.run(
47326
+ WEIGHT_MIN,
47327
+ WEIGHT_MAX,
47328
+ deltaWNeg,
47329
+ nowIso,
47330
+ // last_depressed_at
47331
+ upgradedClass,
47332
+ stability,
47333
+ spikeB.entryId,
47334
+ spikeA.entryId
47335
+ );
47336
+ const isLtdDuplicate = isPlasticityEventDuplicate(
47337
+ nativeDb,
47338
+ spikeB.entryId,
47339
+ spikeA.entryId,
47340
+ "ltd",
47341
+ eventSessionId,
47342
+ 1
47343
+ // within 1 hour
47344
+ );
47345
+ if (isLtdDuplicate) {
47346
+ result.ltdEvents++;
47347
+ if (ltdWasRewardModulated) result.rewardModulatedEvents++;
47348
+ continue;
47349
+ }
47350
+ const ltdEvtStmt = prepareLogEvent.run(
47351
+ spikeB.entryId,
47352
+ spikeA.entryId,
47353
+ deltaWNeg,
47354
+ "ltd",
47355
+ nowIso,
47356
+ eventSessionId,
47357
+ spikeB.rowId,
47358
+ currentReverseWeight,
47359
+ newReverseWeight,
47360
+ deltaT
47361
+ );
47362
+ const ltdEventId = ltdEvtStmt.lastInsertRowid != null ? Number(ltdEvtStmt.lastInsertRowid) : null;
47363
+ if (prepareLogWeightHistory) {
47364
+ prepareLogWeightHistory.run(
47365
+ spikeB.entryId,
47366
+ spikeA.entryId,
47367
+ currentReverseWeight,
47368
+ newReverseWeight,
47369
+ deltaWNeg,
47370
+ "ltd",
47371
+ ltdEventId,
47372
+ spikeB.rowId,
47373
+ eventRewardSignal,
47374
+ nowIso
47375
+ );
47376
+ }
47034
47377
  result.ltdEvents++;
47378
+ if (ltdWasRewardModulated) result.rewardModulatedEvents++;
47035
47379
  } catch {
47036
47380
  }
47037
47381
  }
@@ -47101,15 +47445,240 @@ async function getPlasticityStats(projectRoot, limit = 20) {
47101
47445
  }))
47102
47446
  };
47103
47447
  }
47104
- var TAU_PRE_MS, TAU_POST_MS, A_PRE, A_POST, WEIGHT_MIN, WEIGHT_MAX;
47448
+ async function backfillRewardSignals(projectRoot, sessionId, lookbackDays = 30) {
47449
+ const result = { rowsLabeled: 0, rowsSkipped: 0 };
47450
+ if (!sessionId) {
47451
+ return result;
47452
+ }
47453
+ if (sessionId.startsWith("ses_backfill_")) {
47454
+ return result;
47455
+ }
47456
+ let taskRows = [];
47457
+ try {
47458
+ const { getDb: getDb4 } = await Promise.resolve().then(() => (init_sqlite2(), sqlite_exports));
47459
+ const tasksDb = await getDb4(projectRoot);
47460
+ const { tasks: tasks2 } = await Promise.resolve().then(() => (init_tasks_schema(), tasks_schema_exports));
47461
+ const { and: and15, eq: eq18, inArray: inArray8, gte: gte4, or: or7, isNotNull: isNotNull3 } = await import("drizzle-orm");
47462
+ const cutoffTs = new Date(Date.now() - lookbackDays * 24 * 60 * 60 * 1e3).toISOString().replace("T", " ").slice(0, 19);
47463
+ const rawRows = await tasksDb.select({
47464
+ id: tasks2.id,
47465
+ status: tasks2.status,
47466
+ verificationJson: tasks2.verificationJson,
47467
+ completedAt: tasks2.completedAt,
47468
+ cancelledAt: tasks2.cancelledAt
47469
+ }).from(tasks2).where(
47470
+ and15(
47471
+ eq18(tasks2.sessionId, sessionId),
47472
+ inArray8(tasks2.status, ["done", "cancelled"]),
47473
+ or7(
47474
+ and15(isNotNull3(tasks2.completedAt), gte4(tasks2.completedAt, cutoffTs)),
47475
+ and15(isNotNull3(tasks2.cancelledAt), gte4(tasks2.cancelledAt, cutoffTs))
47476
+ )
47477
+ )
47478
+ ).all();
47479
+ taskRows = rawRows.map((r) => ({
47480
+ id: r.id,
47481
+ status: r.status,
47482
+ verificationJson: r.verificationJson ?? null,
47483
+ completedAt: r.completedAt ?? null,
47484
+ cancelledAt: r.cancelledAt ?? null
47485
+ }));
47486
+ } catch {
47487
+ return result;
47488
+ }
47489
+ if (taskRows.length === 0) {
47490
+ return result;
47491
+ }
47492
+ let sessionReward = null;
47493
+ function deriveTaskReward(task) {
47494
+ if (task.status === "cancelled") {
47495
+ return -0.5;
47496
+ }
47497
+ let verificationPassed = false;
47498
+ if (task.verificationJson) {
47499
+ try {
47500
+ const v2 = JSON.parse(task.verificationJson);
47501
+ verificationPassed = v2.passed === true;
47502
+ } catch {
47503
+ }
47504
+ }
47505
+ return verificationPassed ? 1 : 0.5;
47506
+ }
47507
+ for (const task of taskRows) {
47508
+ const taskReward = deriveTaskReward(task);
47509
+ if (sessionReward === null || taskReward > sessionReward) {
47510
+ sessionReward = taskReward;
47511
+ }
47512
+ }
47513
+ if (sessionReward === null) {
47514
+ return result;
47515
+ }
47516
+ try {
47517
+ const { getBrainDb: getBrainDb2, getBrainNativeDb: getBrainNativeDb2 } = await Promise.resolve().then(() => (init_brain_sqlite(), brain_sqlite_exports));
47518
+ await getBrainDb2(projectRoot);
47519
+ const nativeDb = getBrainNativeDb2();
47520
+ if (!nativeDb) return result;
47521
+ try {
47522
+ nativeDb.prepare("SELECT 1 FROM brain_retrieval_log LIMIT 1").get();
47523
+ } catch {
47524
+ return result;
47525
+ }
47526
+ const updateResult = nativeDb.prepare(
47527
+ `UPDATE brain_retrieval_log
47528
+ SET reward_signal = ?
47529
+ WHERE session_id = ?
47530
+ AND reward_signal IS NULL`
47531
+ ).run(sessionReward, sessionId);
47532
+ const updatedCount = typeof updateResult.changes === "number" ? updateResult.changes : 0;
47533
+ result.rowsLabeled = updatedCount;
47534
+ const skipRow = nativeDb.prepare(
47535
+ `SELECT COUNT(*) AS cnt FROM brain_retrieval_log
47536
+ WHERE session_id = ? AND reward_signal IS NOT NULL`
47537
+ ).get(sessionId);
47538
+ result.rowsSkipped = skipRow?.cnt ?? 0;
47539
+ let modulatorsExist = false;
47540
+ try {
47541
+ nativeDb.prepare("SELECT 1 FROM brain_modulators LIMIT 1").get();
47542
+ modulatorsExist = true;
47543
+ } catch {
47544
+ }
47545
+ if (modulatorsExist && updatedCount > 0) {
47546
+ const insertModulator = nativeDb.prepare(
47547
+ `INSERT INTO brain_modulators
47548
+ (modulator_type, valence, magnitude, source_event_id, session_id, description)
47549
+ VALUES (?, ?, 1.0, ?, ?, ?)`
47550
+ );
47551
+ for (const task of taskRows) {
47552
+ const taskReward = deriveTaskReward(task);
47553
+ let modulatorType;
47554
+ let description;
47555
+ if (task.status === "cancelled") {
47556
+ modulatorType = "task_cancelled";
47557
+ description = `Task ${task.id} cancelled`;
47558
+ } else if (taskReward >= 1) {
47559
+ modulatorType = "task_verified";
47560
+ description = `Task ${task.id} completed and verified`;
47561
+ } else {
47562
+ modulatorType = "task_completed";
47563
+ description = `Task ${task.id} completed (unverified)`;
47564
+ }
47565
+ try {
47566
+ insertModulator.run(modulatorType, taskReward, task.id, sessionId, description);
47567
+ } catch {
47568
+ }
47569
+ }
47570
+ }
47571
+ } catch {
47572
+ }
47573
+ return result;
47574
+ }
47575
+ async function applyHomeostaticDecay(projectRoot, options) {
47576
+ const decayRatePerDay = options?.decayRatePerDay ?? 0.02;
47577
+ const gracePeriodDays = options?.gracePeriodDays ?? 7;
47578
+ const pruneThreshold = options?.pruneThreshold ?? 0.05;
47579
+ const result = { edgesDecayed: 0, edgesPruned: 0 };
47580
+ const { getBrainDb: getBrainDb2, getBrainNativeDb: getBrainNativeDb2 } = await Promise.resolve().then(() => (init_brain_sqlite(), brain_sqlite_exports));
47581
+ await getBrainDb2(projectRoot);
47582
+ const nativeDb = getBrainNativeDb2();
47583
+ if (!nativeDb) return result;
47584
+ try {
47585
+ nativeDb.prepare("SELECT 1 FROM brain_page_edges LIMIT 1").get();
47586
+ } catch {
47587
+ return result;
47588
+ }
47589
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
47590
+ let weightHistoryExists = false;
47591
+ try {
47592
+ nativeDb.prepare("SELECT 1 FROM brain_weight_history LIMIT 1").get();
47593
+ weightHistoryExists = true;
47594
+ } catch {
47595
+ }
47596
+ let candidates = [];
47597
+ try {
47598
+ candidates = typedAll(
47599
+ nativeDb.prepare(
47600
+ `SELECT from_id, to_id, edge_type, weight, plasticity_class, last_reinforced_at
47601
+ FROM brain_page_edges
47602
+ WHERE plasticity_class IN ('hebbian', 'stdp')
47603
+ AND last_reinforced_at IS NOT NULL
47604
+ AND (julianday('now') - julianday(last_reinforced_at)) > ?`
47605
+ ),
47606
+ gracePeriodDays
47607
+ );
47608
+ } catch {
47609
+ return result;
47610
+ }
47611
+ if (candidates.length === 0) return result;
47612
+ const prepareUpdateWeight = nativeDb.prepare(
47613
+ `UPDATE brain_page_edges
47614
+ SET weight = ?
47615
+ WHERE from_id = ? AND to_id = ? AND edge_type = ?`
47616
+ );
47617
+ const prepareDeleteEdge = nativeDb.prepare(
47618
+ `DELETE FROM brain_page_edges
47619
+ WHERE from_id = ? AND to_id = ? AND edge_type = ?`
47620
+ );
47621
+ let prepareInsertHistory = null;
47622
+ if (weightHistoryExists) {
47623
+ try {
47624
+ prepareInsertHistory = nativeDb.prepare(
47625
+ `INSERT INTO brain_weight_history
47626
+ (edge_from_id, edge_to_id, edge_type, weight_before, weight_after,
47627
+ delta_weight, event_kind, changed_at)
47628
+ VALUES (?, ?, ?, ?, 0.0, ?, 'prune', ?)`
47629
+ );
47630
+ } catch {
47631
+ }
47632
+ }
47633
+ const nowMs = Date.now();
47634
+ for (const edge of candidates) {
47635
+ try {
47636
+ const lastReinforced = (/* @__PURE__ */ new Date(edge.last_reinforced_at.replace(" ", "T") + "Z")).getTime();
47637
+ const daysIdle = (nowMs - lastReinforced) / (24 * 60 * 60 * 1e3);
47638
+ const decayDays = Math.max(0, daysIdle - gracePeriodDays);
47639
+ const newWeight = edge.weight * (1 - decayRatePerDay) ** decayDays;
47640
+ if (newWeight < pruneThreshold) {
47641
+ prepareDeleteEdge.run(edge.from_id, edge.to_id, edge.edge_type);
47642
+ result.edgesPruned++;
47643
+ if (prepareInsertHistory) {
47644
+ const deltaW = -edge.weight;
47645
+ try {
47646
+ prepareInsertHistory.run(
47647
+ edge.from_id,
47648
+ edge.to_id,
47649
+ edge.edge_type,
47650
+ edge.weight,
47651
+ deltaW,
47652
+ nowIso
47653
+ );
47654
+ } catch {
47655
+ }
47656
+ }
47657
+ } else {
47658
+ prepareUpdateWeight.run(newWeight, edge.from_id, edge.to_id, edge.edge_type);
47659
+ result.edgesDecayed++;
47660
+ }
47661
+ } catch {
47662
+ }
47663
+ }
47664
+ return result;
47665
+ }
47666
+ var DEFAULT_LOOKBACK_DAYS, DEFAULT_PAIRING_WINDOW_MS, TAU_NEAR_MS, TAU_SESSION_MS, TAU_EPISODIC_MS, TAU_NEAR_THRESHOLD_MS, TAU_SESSION_THRESHOLD_MS, A_PRE, A_POST, K_NOVELTY, WEIGHT_MIN, WEIGHT_MAX;
47105
47667
  var init_brain_stdp = __esm({
47106
47668
  "packages/core/src/memory/brain-stdp.ts"() {
47107
47669
  "use strict";
47108
47670
  init_typed_query();
47109
- TAU_PRE_MS = 2e4;
47110
- TAU_POST_MS = 2e4;
47671
+ init_brain_plasticity_class();
47672
+ DEFAULT_LOOKBACK_DAYS = 30;
47673
+ DEFAULT_PAIRING_WINDOW_MS = 24 * 60 * 60 * 1e3;
47674
+ TAU_NEAR_MS = 2e4;
47675
+ TAU_SESSION_MS = 30 * 60 * 1e3;
47676
+ TAU_EPISODIC_MS = 12 * 60 * 60 * 1e3;
47677
+ TAU_NEAR_THRESHOLD_MS = 3e4;
47678
+ TAU_SESSION_THRESHOLD_MS = 2 * 60 * 60 * 1e3;
47111
47679
  A_PRE = 0.05;
47112
47680
  A_POST = 0.06;
47681
+ K_NOVELTY = 1.5;
47113
47682
  WEIGHT_MIN = 0;
47114
47683
  WEIGHT_MAX = 1;
47115
47684
  }
@@ -47358,7 +47927,8 @@ async function runTierPromotion(projectRoot) {
47358
47927
  }
47359
47928
  return { promoted, evicted };
47360
47929
  }
47361
- async function runConsolidation(projectRoot) {
47930
+ async function runConsolidation(projectRoot, sessionId, trigger = "session_end") {
47931
+ const consolidationStartMs = Date.now();
47362
47932
  const result = {
47363
47933
  deduplicated: 0,
47364
47934
  qualityRecomputed: 0,
@@ -47421,11 +47991,59 @@ async function runConsolidation(projectRoot) {
47421
47991
  console.warn("[consolidation] Step 8 graph memory bridge failed:", err);
47422
47992
  }
47423
47993
  try {
47424
- const { applyStdpPlasticity: applyStdpPlasticity2 } = await Promise.resolve().then(() => (init_brain_stdp(), brain_stdp_exports));
47425
- const stdpResult = await applyStdpPlasticity2(projectRoot);
47426
- result.stdpPlasticity = stdpResult;
47994
+ const { backfillRewardSignals: backfillRewardSignals2 } = await Promise.resolve().then(() => (init_brain_stdp(), brain_stdp_exports));
47995
+ const rewardResult = await backfillRewardSignals2(projectRoot, sessionId ?? null);
47996
+ result.rewardBackfilled = rewardResult;
47997
+ } catch (err) {
47998
+ console.warn("[consolidation] Step 9a reward backfill failed:", err);
47999
+ }
48000
+ try {
48001
+ const { applyStdpPlasticity: applyStdpPlasticity2, shouldRunPlasticity: shouldRunPlasticity2 } = await Promise.resolve().then(() => (init_brain_stdp(), brain_stdp_exports));
48002
+ const shouldRun = await shouldRunPlasticity2(projectRoot, sessionId ?? null, 2);
48003
+ if (shouldRun) {
48004
+ const stdpResult = await applyStdpPlasticity2(projectRoot);
48005
+ result.stdpPlasticity = stdpResult;
48006
+ } else {
48007
+ result.stdpPlasticity = {
48008
+ ltpEvents: 0,
48009
+ ltdEvents: 0,
48010
+ edgesCreated: 0,
48011
+ pairsExamined: 0
48012
+ };
48013
+ }
48014
+ } catch (err) {
48015
+ console.warn("[consolidation] Step 9b STDP plasticity failed:", err);
48016
+ }
48017
+ try {
48018
+ const { applyHomeostaticDecay: applyHomeostaticDecay2 } = await Promise.resolve().then(() => (init_brain_stdp(), brain_stdp_exports));
48019
+ const decayResult = await applyHomeostaticDecay2(projectRoot);
48020
+ result.homeostaticDecay = decayResult;
48021
+ } catch (err) {
48022
+ console.warn("[consolidation] Step 9c homeostatic decay failed:", err);
48023
+ }
48024
+ try {
48025
+ const { getBrainDb: getBrainDb2, getBrainNativeDb: getBrainNativeDb2 } = await Promise.resolve().then(() => (init_brain_sqlite(), brain_sqlite_exports));
48026
+ await getBrainDb2(projectRoot);
48027
+ const nativeDb = getBrainNativeDb2();
48028
+ if (nativeDb) {
48029
+ let consolidationEventsExist = false;
48030
+ try {
48031
+ nativeDb.prepare("SELECT 1 FROM brain_consolidation_events LIMIT 1").get();
48032
+ consolidationEventsExist = true;
48033
+ } catch {
48034
+ }
48035
+ if (consolidationEventsExist) {
48036
+ const durationMs = Date.now() - consolidationStartMs;
48037
+ const stepResultsJson = JSON.stringify(result);
48038
+ nativeDb.prepare(
48039
+ `INSERT INTO brain_consolidation_events
48040
+ (trigger, session_id, step_results_json, duration_ms, succeeded)
48041
+ VALUES (?, ?, ?, ?, 1)`
48042
+ ).run(trigger, sessionId ?? null, stepResultsJson, durationMs);
48043
+ }
48044
+ }
47427
48045
  } catch (err) {
47428
- console.warn("[consolidation] Step 9 STDP plasticity failed:", err);
48046
+ console.warn("[consolidation] Step 9e consolidation event log failed:", err);
47429
48047
  }
47430
48048
  return result;
47431
48049
  }
@@ -47587,7 +48205,8 @@ async function strengthenCoRetrievedEdges(projectRoot) {
47587
48205
  try {
47588
48206
  const updateStmt = nativeDb.prepare(`
47589
48207
  UPDATE brain_page_edges
47590
- SET weight = MIN(1.0, weight + 0.1)
48208
+ SET weight = MIN(1.0, weight + 0.1),
48209
+ plasticity_class = 'hebbian'
47591
48210
  WHERE from_id = ? AND to_id = ? AND edge_type = ?
47592
48211
  `);
47593
48212
  const updateResult = updateStmt.run(nodeFrom, nodeTo, EDGE_TYPES.CO_RETRIEVED);
@@ -47595,8 +48214,8 @@ async function strengthenCoRetrievedEdges(projectRoot) {
47595
48214
  if (changes === 0) {
47596
48215
  nativeDb.prepare(`
47597
48216
  INSERT OR IGNORE INTO brain_page_edges
47598
- (from_id, to_id, edge_type, weight, provenance, created_at)
47599
- VALUES (?, ?, ?, 0.3, 'consolidation:co-retrieval', ?)
48217
+ (from_id, to_id, edge_type, weight, provenance, plasticity_class, created_at)
48218
+ VALUES (?, ?, ?, 0.3, 'consolidation:co-retrieval', 'hebbian', ?)
47600
48219
  `).run(nodeFrom, nodeTo, EDGE_TYPES.CO_RETRIEVED, now2);
47601
48220
  }
47602
48221
  strengthened++;
@@ -57185,8 +57804,8 @@ async function ensureGlobalHome() {
57185
57804
  await writeFile6(globalConfigPath, resolved);
57186
57805
  }
57187
57806
  }
57188
- const homedir8 = (await import("node:os")).homedir();
57189
- const legacyCleoHome = join42(homedir8, ".cleo");
57807
+ const homedir9 = (await import("node:os")).homedir();
57808
+ const legacyCleoHome = join42(homedir9, ".cleo");
57190
57809
  const cleanupPaths = [cleoHome];
57191
57810
  if (legacyCleoHome !== cleoHome && existsSync40(legacyCleoHome)) {
57192
57811
  cleanupPaths.push(legacyCleoHome);
@@ -65289,10 +65908,10 @@ async function readProjectMeta(projectPath) {
65289
65908
  }
65290
65909
  async function readProjectId(projectPath) {
65291
65910
  try {
65292
- const { readFileSync: readFileSync107, existsSync: existsSync135 } = await import("node:fs");
65911
+ const { readFileSync: readFileSync108, existsSync: existsSync136 } = await import("node:fs");
65293
65912
  const infoPath = join64(projectPath, ".cleo", "project-info.json");
65294
- if (!existsSync135(infoPath)) return "";
65295
- const data = JSON.parse(readFileSync107(infoPath, "utf-8"));
65913
+ if (!existsSync136(infoPath)) return "";
65914
+ const data = JSON.parse(readFileSync108(infoPath, "utf-8"));
65296
65915
  return typeof data.projectId === "string" ? data.projectId : "";
65297
65916
  } catch {
65298
65917
  return "";
@@ -66996,6 +67615,10 @@ async function startTask(taskId, cwd, accessor) {
66996
67615
  }
66997
67616
  );
66998
67617
  }
67618
+ const currentStage = task.pipelineStage;
67619
+ if (currentStage && isValidPipelineStage(currentStage) && PLANNING_STAGES2.has(currentStage)) {
67620
+ await acc.updateTaskFields(taskId, { pipelineStage: "implementation" });
67621
+ }
66999
67622
  const focus = await acc.getMetaValue("focus_state") ?? {};
67000
67623
  const previousTask = focus.currentTask ?? null;
67001
67624
  focus.currentTask = taskId;
@@ -67083,16 +67706,24 @@ async function getWorkHistory(cwd, accessor) {
67083
67706
  }
67084
67707
  return history.reverse();
67085
67708
  }
67086
- var getTaskHistory;
67709
+ var PLANNING_STAGES2, getTaskHistory;
67087
67710
  var init_task_work = __esm({
67088
67711
  "packages/core/src/task-work/index.ts"() {
67089
67712
  "use strict";
67713
+ init_handlers();
67090
67714
  init_src();
67091
67715
  init_errors3();
67092
67716
  init_data_accessor();
67093
67717
  init_add();
67094
67718
  init_dependency_check();
67095
- init_handlers();
67719
+ init_pipeline_stage();
67720
+ PLANNING_STAGES2 = /* @__PURE__ */ new Set([
67721
+ "research",
67722
+ "consensus",
67723
+ "architecture_decision",
67724
+ "specification",
67725
+ "decomposition"
67726
+ ]);
67096
67727
  getTaskHistory = getWorkHistory;
67097
67728
  }
67098
67729
  });
@@ -67214,6 +67845,10 @@ async function completeTask(options, cwd, accessor) {
67214
67845
  }
67215
67846
  const now2 = (/* @__PURE__ */ new Date()).toISOString();
67216
67847
  const before = { ...task };
67848
+ const completionStage = task.pipelineStage;
67849
+ if (completionStage && isValidPipelineStage(completionStage) && EXECUTION_STAGES_FOR_RELEASE.has(completionStage)) {
67850
+ task.pipelineStage = "release";
67851
+ }
67217
67852
  task.status = "done";
67218
67853
  task.completedAt = now2;
67219
67854
  task.updatedAt = now2;
@@ -67326,7 +67961,7 @@ async function completeTask(options, cwd, accessor) {
67326
67961
  ...unblockedTasks.length > 0 && { unblockedTasks }
67327
67962
  };
67328
67963
  }
67329
- var DEFAULT_VERIFICATION_REQUIRED_GATES, VERIFICATION_GATES;
67964
+ var EXECUTION_STAGES_FOR_RELEASE, DEFAULT_VERIFICATION_REQUIRED_GATES, VERIFICATION_GATES;
67330
67965
  var init_complete = __esm({
67331
67966
  "packages/core/src/tasks/complete.ts"() {
67332
67967
  "use strict";
@@ -67336,6 +67971,8 @@ var init_complete = __esm({
67336
67971
  init_session_enforcement();
67337
67972
  init_data_accessor();
67338
67973
  init_enforcement();
67974
+ init_pipeline_stage();
67975
+ EXECUTION_STAGES_FOR_RELEASE = /* @__PURE__ */ new Set(["implementation", "validation", "testing"]);
67339
67976
  DEFAULT_VERIFICATION_REQUIRED_GATES = [
67340
67977
  "implemented",
67341
67978
  "testsPassed",
@@ -76910,8 +77547,8 @@ async function initializeSpawnAdapters(manifests) {
76910
77547
  if (!manifest.capabilities?.supportsSpawn) continue;
76911
77548
  if (spawnRegistry.hasAdapterForProvider(manifest.provider)) continue;
76912
77549
  try {
76913
- const { join: join135 } = await import("node:path");
76914
- const modulePath = join135(manifest.packagePath, manifest.entryPoint);
77550
+ const { join: join136 } = await import("node:path");
77551
+ const modulePath = join136(manifest.packagePath, manifest.entryPoint);
76915
77552
  const adapterModule = await import(modulePath);
76916
77553
  let SpawnProviderClass;
76917
77554
  for (const [exportName, exportValue] of Object.entries(adapterModule)) {
@@ -80878,6 +81515,46 @@ async function getSystemHealth(projectRoot, opts) {
80878
81515
  if (existsSync92(dbPath)) {
80879
81516
  checks.push(checkAuditLogAvailability(dbPath));
80880
81517
  }
81518
+ if (existsSync92(dbPath) && databaseSyncCtor) {
81519
+ try {
81520
+ const tasksDb = new databaseSyncCtor(dbPath, { readOnly: true });
81521
+ let taskCount = 0;
81522
+ try {
81523
+ const tableExists3 = tasksDb.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='tasks'").get();
81524
+ if (tableExists3?.name) {
81525
+ const countRow = tasksDb.prepare("SELECT COUNT(*) as cnt FROM tasks").get();
81526
+ taskCount = countRow?.cnt ?? 0;
81527
+ }
81528
+ } finally {
81529
+ tasksDb.close();
81530
+ }
81531
+ if (taskCount === 0) {
81532
+ const { listSqliteBackups: listSqliteBackups2 } = await Promise.resolve().then(() => (init_sqlite_backup(), sqlite_backup_exports));
81533
+ const backups = listSqliteBackups2(projectRoot);
81534
+ if (backups.length > 0) {
81535
+ const latestBackup = backups[0];
81536
+ checks.push({
81537
+ name: "tasks_wipe_guard",
81538
+ status: "fail",
81539
+ message: `WIPE ALERT: tasks.db has 0 tasks but ${backups.length} backup(s) exist. Latest: ${latestBackup?.name ?? "unknown"} (${new Date(latestBackup?.mtimeMs ?? 0).toISOString()}). Probable data loss \u2014 restore via: cleo restore backup --file tasks.db. See docs/RECOVERY.md for the full procedure. (T724)`
81540
+ });
81541
+ } else {
81542
+ checks.push({
81543
+ name: "tasks_wipe_guard",
81544
+ status: "warn",
81545
+ message: "tasks.db has 0 tasks and no backups exist (fresh install or data loss without backup recovery)."
81546
+ });
81547
+ }
81548
+ } else {
81549
+ checks.push({
81550
+ name: "tasks_wipe_guard",
81551
+ status: "pass",
81552
+ message: `tasks.db integrity: ${taskCount} task(s) present`
81553
+ });
81554
+ }
81555
+ } catch {
81556
+ }
81557
+ }
80881
81558
  const sdDbPath = join94(cleoDir, "signaldock.db");
80882
81559
  if (existsSync92(sdDbPath)) {
80883
81560
  try {
@@ -88706,6 +89383,7 @@ var init_cleo = __esm({
88706
89383
  // packages/core/src/index.ts
88707
89384
  var init_src3 = __esm({
88708
89385
  "packages/core/src/index.ts"() {
89386
+ "use strict";
88709
89387
  init_src();
88710
89388
  init_adapters();
88711
89389
  init_admin();
@@ -92175,6 +92853,197 @@ var init_claude_mem_migration = __esm({
92175
92853
  }
92176
92854
  });
92177
92855
 
92856
+ // packages/core/src/memory/dream-cycle.ts
92857
+ function countNewObservations(afterTimestamp) {
92858
+ const db = getBrainNativeDb();
92859
+ if (!db) return 0;
92860
+ try {
92861
+ const row = db.prepare(
92862
+ `SELECT COUNT(*) AS cnt FROM brain_observations
92863
+ WHERE created_at > ? AND invalid_at IS NULL`
92864
+ ).get(afterTimestamp);
92865
+ return row?.cnt ?? 0;
92866
+ } catch {
92867
+ return 0;
92868
+ }
92869
+ }
92870
+ function getLastConsolidationTimestamp() {
92871
+ const db = getBrainNativeDb();
92872
+ if (!db) return null;
92873
+ try {
92874
+ const row = db.prepare(
92875
+ `SELECT started_at FROM brain_consolidation_events
92876
+ ORDER BY started_at DESC LIMIT 1`
92877
+ ).get();
92878
+ return row?.started_at ?? null;
92879
+ } catch {
92880
+ return null;
92881
+ }
92882
+ }
92883
+ function getLastRetrievalTimestamp() {
92884
+ const db = getBrainNativeDb();
92885
+ if (!db) return null;
92886
+ try {
92887
+ const row = db.prepare(`SELECT created_at FROM brain_retrieval_log ORDER BY created_at DESC LIMIT 1`).get();
92888
+ return row?.created_at ?? null;
92889
+ } catch {
92890
+ return null;
92891
+ }
92892
+ }
92893
+ function minutesSince(isoTimestamp2) {
92894
+ if (!isoTimestamp2) return Infinity;
92895
+ const normalised = isoTimestamp2.includes("T") ? isoTimestamp2 : isoTimestamp2.replace(" ", "T") + "Z";
92896
+ const ms2 = Date.now() - new Date(normalised).getTime();
92897
+ return ms2 / 6e4;
92898
+ }
92899
+ function checkVolumeTrigger(threshold) {
92900
+ const lastConsolidated = getLastConsolidationTimestamp();
92901
+ const after = lastConsolidated ?? "1970-01-01 00:00:00";
92902
+ const newObservationCount = countNewObservations(after);
92903
+ return {
92904
+ shouldTrigger: newObservationCount >= threshold,
92905
+ newObservationCount
92906
+ };
92907
+ }
92908
+ function checkIdleTrigger(idleThresholdMinutes) {
92909
+ const lastRetrievalTs = getLastRetrievalTimestamp();
92910
+ if (lastRetrievalTs === null) {
92911
+ return { shouldTrigger: false, idleMinutes: 0 };
92912
+ }
92913
+ const idleMinutes = minutesSince(lastRetrievalTs);
92914
+ return {
92915
+ shouldTrigger: idleMinutes >= idleThresholdMinutes,
92916
+ idleMinutes
92917
+ };
92918
+ }
92919
+ async function dispatchDream(projectRoot, sessionId, inline = false) {
92920
+ if (dreamInFlight) return;
92921
+ dreamInFlight = true;
92922
+ lastDreamAt = Date.now();
92923
+ const run = async () => {
92924
+ try {
92925
+ const { runConsolidation: runConsolidation2 } = await Promise.resolve().then(() => (init_brain_lifecycle(), brain_lifecycle_exports));
92926
+ await runConsolidation2(projectRoot, sessionId ?? null, "scheduled");
92927
+ } catch (err) {
92928
+ console.warn("[dream-cycle] Consolidation failed:", err);
92929
+ } finally {
92930
+ dreamInFlight = false;
92931
+ }
92932
+ };
92933
+ if (inline) {
92934
+ await run();
92935
+ } else {
92936
+ setImmediate(run);
92937
+ }
92938
+ }
92939
+ async function checkAndDream(projectRoot, opts = {}) {
92940
+ const volumeThreshold = opts.volumeThreshold ?? VOLUME_THRESHOLD_DEFAULT;
92941
+ const idleThresholdMinutes = opts.idleThresholdMinutes ?? IDLE_MINUTES_DEFAULT;
92942
+ try {
92943
+ await getBrainDb(projectRoot);
92944
+ } catch {
92945
+ return {
92946
+ triggered: false,
92947
+ tier: null,
92948
+ skippedReason: "brain.db unavailable"
92949
+ };
92950
+ }
92951
+ const msSinceLastDream = Date.now() - lastDreamAt;
92952
+ if (msSinceLastDream < DREAM_COOLDOWN_MS) {
92953
+ return {
92954
+ triggered: false,
92955
+ tier: null,
92956
+ skippedReason: `dream cooldown active (${Math.round(msSinceLastDream / 1e3)}s since last dream)`
92957
+ };
92958
+ }
92959
+ if (dreamInFlight) {
92960
+ return {
92961
+ triggered: false,
92962
+ tier: null,
92963
+ skippedReason: "dream already in flight"
92964
+ };
92965
+ }
92966
+ const volumeCheck = checkVolumeTrigger(volumeThreshold);
92967
+ if (volumeCheck.shouldTrigger) {
92968
+ await dispatchDream(projectRoot, opts.sessionId, opts.inline);
92969
+ return {
92970
+ triggered: true,
92971
+ tier: "volume",
92972
+ newObservationCount: volumeCheck.newObservationCount
92973
+ };
92974
+ }
92975
+ const idleCheck = checkIdleTrigger(idleThresholdMinutes);
92976
+ if (idleCheck.shouldTrigger) {
92977
+ await dispatchDream(projectRoot, opts.sessionId, opts.inline);
92978
+ return {
92979
+ triggered: true,
92980
+ tier: "idle",
92981
+ idleMinutes: idleCheck.idleMinutes
92982
+ };
92983
+ }
92984
+ return {
92985
+ triggered: false,
92986
+ tier: null,
92987
+ skippedReason: `volume below threshold (${volumeCheck.newObservationCount}/${volumeThreshold}); idle below threshold (${Math.round(idleCheck.idleMinutes)}/${idleThresholdMinutes} min)`,
92988
+ newObservationCount: volumeCheck.newObservationCount,
92989
+ idleMinutes: idleCheck.idleMinutes
92990
+ };
92991
+ }
92992
+ async function triggerManualDream(projectRoot, sessionId) {
92993
+ const { runConsolidation: runConsolidation2 } = await Promise.resolve().then(() => (init_brain_lifecycle(), brain_lifecycle_exports));
92994
+ const result = await runConsolidation2(projectRoot, sessionId ?? null, "manual");
92995
+ lastDreamAt = Date.now();
92996
+ return result;
92997
+ }
92998
+ function startDreamScheduler(projectRoot, hourUTC = NIGHTLY_HOUR_DEFAULT) {
92999
+ if (nightlyTimer !== null) return false;
93000
+ const msUntilNextFire = () => {
93001
+ const now2 = /* @__PURE__ */ new Date();
93002
+ const next = new Date(now2);
93003
+ next.setUTCHours(hourUTC, 0, 0, 0);
93004
+ if (next.getTime() <= now2.getTime()) {
93005
+ next.setUTCDate(next.getUTCDate() + 1);
93006
+ }
93007
+ return next.getTime() - now2.getTime();
93008
+ };
93009
+ const scheduleNext = () => {
93010
+ nightlyTimer = setTimeout(() => {
93011
+ nightlyTimer = null;
93012
+ checkAndDream(projectRoot, { inline: false }).catch((err) => {
93013
+ console.warn("[dream-cycle] Nightly cron failed:", err);
93014
+ });
93015
+ scheduleNext();
93016
+ }, msUntilNextFire());
93017
+ };
93018
+ scheduleNext();
93019
+ return true;
93020
+ }
93021
+ function stopDreamScheduler() {
93022
+ if (nightlyTimer === null) return false;
93023
+ clearTimeout(nightlyTimer);
93024
+ nightlyTimer = null;
93025
+ return true;
93026
+ }
93027
+ function _resetDreamState() {
93028
+ lastDreamAt = 0;
93029
+ dreamInFlight = false;
93030
+ stopDreamScheduler();
93031
+ }
93032
+ var VOLUME_THRESHOLD_DEFAULT, IDLE_MINUTES_DEFAULT, DREAM_COOLDOWN_MS, NIGHTLY_HOUR_DEFAULT, lastDreamAt, dreamInFlight, nightlyTimer;
93033
+ var init_dream_cycle = __esm({
93034
+ "packages/core/src/memory/dream-cycle.ts"() {
93035
+ "use strict";
93036
+ init_brain_sqlite();
93037
+ VOLUME_THRESHOLD_DEFAULT = 10;
93038
+ IDLE_MINUTES_DEFAULT = 30;
93039
+ DREAM_COOLDOWN_MS = 5 * 60 * 1e3;
93040
+ NIGHTLY_HOUR_DEFAULT = 4;
93041
+ lastDreamAt = 0;
93042
+ dreamInFlight = false;
93043
+ nightlyTimer = null;
93044
+ }
93045
+ });
93046
+
92178
93047
  // packages/core/src/memory/mental-model-queue.ts
92179
93048
  async function drainQueue() {
92180
93049
  if (_queue.length === 0) return 0;
@@ -101333,9 +102202,9 @@ async function runUpgrade(options = {}) {
101333
102202
  mkdirSync29(backupDir, { recursive: true });
101334
102203
  }
101335
102204
  copyFileSync9(dbPath2, dbBackupPath);
101336
- const { createHash: createHash17 } = await import("node:crypto");
101337
- const origChecksum = createHash17("sha256").update(readFileSync87(dbPath2)).digest("hex");
101338
- const backupChecksum = createHash17("sha256").update(readFileSync87(dbBackupPath)).digest("hex");
102205
+ const { createHash: createHash18 } = await import("node:crypto");
102206
+ const origChecksum = createHash18("sha256").update(readFileSync87(dbPath2)).digest("hex");
102207
+ const backupChecksum = createHash18("sha256").update(readFileSync87(dbBackupPath)).digest("hex");
101339
102208
  if (origChecksum !== backupChecksum) {
101340
102209
  throw new Error(
101341
102210
  `Backup verification failed: checksum mismatch. Aborting migration to prevent data loss.`
@@ -113993,6 +114862,7 @@ __export(internal_exports, {
113993
114862
  WORKFLOW_GATE_SEQUENCE: () => WORKFLOW_GATE_SEQUENCE,
113994
114863
  WorkflowGateName: () => WorkflowGateName,
113995
114864
  WorkflowGateTracker: () => WorkflowGateTracker,
114865
+ _resetDreamState: () => _resetDreamState,
113996
114866
  _resetGlobalSignaldockDb_TESTING_ONLY: () => _resetGlobalSignaldockDb_TESTING_ONLY,
113997
114867
  adapters: () => adapters_exports,
113998
114868
  addChain: () => addChain,
@@ -114065,6 +114935,7 @@ __export(internal_exports, {
114065
114935
  channelToDistTag: () => channelToDistTag,
114066
114936
  checkAgentHealth: () => checkAgentHealth2,
114067
114937
  checkAllDependencies: () => checkAllDependencies,
114938
+ checkAndDream: () => checkAndDream,
114068
114939
  checkArchitectureDecisionManifest: () => checkArchitectureDecisionManifest,
114069
114940
  checkArtifactPublishManifest: () => checkArtifactPublishManifest,
114070
114941
  checkConduitDbHealth: () => checkConduitDbHealth,
@@ -114076,6 +114947,7 @@ __export(internal_exports, {
114076
114947
  checkEpicCompleteness: () => checkEpicCompleteness,
114077
114948
  checkGate: () => checkGate,
114078
114949
  checkGlobalSignaldockDbHealth: () => checkGlobalSignaldockDbHealth,
114950
+ checkIdleTrigger: () => checkIdleTrigger,
114079
114951
  checkImplementationManifest: () => checkImplementationManifest,
114080
114952
  checkProvenanceManifest: () => checkProvenanceManifest,
114081
114953
  checkReleaseManifest: () => checkReleaseManifest,
@@ -114089,6 +114961,7 @@ __export(internal_exports, {
114089
114961
  checkStorageMigration: () => checkStorageMigration,
114090
114962
  checkTestingManifest: () => checkTestingManifest,
114091
114963
  checkValidationManifest: () => checkValidationManifest,
114964
+ checkVolumeTrigger: () => checkVolumeTrigger,
114092
114965
  classifyError: () => classifyError,
114093
114966
  classifyProject: () => classifyProject,
114094
114967
  cleanupSessions: () => cleanupSessions,
@@ -114685,6 +115558,7 @@ __export(internal_exports, {
114685
115558
  snapshot: () => snapshot_exports,
114686
115559
  spawn: () => spawn_exports,
114687
115560
  spawnRegistry: () => spawnRegistry,
115561
+ startDreamScheduler: () => startDreamScheduler,
114688
115562
  startParallelExecution: () => startParallelExecution,
114689
115563
  startPhase: () => startPhase,
114690
115564
  startSession: () => startSession,
@@ -114692,6 +115566,7 @@ __export(internal_exports, {
114692
115566
  startupHealthCheck: () => startupHealthCheck,
114693
115567
  stats: () => stats_exports,
114694
115568
  sticky: () => sticky_exports,
115569
+ stopDreamScheduler: () => stopDreamScheduler,
114695
115570
  stopTask: () => stopTask,
114696
115571
  storeDetectedPattern: () => storeDetectedPattern,
114697
115572
  storeHealingStrategy: () => storeHealingStrategy,
@@ -114726,6 +115601,7 @@ __export(internal_exports, {
114726
115601
  tokenUsageTransportSchema: () => tokenUsageTransportSchema,
114727
115602
  touchLink: () => touchLink,
114728
115603
  trackMemoryUsage: () => trackMemoryUsage,
115604
+ triggerManualDream: () => triggerManualDream,
114729
115605
  ui: () => ui_exports,
114730
115606
  uncancelTask: () => uncancelTask,
114731
115607
  unpackBundle: () => unpackBundle,
@@ -114813,6 +115689,7 @@ var init_internal = __esm({
114813
115689
  init_brain_retrieval();
114814
115690
  init_brain_stdp();
114815
115691
  init_claude_mem_migration();
115692
+ init_dream_cycle();
114816
115693
  init_engine_compat();
114817
115694
  init_pipeline_manifest_sqlite();
114818
115695
  init_quality_feedback();
@@ -122185,7 +123062,7 @@ async function systemStats(projectRoot, params) {
122185
123062
  }
122186
123063
  const byPhase = {};
122187
123064
  for (const t of activeTasks) {
122188
- const phase = t.phase || "unassigned";
123065
+ const phase = t.pipelineStage || "unassigned";
122189
123066
  byPhase[phase] = (byPhase[phase] ?? 0) + 1;
122190
123067
  }
122191
123068
  const completedTasks = tasks2.filter((t) => t.status === "done" && t.completedAt && t.createdAt);
@@ -122271,10 +123148,10 @@ async function systemLog(projectRoot, filters) {
122271
123148
  }
122272
123149
  async function queryAuditLogSqlite(projectRoot, filters) {
122273
123150
  try {
122274
- const { join: join135 } = await import("node:path");
122275
- const { existsSync: existsSync135 } = await import("node:fs");
122276
- const dbPath = join135(projectRoot, ".cleo", "tasks.db");
122277
- if (!existsSync135(dbPath)) {
123151
+ const { join: join136 } = await import("node:path");
123152
+ const { existsSync: existsSync136 } = await import("node:fs");
123153
+ const dbPath = join136(projectRoot, ".cleo", "tasks.db");
123154
+ if (!existsSync136(dbPath)) {
122278
123155
  const offset = filters?.offset ?? 0;
122279
123156
  const limit = filters?.limit ?? 20;
122280
123157
  return {
@@ -127880,16 +128757,16 @@ var init_nexus2 = __esm({
127880
128757
  async function orchestrateClassify(request, context, projectRoot) {
127881
128758
  try {
127882
128759
  const { getCleoCantWorkflowsDir: getCleoCantWorkflowsDir2 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
127883
- const { readFileSync: readFileSync107, readdirSync: readdirSync42, existsSync: existsSync135 } = await import("node:fs");
127884
- const { join: join135 } = await import("node:path");
128760
+ const { readFileSync: readFileSync108, readdirSync: readdirSync43, existsSync: existsSync136 } = await import("node:fs");
128761
+ const { join: join136 } = await import("node:path");
127885
128762
  const workflowsDir = getCleoCantWorkflowsDir2();
127886
128763
  const combined = `${request} ${context ?? ""}`.toLowerCase();
127887
128764
  const matches = [];
127888
- if (existsSync135(workflowsDir)) {
127889
- const files = readdirSync42(workflowsDir).filter((f2) => f2.endsWith(".cant"));
128765
+ if (existsSync136(workflowsDir)) {
128766
+ const files = readdirSync43(workflowsDir).filter((f2) => f2.endsWith(".cant"));
127890
128767
  for (const file2 of files) {
127891
128768
  try {
127892
- const src = readFileSync107(join135(workflowsDir, file2), "utf-8");
128769
+ const src = readFileSync108(join136(workflowsDir, file2), "utf-8");
127893
128770
  const teamMatch = /^team\s+(\S+):/m.exec(src);
127894
128771
  if (!teamMatch) continue;
127895
128772
  const teamName = teamMatch[1];
@@ -127904,12 +128781,12 @@ async function orchestrateClassify(request, context, projectRoot) {
127904
128781
  }
127905
128782
  }
127906
128783
  }
127907
- const localCantDir = join135(projectRoot, ".cleo", "workflows");
127908
- if (existsSync135(localCantDir)) {
127909
- const files = readdirSync42(localCantDir).filter((f2) => f2.endsWith(".cant"));
128784
+ const localCantDir = join136(projectRoot, ".cleo", "workflows");
128785
+ if (existsSync136(localCantDir)) {
128786
+ const files = readdirSync43(localCantDir).filter((f2) => f2.endsWith(".cant"));
127910
128787
  for (const file2 of files) {
127911
128788
  try {
127912
- const src = readFileSync107(join135(localCantDir, file2), "utf-8");
128789
+ const src = readFileSync108(join136(localCantDir, file2), "utf-8");
127913
128790
  const teamMatch = /^team\s+(\S+):/m.exec(src);
127914
128791
  if (!teamMatch) continue;
127915
128792
  const teamName = teamMatch[1];
@@ -132554,8 +133431,8 @@ var init_cli = __esm({
132554
133431
 
132555
133432
  // packages/cleo/src/cli/index.ts
132556
133433
  init_internal();
132557
- import { readFileSync as readFileSync106 } from "node:fs";
132558
- import { dirname as dirname31, join as join134 } from "node:path";
133434
+ import { readFileSync as readFileSync107 } from "node:fs";
133435
+ import { dirname as dirname31, join as join135 } from "node:path";
132559
133436
  import { fileURLToPath as fileURLToPath8 } from "node:url";
132560
133437
 
132561
133438
  // node_modules/.pnpm/citty@0.2.1/node_modules/citty/dist/_chunks/libs/scule.mjs
@@ -134057,13 +134934,13 @@ function registerAgentCommand(program) {
134057
134934
  transportConfig: {},
134058
134935
  isActive: true
134059
134936
  });
134060
- const { existsSync: existsSync135, mkdirSync: mkdirSync35, writeFileSync: writeFileSync26 } = await import("node:fs");
134061
- const { join: join135 } = await import("node:path");
134062
- const cantDir = join135(".cleo", "agents");
134063
- const cantPath = join135(cantDir, `${agentId}.cant`);
134937
+ const { existsSync: existsSync136, mkdirSync: mkdirSync36, writeFileSync: writeFileSync27 } = await import("node:fs");
134938
+ const { join: join136 } = await import("node:path");
134939
+ const cantDir = join136(".cleo", "agents");
134940
+ const cantPath = join136(cantDir, `${agentId}.cant`);
134064
134941
  let cantScaffolded = false;
134065
- if (!existsSync135(cantPath)) {
134066
- mkdirSync35(cantDir, { recursive: true });
134942
+ if (!existsSync136(cantPath)) {
134943
+ mkdirSync36(cantDir, { recursive: true });
134067
134944
  const role = classification ?? "specialist";
134068
134945
  const cantContent = `---
134069
134946
  kind: agent
@@ -134113,7 +134990,7 @@ agent ${agentId}:
134113
134990
  enforcement:
134114
134991
  1: TODO \u2014 what does this agent push back on?
134115
134992
  `;
134116
- writeFileSync26(cantPath, cantContent, "utf-8");
134993
+ writeFileSync27(cantPath, cantContent, "utf-8");
134117
134994
  cantScaffolded = true;
134118
134995
  }
134119
134996
  cliOutput(
@@ -134122,7 +134999,7 @@ agent ${agentId}:
134122
134999
  data: {
134123
135000
  agentId: credential.agentId,
134124
135001
  displayName: credential.displayName,
134125
- cantFile: cantScaffolded ? cantPath : existsSync135(cantPath) ? cantPath : null,
135002
+ cantFile: cantScaffolded ? cantPath : existsSync136(cantPath) ? cantPath : null,
134126
135003
  cantScaffolded
134127
135004
  }
134128
135005
  },
@@ -134202,8 +135079,8 @@ agent ${agentId}:
134202
135079
  try {
134203
135080
  const { AgentRegistryAccessor: AgentRegistryAccessor2, getDb: getDb4 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
134204
135081
  const { createRuntime } = await import("@cleocode/runtime");
134205
- const { existsSync: existsSync135, readFileSync: readFileSync107 } = await import("node:fs");
134206
- const { join: join135 } = await import("node:path");
135082
+ const { existsSync: existsSync136, readFileSync: readFileSync108 } = await import("node:fs");
135083
+ const { join: join136 } = await import("node:path");
134207
135084
  await getDb4();
134208
135085
  const registry2 = new AgentRegistryAccessor2(process.cwd());
134209
135086
  const credential = await registry2.get(agentId);
@@ -134223,9 +135100,9 @@ agent ${agentId}:
134223
135100
  }
134224
135101
  let profile = null;
134225
135102
  let cantValidation = null;
134226
- const cantPath = opts["cant"] ?? join135(".cleo", "agents", `${agentId}.cant`);
134227
- if (existsSync135(cantPath)) {
134228
- profile = readFileSync107(cantPath, "utf-8");
135103
+ const cantPath = opts["cant"] ?? join136(".cleo", "agents", `${agentId}.cant`);
135104
+ if (existsSync136(cantPath)) {
135105
+ profile = readFileSync108(cantPath, "utf-8");
134229
135106
  try {
134230
135107
  const cantModule = await import("@cleocode/cant");
134231
135108
  const validate = "validate" in cantModule ? cantModule.validate : null;
@@ -134620,8 +135497,8 @@ Task ${taskId} reassigned to you by ${active.agentId}. Run: cleo show ${taskId}
134620
135497
  try {
134621
135498
  const { AgentRegistryAccessor: AgentRegistryAccessor2, getDb: getDb4 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
134622
135499
  const { createRuntime } = await import("@cleocode/runtime");
134623
- const { existsSync: existsSync135 } = await import("node:fs");
134624
- const { join: join135 } = await import("node:path");
135500
+ const { existsSync: existsSync136 } = await import("node:fs");
135501
+ const { join: join136 } = await import("node:path");
134625
135502
  const { execFile: execFile8 } = await import("node:child_process");
134626
135503
  const { promisify: promisify8 } = await import("node:util");
134627
135504
  const execFileAsync6 = promisify8(execFile8);
@@ -134641,8 +135518,8 @@ Task ${taskId} reassigned to you by ${active.agentId}. Run: cleo show ${taskId}
134641
135518
  }
134642
135519
  await registry2.update(agentId, { isActive: true });
134643
135520
  await registry2.markUsed(agentId);
134644
- const cantPath = join135(".cleo", "agents", `${agentId}.cant`);
134645
- const hasProfile = existsSync135(cantPath);
135521
+ const cantPath = join136(".cleo", "agents", `${agentId}.cant`);
135522
+ const hasProfile = existsSync136(cantPath);
134646
135523
  const runtime = await createRuntime(registry2, {
134647
135524
  agentId,
134648
135525
  pollIntervalMs: 5e3,
@@ -135265,12 +136142,12 @@ Task ${taskId} reassigned to you by ${active.agentId}. Run: cleo show ${taskId}
135265
136142
  });
135266
136143
  agent.command("install <path>").description("Install an agent from a .cantz archive or agent directory").option("--global", "Install to global tier (~/.local/share/cleo/cant/agents/)").action(async (sourcePath, opts) => {
135267
136144
  try {
135268
- const { existsSync: existsSync135, mkdirSync: mkdirSync35, cpSync, readFileSync: readFileSync107, rmSync: rmSync2, statSync: statSync22 } = await import("node:fs");
135269
- const { join: join135, basename: basename19, resolve: resolve17 } = await import("node:path");
135270
- const { homedir: homedir8 } = await import("node:os");
136145
+ const { existsSync: existsSync136, mkdirSync: mkdirSync36, cpSync, readFileSync: readFileSync108, rmSync: rmSync2, statSync: statSync22 } = await import("node:fs");
136146
+ const { join: join136, basename: basename19, resolve: resolve17 } = await import("node:path");
136147
+ const { homedir: homedir9 } = await import("node:os");
135271
136148
  const { tmpdir: tmpdir2 } = await import("node:os");
135272
136149
  const resolvedPath = resolve17(sourcePath);
135273
- if (!existsSync135(resolvedPath)) {
136150
+ if (!existsSync136(resolvedPath)) {
135274
136151
  cliOutput(
135275
136152
  {
135276
136153
  success: false,
@@ -135290,8 +136167,8 @@ Task ${taskId} reassigned to you by ${active.agentId}. Run: cleo show ${taskId}
135290
136167
  const isCantzArchive = resolvedPath.endsWith(".cantz") && statSync22(resolvedPath).isFile();
135291
136168
  if (isCantzArchive) {
135292
136169
  const { execFileSync: execFileSync19 } = await import("node:child_process");
135293
- tempDir = join135(tmpdir2(), `cleo-agent-install-${Date.now()}`);
135294
- mkdirSync35(tempDir, { recursive: true });
136170
+ tempDir = join136(tmpdir2(), `cleo-agent-install-${Date.now()}`);
136171
+ mkdirSync36(tempDir, { recursive: true });
135295
136172
  try {
135296
136173
  execFileSync19("unzip", ["-o", "-q", resolvedPath, "-d", tempDir], {
135297
136174
  encoding: "utf-8",
@@ -135312,9 +136189,9 @@ Task ${taskId} reassigned to you by ${active.agentId}. Run: cleo show ${taskId}
135312
136189
  process.exitCode = 6;
135313
136190
  return;
135314
136191
  }
135315
- const { readdirSync: readdirSync42 } = await import("node:fs");
135316
- const topLevel = readdirSync42(tempDir).filter((entry) => {
135317
- const entryPath = join135(tempDir, entry);
136192
+ const { readdirSync: readdirSync43 } = await import("node:fs");
136193
+ const topLevel = readdirSync43(tempDir).filter((entry) => {
136194
+ const entryPath = join136(tempDir, entry);
135318
136195
  return statSync22(entryPath).isDirectory();
135319
136196
  });
135320
136197
  if (topLevel.length !== 1) {
@@ -135333,7 +136210,7 @@ Task ${taskId} reassigned to you by ${active.agentId}. Run: cleo show ${taskId}
135333
136210
  return;
135334
136211
  }
135335
136212
  agentName = topLevel[0];
135336
- agentDir = join135(tempDir, agentName);
136213
+ agentDir = join136(tempDir, agentName);
135337
136214
  } else if (statSync22(resolvedPath).isDirectory()) {
135338
136215
  agentDir = resolvedPath;
135339
136216
  agentName = basename19(resolvedPath);
@@ -135351,8 +136228,8 @@ Task ${taskId} reassigned to you by ${active.agentId}. Run: cleo show ${taskId}
135351
136228
  process.exitCode = 6;
135352
136229
  return;
135353
136230
  }
135354
- const personaPath = join135(agentDir, "persona.cant");
135355
- if (!existsSync135(personaPath)) {
136231
+ const personaPath = join136(agentDir, "persona.cant");
136232
+ if (!existsSync136(personaPath)) {
135356
136233
  if (tempDir) rmSync2(tempDir, { recursive: true, force: true });
135357
136234
  cliOutput(
135358
136235
  {
@@ -135370,21 +136247,21 @@ Task ${taskId} reassigned to you by ${active.agentId}. Run: cleo show ${taskId}
135370
136247
  const isGlobal = opts["global"] === true;
135371
136248
  let targetRoot;
135372
136249
  if (isGlobal) {
135373
- const home = homedir8();
135374
- const xdgData = process.env["XDG_DATA_HOME"] ?? join135(home, ".local", "share");
135375
- targetRoot = join135(xdgData, "cleo", "cant", "agents");
136250
+ const home = homedir9();
136251
+ const xdgData = process.env["XDG_DATA_HOME"] ?? join136(home, ".local", "share");
136252
+ targetRoot = join136(xdgData, "cleo", "cant", "agents");
135376
136253
  } else {
135377
- targetRoot = join135(process.cwd(), ".cleo", "cant", "agents");
136254
+ targetRoot = join136(process.cwd(), ".cleo", "cant", "agents");
135378
136255
  }
135379
- const targetDir = join135(targetRoot, agentName);
135380
- mkdirSync35(targetRoot, { recursive: true });
136256
+ const targetDir = join136(targetRoot, agentName);
136257
+ mkdirSync36(targetRoot, { recursive: true });
135381
136258
  cpSync(agentDir, targetDir, { recursive: true, force: true });
135382
136259
  if (tempDir) {
135383
136260
  rmSync2(tempDir, { recursive: true, force: true });
135384
136261
  }
135385
136262
  let registered = false;
135386
136263
  try {
135387
- const persona = readFileSync107(join135(targetDir, "persona.cant"), "utf-8");
136264
+ const persona = readFileSync108(join136(targetDir, "persona.cant"), "utf-8");
135388
136265
  const descMatch = persona.match(/description:\s*"([^"]+)"/);
135389
136266
  const displayName = descMatch?.[1] ?? agentName;
135390
136267
  const { AgentRegistryAccessor: AgentRegistryAccessor2, getDb: getDb4 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
@@ -135431,11 +136308,11 @@ Task ${taskId} reassigned to you by ${active.agentId}. Run: cleo show ${taskId}
135431
136308
  });
135432
136309
  agent.command("pack <dir>").description("Package an agent directory as a .cantz archive").action(async (dir) => {
135433
136310
  try {
135434
- const { existsSync: existsSync135, statSync: statSync22 } = await import("node:fs");
136311
+ const { existsSync: existsSync136, statSync: statSync22 } = await import("node:fs");
135435
136312
  const { resolve: resolve17, basename: basename19, dirname: dirname32 } = await import("node:path");
135436
136313
  const { execFileSync: execFileSync19 } = await import("node:child_process");
135437
136314
  const resolvedDir = resolve17(dir);
135438
- if (!existsSync135(resolvedDir) || !statSync22(resolvedDir).isDirectory()) {
136315
+ if (!existsSync136(resolvedDir) || !statSync22(resolvedDir).isDirectory()) {
135439
136316
  cliOutput(
135440
136317
  {
135441
136318
  success: false,
@@ -135449,9 +136326,9 @@ Task ${taskId} reassigned to you by ${active.agentId}. Run: cleo show ${taskId}
135449
136326
  process.exitCode = 4;
135450
136327
  return;
135451
136328
  }
135452
- const { join: join135 } = await import("node:path");
135453
- const personaPath = join135(resolvedDir, "persona.cant");
135454
- if (!existsSync135(personaPath)) {
136329
+ const { join: join136 } = await import("node:path");
136330
+ const personaPath = join136(resolvedDir, "persona.cant");
136331
+ if (!existsSync136(personaPath)) {
135455
136332
  cliOutput(
135456
136333
  {
135457
136334
  success: false,
@@ -135490,15 +136367,15 @@ Task ${taskId} reassigned to you by ${active.agentId}. Run: cleo show ${taskId}
135490
136367
  return;
135491
136368
  }
135492
136369
  const archiveStats = statSync22(archivePath);
135493
- const { readdirSync: readdirSync42 } = await import("node:fs");
136370
+ const { readdirSync: readdirSync43 } = await import("node:fs");
135494
136371
  let fileCount = 0;
135495
136372
  const countFiles2 = (dirPath) => {
135496
- const entries = readdirSync42(dirPath, { withFileTypes: true });
136373
+ const entries = readdirSync43(dirPath, { withFileTypes: true });
135497
136374
  for (const entry of entries) {
135498
136375
  if (entry.isFile()) {
135499
136376
  fileCount++;
135500
136377
  } else if (entry.isDirectory()) {
135501
- countFiles2(join135(dirPath, entry.name));
136378
+ countFiles2(join136(dirPath, entry.name));
135502
136379
  }
135503
136380
  }
135504
136381
  };
@@ -135525,9 +136402,9 @@ Task ${taskId} reassigned to you by ${active.agentId}. Run: cleo show ${taskId}
135525
136402
  });
135526
136403
  agent.command("create").description("Scaffold a new agent package with persona.cant and manifest.json").requiredOption("--name <name>", "Agent name (kebab-case)").requiredOption("--role <role>", "Agent role: orchestrator, lead, worker, or docs-worker").option("--tier <tier>", "Agent tier: low, mid, or high (defaults based on role)").option("--team <teamName>", "Team this agent belongs to").option("--domain <description>", "Domain description for file permissions and context").option("--global", "Create in global tier (~/.local/share/cleo/cant/agents/)").option("--seed-brain", "Create expertise/mental-model-seed.md and seed a BRAIN observation").option("--parent <parentAgent>", "Parent agent name in the hierarchy").action(async (opts) => {
135527
136404
  try {
135528
- const { existsSync: existsSync135, mkdirSync: mkdirSync35, writeFileSync: writeFileSync26 } = await import("node:fs");
135529
- const { join: join135 } = await import("node:path");
135530
- const { homedir: homedir8 } = await import("node:os");
136405
+ const { existsSync: existsSync136, mkdirSync: mkdirSync36, writeFileSync: writeFileSync27 } = await import("node:fs");
136406
+ const { join: join136 } = await import("node:path");
136407
+ const { homedir: homedir9 } = await import("node:os");
135531
136408
  const name2 = opts["name"];
135532
136409
  const role = opts["role"];
135533
136410
  const tier = opts["tier"] ?? inferTierFromRole(role);
@@ -135585,14 +136462,14 @@ Task ${taskId} reassigned to you by ${active.agentId}. Run: cleo show ${taskId}
135585
136462
  }
135586
136463
  let targetRoot;
135587
136464
  if (isGlobal) {
135588
- const home = homedir8();
135589
- const xdgData = process.env["XDG_DATA_HOME"] ?? join135(home, ".local", "share");
135590
- targetRoot = join135(xdgData, "cleo", "cant", "agents");
136465
+ const home = homedir9();
136466
+ const xdgData = process.env["XDG_DATA_HOME"] ?? join136(home, ".local", "share");
136467
+ targetRoot = join136(xdgData, "cleo", "cant", "agents");
135591
136468
  } else {
135592
- targetRoot = join135(process.cwd(), ".cleo", "cant", "agents");
136469
+ targetRoot = join136(process.cwd(), ".cleo", "cant", "agents");
135593
136470
  }
135594
- const agentDir = join135(targetRoot, name2);
135595
- if (existsSync135(agentDir)) {
136471
+ const agentDir = join136(targetRoot, name2);
136472
+ if (existsSync136(agentDir)) {
135596
136473
  cliOutput(
135597
136474
  {
135598
136475
  success: false,
@@ -135607,7 +136484,7 @@ Task ${taskId} reassigned to you by ${active.agentId}. Run: cleo show ${taskId}
135607
136484
  process.exitCode = 6;
135608
136485
  return;
135609
136486
  }
135610
- mkdirSync35(agentDir, { recursive: true });
136487
+ mkdirSync36(agentDir, { recursive: true });
135611
136488
  const personaContent = generatePersonaCant({
135612
136489
  name: name2,
135613
136490
  role,
@@ -135616,29 +136493,29 @@ Task ${taskId} reassigned to you by ${active.agentId}. Run: cleo show ${taskId}
135616
136493
  domain: domain2,
135617
136494
  parent
135618
136495
  });
135619
- writeFileSync26(join135(agentDir, "persona.cant"), personaContent, "utf-8");
136496
+ writeFileSync27(join136(agentDir, "persona.cant"), personaContent, "utf-8");
135620
136497
  const manifest = generateManifest2({ name: name2, role, tier, domain: domain2 });
135621
- writeFileSync26(
135622
- join135(agentDir, "manifest.json"),
136498
+ writeFileSync27(
136499
+ join136(agentDir, "manifest.json"),
135623
136500
  `${JSON.stringify(manifest, null, 2)}
135624
136501
  `,
135625
136502
  "utf-8"
135626
136503
  );
135627
136504
  const createdFiles = [
135628
- join135(agentDir, "persona.cant"),
135629
- join135(agentDir, "manifest.json")
136505
+ join136(agentDir, "persona.cant"),
136506
+ join136(agentDir, "manifest.json")
135630
136507
  ];
135631
136508
  if (team) {
135632
136509
  const teamConfigContent = generateTeamConfig(name2, role, team);
135633
- writeFileSync26(join135(agentDir, "team-config.cant"), teamConfigContent, "utf-8");
135634
- createdFiles.push(join135(agentDir, "team-config.cant"));
136510
+ writeFileSync27(join136(agentDir, "team-config.cant"), teamConfigContent, "utf-8");
136511
+ createdFiles.push(join136(agentDir, "team-config.cant"));
135635
136512
  }
135636
136513
  if (seedBrain) {
135637
- const expertiseDir = join135(agentDir, "expertise");
135638
- mkdirSync35(expertiseDir, { recursive: true });
136514
+ const expertiseDir = join136(agentDir, "expertise");
136515
+ mkdirSync36(expertiseDir, { recursive: true });
135639
136516
  const seedContent = generateMentalModelSeed(name2, role, domain2);
135640
- writeFileSync26(join135(expertiseDir, "mental-model-seed.md"), seedContent, "utf-8");
135641
- createdFiles.push(join135(expertiseDir, "mental-model-seed.md"));
136517
+ writeFileSync27(join136(expertiseDir, "mental-model-seed.md"), seedContent, "utf-8");
136518
+ createdFiles.push(join136(expertiseDir, "mental-model-seed.md"));
135642
136519
  try {
135643
136520
  const { execFile: execFile8 } = await import("node:child_process");
135644
136521
  const { promisify: promisify8 } = await import("node:util");
@@ -137509,10 +138386,10 @@ function registerCheckCommand(program) {
137509
138386
  );
137510
138387
  });
137511
138388
  check2.command("chain-validate <file>").description("Validate a WarpChain definition from a JSON file").action(async (file2) => {
137512
- const { readFileSync: readFileSync107 } = await import("node:fs");
138389
+ const { readFileSync: readFileSync108 } = await import("node:fs");
137513
138390
  let chain;
137514
138391
  try {
137515
- chain = JSON.parse(readFileSync107(file2, "utf8"));
138392
+ chain = JSON.parse(readFileSync108(file2, "utf8"));
137516
138393
  } catch (err) {
137517
138394
  const message = err instanceof Error ? err.message : String(err);
137518
138395
  console.error(`Failed to read or parse chain file: ${message}`);
@@ -139956,6 +140833,55 @@ function registerMapCommand(program) {
139956
140833
  init_internal();
139957
140834
  init_cli();
139958
140835
  init_renderers();
140836
+ import { createHash as createHash17 } from "node:crypto";
140837
+ import { existsSync as existsSync135, mkdirSync as mkdirSync35, readdirSync as readdirSync42, readFileSync as readFileSync105, writeFileSync as writeFileSync26 } from "node:fs";
140838
+ import { homedir as homedir8 } from "node:os";
140839
+ import { join as join132 } from "node:path";
140840
+ function parseMemoryFileFrontmatter(raw) {
140841
+ const lines = raw.split("\n");
140842
+ if (!lines[0]?.trim().startsWith("---")) {
140843
+ return { body: raw.trim() };
140844
+ }
140845
+ const endIdx = lines.slice(1).findIndex((l) => /^---\s*$/.test(l));
140846
+ if (endIdx === -1) {
140847
+ return { body: raw.trim() };
140848
+ }
140849
+ const fmLines = lines.slice(1, endIdx + 1);
140850
+ const body = lines.slice(endIdx + 2).join("\n").trim();
140851
+ const fm = {};
140852
+ for (const line2 of fmLines) {
140853
+ const colonIdx = line2.indexOf(":");
140854
+ if (colonIdx === -1) continue;
140855
+ const key = line2.slice(0, colonIdx).trim();
140856
+ const value = line2.slice(colonIdx + 1).trim();
140857
+ if (key && value) fm[key] = value;
140858
+ }
140859
+ return {
140860
+ name: fm["name"],
140861
+ description: fm["description"],
140862
+ type: fm["type"],
140863
+ body
140864
+ };
140865
+ }
140866
+ function memoryContentHash(title, body) {
140867
+ return createHash17("sha256").update(`${title}
140868
+ ${body}`).digest("hex").slice(0, 16);
140869
+ }
140870
+ function loadImportHashes(stateFile) {
140871
+ try {
140872
+ if (!existsSync135(stateFile)) return /* @__PURE__ */ new Set();
140873
+ const raw = readFileSync105(stateFile, "utf-8");
140874
+ const parsed = JSON.parse(raw);
140875
+ return new Set(parsed.hashes);
140876
+ } catch {
140877
+ return /* @__PURE__ */ new Set();
140878
+ }
140879
+ }
140880
+ function saveImportHashes(stateFile, hashes) {
140881
+ const dir = stateFile.slice(0, stateFile.lastIndexOf("/"));
140882
+ if (!existsSync135(dir)) mkdirSync35(dir, { recursive: true });
140883
+ writeFileSync26(stateFile, JSON.stringify({ hashes: [...hashes] }, null, 2), "utf-8");
140884
+ }
139959
140885
  function registerMemoryBrainCommand(program) {
139960
140886
  const memory = program.command("memory").description("BRAIN memory operations (patterns, learnings)");
139961
140887
  memory.command("store").description("Store a pattern or learning to BRAIN memory").requiredOption("--type <type>", "Memory type: pattern or learning").requiredOption("--content <text>", "Content of the memory entry").option("--context <text>", "Context in which the pattern/learning was observed").option("--source <text>", "Source of the learning").option(
@@ -140371,6 +141297,206 @@ function registerMemoryBrainCommand(program) {
140371
141297
  process.exit(1);
140372
141298
  }
140373
141299
  });
141300
+ memory.command("dream").description(
141301
+ "Manually trigger the full auto-dream cycle: consolidation pipeline including R-STDP reward backfill (Step 9a), STDP plasticity (Step 9b), and homeostatic decay (Step 9c). Equivalent to autonomous autonomous nightly consolidation but triggered on demand. Idempotent \u2014 safe to run multiple times."
141302
+ ).option("--json", "Output results as JSON").action(async (opts) => {
141303
+ const root = getProjectRoot();
141304
+ const isJson = !!opts.json;
141305
+ if (!isJson) {
141306
+ console.log("Triggering dream cycle (full consolidation including STDP plasticity)...");
141307
+ }
141308
+ try {
141309
+ const result = await triggerManualDream(root);
141310
+ if (isJson) {
141311
+ console.log(
141312
+ JSON.stringify(
141313
+ {
141314
+ success: true,
141315
+ data: result,
141316
+ meta: {
141317
+ operation: "memory.dream",
141318
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
141319
+ }
141320
+ },
141321
+ null,
141322
+ 2
141323
+ )
141324
+ );
141325
+ return;
141326
+ }
141327
+ console.log("\nDream cycle complete.");
141328
+ console.log(` Deduplicated: ${result.deduplicated}`);
141329
+ console.log(` Quality recomp: ${result.qualityRecomputed}`);
141330
+ console.log(` Tier promoted: ${result.tierPromotions.promoted.length} entries promoted`);
141331
+ console.log(` Tier evicted: ${result.tierPromotions.evicted.length} entries evicted`);
141332
+ console.log(` Contradictions: ${result.contradictions}`);
141333
+ console.log(` Soft evicted: ${result.softEvicted}`);
141334
+ console.log(` Edges strength: ${result.edgesStrengthened}`);
141335
+ console.log(` Summaries gen: ${result.summariesGenerated}`);
141336
+ if (result.graphLinksCreated !== void 0) {
141337
+ console.log(` Graph links: ${result.graphLinksCreated}`);
141338
+ }
141339
+ if (result.rewardBackfilled !== void 0) {
141340
+ console.log(
141341
+ ` Reward backfill: ${result.rewardBackfilled.rowsLabeled} labeled, ${result.rewardBackfilled.rowsSkipped} skipped`
141342
+ );
141343
+ }
141344
+ if (result.stdpPlasticity !== void 0) {
141345
+ console.log(
141346
+ ` STDP plasticity: ${result.stdpPlasticity.ltpEvents} LTP, ${result.stdpPlasticity.ltdEvents} LTD, ${result.stdpPlasticity.edgesCreated} edges created`
141347
+ );
141348
+ }
141349
+ if (result.homeostaticDecay !== void 0) {
141350
+ console.log(
141351
+ ` Decay/pruning: ${result.homeostaticDecay.edgesDecayed} decayed, ${result.homeostaticDecay.edgesPruned} pruned`
141352
+ );
141353
+ }
141354
+ } catch (err) {
141355
+ const message = err instanceof Error ? err.message : String(err);
141356
+ if (isJson) {
141357
+ console.log(JSON.stringify({ success: false, error: message }));
141358
+ } else {
141359
+ console.error(`Dream cycle failed: ${message}`);
141360
+ }
141361
+ process.exit(1);
141362
+ }
141363
+ });
141364
+ memory.command("import").description(
141365
+ "Import memory files from a provider-specific directory (e.g. ~/.claude/projects/*/memory/) into brain.db. Enables provider-agnostic memory via CLEO CLI instead of Claude Code MEMORY.md."
141366
+ ).option(
141367
+ "--from <dir>",
141368
+ "Source directory containing *.md memory files (default: ~/.claude/projects/-mnt-projects-cleocode/memory)"
141369
+ ).option("--dry-run", "Print what would be imported without writing to brain.db").option("--json", "Output results as JSON").action(async (opts) => {
141370
+ const sourceDir = opts.from ?? join132(homedir8(), ".claude", "projects", "-mnt-projects-cleocode", "memory");
141371
+ const isDryRun = !!opts.dryRun;
141372
+ const isJson = !!opts.json;
141373
+ const projectRoot = getProjectRoot();
141374
+ const stateFile = join132(projectRoot, ".cleo", "migrate-memory-hashes.json");
141375
+ if (!existsSync135(sourceDir)) {
141376
+ const msg = `Source directory not found: ${sourceDir}`;
141377
+ if (isJson) {
141378
+ console.log(JSON.stringify({ success: false, error: msg }));
141379
+ } else {
141380
+ console.error(msg);
141381
+ }
141382
+ process.exit(1);
141383
+ }
141384
+ const files = readdirSync42(sourceDir).filter((f2) => f2.endsWith(".md") && f2 !== "MEMORY.md").map((f2) => join132(sourceDir, f2));
141385
+ const importedHashes = isDryRun ? /* @__PURE__ */ new Set() : loadImportHashes(stateFile);
141386
+ const stats2 = { total: files.length, imported: 0, skipped: 0, errors: 0 };
141387
+ const importedEntries = [];
141388
+ const skippedEntries = [];
141389
+ const errorEntries = [];
141390
+ if (!isJson) {
141391
+ console.log(`Importing memory from: ${sourceDir}`);
141392
+ console.log(`Files found: ${files.length}`);
141393
+ if (isDryRun) console.log("Mode: DRY RUN");
141394
+ console.log("");
141395
+ }
141396
+ for (const filePath of files) {
141397
+ const fileName = filePath.split("/").pop() ?? filePath;
141398
+ try {
141399
+ const raw = readFileSync105(filePath, "utf-8");
141400
+ if (!raw.trim()) {
141401
+ stats2.skipped++;
141402
+ skippedEntries.push({ file: fileName, reason: "empty file" });
141403
+ continue;
141404
+ }
141405
+ const { name: name2, description, type, body } = parseMemoryFileFrontmatter(raw);
141406
+ const title = name2 ?? fileName.replace(/\.md$/, "").replace(/-/g, " ");
141407
+ const bodyParts = [description, body].filter(Boolean);
141408
+ const fullText = bodyParts.join("\n\n").trim();
141409
+ if (!fullText) {
141410
+ stats2.skipped++;
141411
+ skippedEntries.push({ file: fileName, reason: "empty body" });
141412
+ continue;
141413
+ }
141414
+ const hash2 = memoryContentHash(title, fullText);
141415
+ if (!isDryRun && importedHashes.has(hash2)) {
141416
+ stats2.skipped++;
141417
+ skippedEntries.push({ file: fileName, reason: `already imported (hash: ${hash2})` });
141418
+ if (!isJson) console.log(` [SKIP] ${fileName}`);
141419
+ continue;
141420
+ }
141421
+ const entryType = type ?? "project";
141422
+ if (!isJson) {
141423
+ const prefix = isDryRun ? "[DRY-RUN]" : "[IMPORT]";
141424
+ console.log(` ${prefix} ${fileName} (type: ${entryType})`);
141425
+ }
141426
+ if (!isDryRun) {
141427
+ if (entryType === "feedback") {
141428
+ await dispatchFromCli(
141429
+ "mutate",
141430
+ "memory",
141431
+ "learning.store",
141432
+ {
141433
+ insight: `[MIGRATED] ${title}: ${fullText}`,
141434
+ source: "manual",
141435
+ confidence: 0.8,
141436
+ actionable: false
141437
+ },
141438
+ { command: "memory", operation: "memory.learning.store" }
141439
+ );
141440
+ } else {
141441
+ const observeType = entryType === "project" ? "feature" : entryType === "reference" ? "discovery" : entryType === "user" ? "change" : "discovery";
141442
+ await dispatchFromCli(
141443
+ "mutate",
141444
+ "memory",
141445
+ "observe",
141446
+ {
141447
+ text: `[MIGRATED] ${title}: ${fullText}`,
141448
+ title: `[MIGRATED] ${title}`,
141449
+ type: observeType,
141450
+ sourceType: "manual"
141451
+ },
141452
+ { command: "memory", operation: "memory.observe" }
141453
+ );
141454
+ }
141455
+ importedHashes.add(hash2);
141456
+ }
141457
+ stats2.imported++;
141458
+ importedEntries.push({ file: fileName, type: entryType, title });
141459
+ } catch (err) {
141460
+ stats2.errors++;
141461
+ const message = err instanceof Error ? err.message : String(err);
141462
+ errorEntries.push({ file: fileName, error: message });
141463
+ if (!isJson) console.error(` [ERROR] ${fileName}: ${message}`);
141464
+ }
141465
+ }
141466
+ if (!isDryRun) {
141467
+ saveImportHashes(stateFile, importedHashes);
141468
+ }
141469
+ if (isJson) {
141470
+ console.log(
141471
+ JSON.stringify(
141472
+ {
141473
+ success: stats2.errors === 0,
141474
+ data: {
141475
+ ...stats2,
141476
+ dryRun: isDryRun,
141477
+ imported: importedEntries,
141478
+ skipped: skippedEntries,
141479
+ errors: errorEntries
141480
+ },
141481
+ meta: {
141482
+ operation: "memory.import",
141483
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
141484
+ }
141485
+ },
141486
+ null,
141487
+ 2
141488
+ )
141489
+ );
141490
+ } else {
141491
+ console.log("");
141492
+ console.log("=== Import Complete ===");
141493
+ console.log(`Total: ${stats2.total}`);
141494
+ console.log(`Imported: ${stats2.imported}`);
141495
+ console.log(`Skipped: ${stats2.skipped}`);
141496
+ console.log(`Errors: ${stats2.errors}`);
141497
+ }
141498
+ if (stats2.errors > 0) process.exit(1);
141499
+ });
140374
141500
  }
140375
141501
 
140376
141502
  // packages/cleo/src/cli/commands/migrate-claude-mem.ts
@@ -141828,17 +142954,17 @@ function registerNexusCommand(program) {
141828
142954
  const autoRegister = !!opts["autoRegister"];
141829
142955
  const includeExisting = !!opts["includeExisting"];
141830
142956
  const maxDepth = Math.max(1, Math.min(opts["maxDepth"] ?? 4, 20));
141831
- const { homedir: homedir8 } = await import("node:os");
141832
- const home = homedir8();
142957
+ const { homedir: homedir9 } = await import("node:os");
142958
+ const home = homedir9();
141833
142959
  const defaultRoots = [path10.join(home, "code"), path10.join(home, "projects"), "/mnt/projects"];
141834
142960
  const rawRoots = typeof opts["roots"] === "string" && opts["roots"].trim().length > 0 ? opts["roots"].split(",").map((r) => r.trim()).filter((r) => r.length > 0).map(
141835
142961
  (r) => r.startsWith("~") ? path10.join(home, r.slice(1)) : path10.resolve(r)
141836
142962
  ) : defaultRoots;
141837
- const { existsSync: existsSync135, readdirSync: readdirSync42, statSync: statSync22 } = await import("node:fs");
142963
+ const { existsSync: existsSync136, readdirSync: readdirSync43, statSync: statSync22 } = await import("node:fs");
141838
142964
  const { Dirent } = await import("node:fs");
141839
142965
  const roots = rawRoots.filter((r) => {
141840
142966
  try {
141841
- return existsSync135(r) && statSync22(r).isDirectory();
142967
+ return existsSync136(r) && statSync22(r).isDirectory();
141842
142968
  } catch {
141843
142969
  return false;
141844
142970
  }
@@ -141879,7 +143005,7 @@ function registerNexusCommand(program) {
141879
143005
  if (depth > maxDepth) return [];
141880
143006
  let entries;
141881
143007
  try {
141882
- entries = readdirSync42(dir, { withFileTypes: true });
143008
+ entries = readdirSync43(dir, { withFileTypes: true });
141883
143009
  } catch {
141884
143010
  return [];
141885
143011
  }
@@ -142422,8 +143548,8 @@ function registerNexusCommand(program) {
142422
143548
  return;
142423
143549
  }
142424
143550
  if (outputFile) {
142425
- const { writeFileSync: writeFileSync26 } = await import("node:fs");
142426
- writeFileSync26(outputFile, output, "utf-8");
143551
+ const { writeFileSync: writeFileSync27 } = await import("node:fs");
143552
+ writeFileSync27(outputFile, output, "utf-8");
142427
143553
  process.stdout.write(
142428
143554
  `[nexus] Exported to ${outputFile} (${nodes.length} nodes, ${relations.length} edges)
142429
143555
  `
@@ -144255,7 +145381,7 @@ init_src();
144255
145381
  init_internal();
144256
145382
  import { execFile as execFile7 } from "node:child_process";
144257
145383
  import { readFile as readFile23 } from "node:fs/promises";
144258
- import { join as join132 } from "node:path";
145384
+ import { join as join133 } from "node:path";
144259
145385
  import * as readline2 from "node:readline";
144260
145386
  import { promisify as promisify7 } from "node:util";
144261
145387
  init_renderers();
@@ -144264,7 +145390,7 @@ var GITHUB_REPO = BUILD_CONFIG.repository.fullName;
144264
145390
  async function getCurrentVersion() {
144265
145391
  const cleoHome = getCleoHome();
144266
145392
  try {
144267
- const content = await readFile23(join132(cleoHome, "VERSION"), "utf-8");
145393
+ const content = await readFile23(join133(cleoHome, "VERSION"), "utf-8");
144268
145394
  return (content.split("\n")[0] ?? "unknown").trim();
144269
145395
  } catch {
144270
145396
  return "unknown";
@@ -144318,7 +145444,7 @@ async function writeRuntimeVersionMetadata(mode, source, version2) {
144318
145444
  ];
144319
145445
  await import("node:fs/promises").then(
144320
145446
  ({ writeFile: writeFile15, mkdir: mkdir20 }) => mkdir20(cleoHome, { recursive: true }).then(
144321
- () => writeFile15(join132(cleoHome, "VERSION"), `${lines.join("\n")}
145447
+ () => writeFile15(join133(cleoHome, "VERSION"), `${lines.join("\n")}
144322
145448
  `, "utf-8")
144323
145449
  )
144324
145450
  );
@@ -145371,10 +146497,10 @@ function registerSyncCommand(program) {
145371
146497
  "How to resolve conflicts: keep-cleo, keep-external, or newest (default: keep-cleo)",
145372
146498
  "keep-cleo"
145373
146499
  ).action(async (file2, opts) => {
145374
- const { readFileSync: readFileSync107 } = await import("node:fs");
146500
+ const { readFileSync: readFileSync108 } = await import("node:fs");
145375
146501
  let externalTasks;
145376
146502
  try {
145377
- externalTasks = JSON.parse(readFileSync107(file2, "utf8"));
146503
+ externalTasks = JSON.parse(readFileSync108(file2, "utf8"));
145378
146504
  } catch (err) {
145379
146505
  const message = err instanceof Error ? err.message : String(err);
145380
146506
  console.error(`Failed to read or parse external tasks file: ${message}`);
@@ -145470,11 +146596,11 @@ function registerTestingCommand(program) {
145470
146596
  init_internal();
145471
146597
  init_cli();
145472
146598
  init_renderers();
145473
- import { readFileSync as readFileSync105 } from "node:fs";
146599
+ import { readFileSync as readFileSync106 } from "node:fs";
145474
146600
  function readPayload(opts, textKey, fileKey) {
145475
146601
  const text3 = opts[textKey];
145476
146602
  const file2 = opts[fileKey];
145477
- if (file2) return readFileSync105(file2, "utf-8");
146603
+ if (file2) return readFileSync106(file2, "utf-8");
145478
146604
  return text3;
145479
146605
  }
145480
146606
  function registerTokenCommand(program) {
@@ -145724,16 +146850,16 @@ init_src3();
145724
146850
  init_renderers();
145725
146851
  import { execFileSync as execFileSync18, spawn as spawn2 } from "node:child_process";
145726
146852
  import { mkdir as mkdir19, open, readFile as readFile24, rm as rm3, stat as stat3, writeFile as writeFile14 } from "node:fs/promises";
145727
- import { join as join133 } from "node:path";
146853
+ import { join as join134 } from "node:path";
145728
146854
  var DEFAULT_PORT = 3456;
145729
146855
  var DEFAULT_HOST = "127.0.0.1";
145730
146856
  function getWebPaths() {
145731
146857
  const cleoHome = getCleoHome();
145732
146858
  return {
145733
- pidFile: join133(cleoHome, "web-server.pid"),
145734
- configFile: join133(cleoHome, "web-server.json"),
145735
- logDir: join133(cleoHome, "logs"),
145736
- logFile: join133(cleoHome, "logs", "web-server.log")
146859
+ pidFile: join134(cleoHome, "web-server.pid"),
146860
+ configFile: join134(cleoHome, "web-server.json"),
146861
+ logDir: join134(cleoHome, "logs"),
146862
+ logFile: join134(cleoHome, "logs", "web-server.log")
145737
146863
  };
145738
146864
  }
145739
146865
  function isProcessRunning(pid) {
@@ -145765,99 +146891,99 @@ async function getStatus() {
145765
146891
  return { running: false, pid: null, port: null, host: null, url: null };
145766
146892
  }
145767
146893
  }
146894
+ async function startWebServer(port, host) {
146895
+ const { pidFile, configFile, logFile, logDir } = getWebPaths();
146896
+ const status = await getStatus();
146897
+ if (status.running) {
146898
+ throw new CleoError(1 /* GENERAL_ERROR */, `Server already running (PID: ${status.pid})`);
146899
+ }
146900
+ const projectRoot = process.env["CLEO_ROOT"] ?? process.cwd();
146901
+ const studioDir = process.env["CLEO_STUDIO_DIR"] ?? join134(projectRoot, "packages", "studio", "build");
146902
+ await mkdir19(logDir, { recursive: true });
146903
+ await writeFile14(
146904
+ configFile,
146905
+ JSON.stringify({
146906
+ port,
146907
+ host,
146908
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
146909
+ })
146910
+ );
146911
+ const webIndexPath = join134(studioDir, "index.js");
146912
+ try {
146913
+ await stat3(webIndexPath);
146914
+ } catch {
146915
+ try {
146916
+ execFileSync18("pnpm", ["--filter", "@cleocode/studio", "run", "build"], {
146917
+ cwd: projectRoot,
146918
+ stdio: "ignore"
146919
+ });
146920
+ } catch {
146921
+ throw new CleoError(
146922
+ 1 /* GENERAL_ERROR */,
146923
+ `Studio build failed. Run: pnpm --filter @cleocode/studio run build
146924
+ Logs: ${logFile}`
146925
+ );
146926
+ }
146927
+ }
146928
+ const logFileHandle = await open(logFile, "a");
146929
+ const serverProcess = spawn2("node", [webIndexPath], {
146930
+ cwd: studioDir,
146931
+ env: {
146932
+ ...process.env,
146933
+ HOST: host,
146934
+ PORT: String(port),
146935
+ // Pass CLEO paths through to the studio server
146936
+ CLEO_ROOT: projectRoot
146937
+ },
146938
+ detached: true,
146939
+ stdio: ["ignore", logFileHandle.fd, logFileHandle.fd]
146940
+ });
146941
+ serverProcess.unref();
146942
+ const pidFileTmp = `${pidFile}.tmp`;
146943
+ await writeFile14(pidFileTmp, String(serverProcess.pid));
146944
+ await rm3(pidFile, { force: true });
146945
+ await writeFile14(pidFile, String(serverProcess.pid));
146946
+ await rm3(pidFileTmp, { force: true });
146947
+ await logFileHandle.close();
146948
+ const maxAttempts = 30;
146949
+ let started = false;
146950
+ for (let i = 0; i < maxAttempts; i++) {
146951
+ try {
146952
+ const response = await fetch(`http://${host}:${port}/api/health`);
146953
+ if (response.ok) {
146954
+ started = true;
146955
+ break;
146956
+ }
146957
+ } catch {
146958
+ }
146959
+ await new Promise((resolve17) => setTimeout(resolve17, 500));
146960
+ }
146961
+ if (!started) {
146962
+ try {
146963
+ process.kill(serverProcess.pid);
146964
+ } catch {
146965
+ }
146966
+ await rm3(pidFile, { force: true });
146967
+ throw new CleoError(1 /* GENERAL_ERROR */, "Server failed to start within 15 seconds");
146968
+ }
146969
+ cliOutput(
146970
+ {
146971
+ pid: serverProcess.pid,
146972
+ port,
146973
+ host,
146974
+ url: `http://${host}:${port}`,
146975
+ logFile
146976
+ },
146977
+ { command: "web", message: `CLEO Web UI running on port ${port}` }
146978
+ );
146979
+ }
145768
146980
  function registerWebCommand(program) {
145769
146981
  const webCmd = program.command("web").description("Manage CLEO Web UI server");
145770
146982
  webCmd.command("start").description("Start the web server").option("--port <port>", "Server port", String(DEFAULT_PORT)).option("--host <host>", "Server host", DEFAULT_HOST).action(async (opts) => {
145771
146983
  try {
145772
146984
  const port = parseInt(opts["port"], 10);
145773
146985
  const host = opts["host"];
145774
- const { pidFile, configFile, logFile, logDir } = getWebPaths();
145775
- const status = await getStatus();
145776
- if (status.running) {
145777
- throw new CleoError(
145778
- 1 /* GENERAL_ERROR */,
145779
- `Server already running (PID: ${status.pid})`
145780
- );
145781
- }
145782
- const projectRoot = process.env["CLEO_ROOT"] ?? process.cwd();
145783
- const studioDir = process.env["CLEO_STUDIO_DIR"] ?? join133(projectRoot, "packages", "studio", "build");
145784
- await mkdir19(logDir, { recursive: true });
145785
- await writeFile14(
145786
- configFile,
145787
- JSON.stringify({
145788
- port,
145789
- host,
145790
- startedAt: (/* @__PURE__ */ new Date()).toISOString()
145791
- })
145792
- );
145793
- const webIndexPath = join133(studioDir, "index.js");
145794
- try {
145795
- await stat3(webIndexPath);
145796
- } catch {
145797
- try {
145798
- execFileSync18("pnpm", ["--filter", "@cleocode/studio", "run", "build"], {
145799
- cwd: projectRoot,
145800
- stdio: "ignore"
145801
- });
145802
- } catch {
145803
- throw new CleoError(
145804
- 1 /* GENERAL_ERROR */,
145805
- `Studio build failed. Run: pnpm --filter @cleocode/studio run build
145806
- Logs: ${logFile}`
145807
- );
145808
- }
145809
- }
145810
- const logFileHandle = await open(logFile, "a");
145811
- const serverProcess = spawn2("node", [webIndexPath], {
145812
- cwd: studioDir,
145813
- env: {
145814
- ...process.env,
145815
- HOST: host,
145816
- PORT: String(port),
145817
- // Pass CLEO paths through to the studio server
145818
- CLEO_ROOT: projectRoot
145819
- },
145820
- detached: true,
145821
- stdio: ["ignore", logFileHandle.fd, logFileHandle.fd]
145822
- });
145823
- serverProcess.unref();
145824
- const pidFileTmp = `${pidFile}.tmp`;
145825
- await writeFile14(pidFileTmp, String(serverProcess.pid));
145826
- await rm3(pidFile, { force: true });
145827
- await writeFile14(pidFile, String(serverProcess.pid));
145828
- await rm3(pidFileTmp, { force: true });
145829
- await logFileHandle.close();
145830
- const maxAttempts = 30;
145831
- let started = false;
145832
- for (let i = 0; i < maxAttempts; i++) {
145833
- try {
145834
- const response = await fetch(`http://${host}:${port}/api/health`);
145835
- if (response.ok) {
145836
- started = true;
145837
- break;
145838
- }
145839
- } catch {
145840
- }
145841
- await new Promise((resolve17) => setTimeout(resolve17, 500));
145842
- }
145843
- if (!started) {
145844
- try {
145845
- process.kill(serverProcess.pid);
145846
- } catch {
145847
- }
145848
- await rm3(pidFile, { force: true });
145849
- throw new CleoError(1 /* GENERAL_ERROR */, "Server failed to start within 15 seconds");
145850
- }
145851
- cliOutput(
145852
- {
145853
- pid: serverProcess.pid,
145854
- port,
145855
- host,
145856
- url: `http://${host}:${port}`,
145857
- logFile
145858
- },
145859
- { command: "web", message: `CLEO Web UI running on port ${port}` }
145860
- );
146986
+ await startWebServer(port, host);
145861
146987
  } catch (err) {
145862
146988
  if (err instanceof CleoError) {
145863
146989
  console.error(formatError3(err));
@@ -145936,16 +147062,9 @@ Logs: ${logFile}`
145936
147062
  }
145937
147063
  await rm3(pidFile, { force: true });
145938
147064
  }
145939
- const port = opts["port"] ?? String(DEFAULT_PORT);
147065
+ const port = parseInt(opts["port"] ?? String(DEFAULT_PORT), 10);
145940
147066
  const host = opts["host"] ?? DEFAULT_HOST;
145941
- const startOpts = { port, host };
145942
- const startCmd = webCmd.commands.find((c) => c.name() === "start");
145943
- const startAction = startCmd?.action;
145944
- if (startAction) {
145945
- await startAction(startOpts);
145946
- } else {
145947
- throw new CleoError(1 /* GENERAL_ERROR */, "Could not restart server");
145948
- }
147067
+ await startWebServer(port, host);
145949
147068
  } catch (err) {
145950
147069
  if (err instanceof CleoError) {
145951
147070
  console.error(formatError3(err));
@@ -146019,9 +147138,9 @@ var codeCommand = defineCommand({
146019
147138
  async run({ args }) {
146020
147139
  await requireTreeSitter();
146021
147140
  const { smartOutline: smartOutline2 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
146022
- const { join: join135 } = await import("node:path");
147141
+ const { join: join136 } = await import("node:path");
146023
147142
  const root = process.cwd();
146024
- const absPath = args.file.startsWith("/") ? args.file : join135(root, args.file);
147143
+ const absPath = args.file.startsWith("/") ? args.file : join136(root, args.file);
146025
147144
  const result = smartOutline2(absPath, root);
146026
147145
  if (result.errors.length > 0 && result.symbols.length === 0) {
146027
147146
  console.error(`Error: ${result.errors.join(", ")}`);
@@ -146087,9 +147206,9 @@ var codeCommand = defineCommand({
146087
147206
  async run({ args }) {
146088
147207
  await requireTreeSitter();
146089
147208
  const { smartUnfold: smartUnfold2 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
146090
- const { join: join135 } = await import("node:path");
147209
+ const { join: join136 } = await import("node:path");
146091
147210
  const root = process.cwd();
146092
- const absPath = args.file.startsWith("/") ? args.file : join135(root, args.file);
147211
+ const absPath = args.file.startsWith("/") ? args.file : join136(root, args.file);
146093
147212
  const result = smartUnfold2(absPath, args.symbol, root);
146094
147213
  if (!result.found) {
146095
147214
  console.error(`Symbol "${args.symbol}" not found in ${args.file}`);
@@ -146108,8 +147227,8 @@ var codeCommand = defineCommand({
146108
147227
 
146109
147228
  // packages/cleo/src/cli/index.ts
146110
147229
  function getPackageVersion() {
146111
- const pkgPath = join134(dirname31(fileURLToPath8(import.meta.url)), "../../package.json");
146112
- const pkg = JSON.parse(readFileSync106(pkgPath, "utf-8"));
147230
+ const pkgPath = join135(dirname31(fileURLToPath8(import.meta.url)), "../../package.json");
147231
+ const pkg = JSON.parse(readFileSync107(pkgPath, "utf-8"));
146113
147232
  return pkg.version;
146114
147233
  }
146115
147234
  var CLI_VERSION = getPackageVersion();