@cleocode/core 2026.4.98 → 2026.4.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/dist/gc/daemon-entry.d.ts +15 -0
  2. package/dist/gc/daemon-entry.d.ts.map +1 -0
  3. package/dist/gc/daemon.d.ts +71 -0
  4. package/dist/gc/daemon.d.ts.map +1 -0
  5. package/dist/gc/daemon.js +481 -0
  6. package/dist/gc/daemon.js.map +7 -0
  7. package/dist/gc/index.d.ts +14 -0
  8. package/dist/gc/index.d.ts.map +1 -0
  9. package/dist/gc/index.js +669 -0
  10. package/dist/gc/index.js.map +7 -0
  11. package/dist/gc/runner.d.ts +132 -0
  12. package/dist/gc/runner.d.ts.map +1 -0
  13. package/dist/gc/runner.js +360 -0
  14. package/dist/gc/runner.js.map +7 -0
  15. package/dist/gc/state.d.ts +94 -0
  16. package/dist/gc/state.d.ts.map +1 -0
  17. package/dist/gc/state.js +49 -0
  18. package/dist/gc/state.js.map +7 -0
  19. package/dist/gc/transcript.d.ts +130 -0
  20. package/dist/gc/transcript.d.ts.map +1 -0
  21. package/dist/gc/transcript.js +209 -0
  22. package/dist/gc/transcript.js.map +7 -0
  23. package/dist/memory/brain-backfill.js +14643 -0
  24. package/dist/memory/brain-backfill.js.map +7 -0
  25. package/dist/memory/precompact-flush.js +47725 -0
  26. package/dist/memory/precompact-flush.js.map +7 -0
  27. package/dist/sentient/daemon-entry.d.ts +11 -0
  28. package/dist/sentient/daemon-entry.d.ts.map +1 -0
  29. package/dist/sentient/daemon.d.ts +160 -0
  30. package/dist/sentient/daemon.d.ts.map +1 -0
  31. package/dist/sentient/daemon.js +1100 -0
  32. package/dist/sentient/daemon.js.map +7 -0
  33. package/dist/sentient/index.d.ts +18 -0
  34. package/dist/sentient/index.d.ts.map +1 -0
  35. package/dist/sentient/index.js +1162 -0
  36. package/dist/sentient/index.js.map +7 -0
  37. package/dist/sentient/ingesters/brain-ingester.d.ts +44 -0
  38. package/dist/sentient/ingesters/brain-ingester.d.ts.map +1 -0
  39. package/dist/sentient/ingesters/nexus-ingester.d.ts +45 -0
  40. package/dist/sentient/ingesters/nexus-ingester.d.ts.map +1 -0
  41. package/dist/sentient/ingesters/test-ingester.d.ts +43 -0
  42. package/dist/sentient/ingesters/test-ingester.d.ts.map +1 -0
  43. package/dist/sentient/proposal-rate-limiter.d.ts +93 -0
  44. package/dist/sentient/proposal-rate-limiter.d.ts.map +1 -0
  45. package/dist/sentient/propose-tick.d.ts +105 -0
  46. package/dist/sentient/propose-tick.d.ts.map +1 -0
  47. package/dist/sentient/propose-tick.js +549 -0
  48. package/dist/sentient/propose-tick.js.map +7 -0
  49. package/dist/sentient/state.d.ts +143 -0
  50. package/dist/sentient/state.d.ts.map +1 -0
  51. package/dist/sentient/state.js +85 -0
  52. package/dist/sentient/state.js.map +7 -0
  53. package/dist/sentient/tick.d.ts +193 -0
  54. package/dist/sentient/tick.d.ts.map +1 -0
  55. package/dist/sentient/tick.js +396 -0
  56. package/dist/sentient/tick.js.map +7 -0
  57. package/dist/system/platform-paths.js +36 -0
  58. package/dist/system/platform-paths.js.map +7 -0
  59. package/package.json +76 -8
  60. package/src/gc/__tests__/runner.test.ts +367 -0
  61. package/src/gc/__tests__/state.test.ts +169 -0
  62. package/src/gc/__tests__/transcript.test.ts +371 -0
  63. package/src/gc/daemon-entry.ts +26 -0
  64. package/src/gc/daemon.ts +251 -0
  65. package/src/gc/index.ts +14 -0
  66. package/src/gc/runner.ts +378 -0
  67. package/src/gc/state.ts +140 -0
  68. package/src/gc/transcript.ts +380 -0
  69. package/src/sentient/__tests__/brain-ingester.test.ts +154 -0
  70. package/src/sentient/__tests__/daemon.test.ts +472 -0
  71. package/src/sentient/__tests__/dream-tick.test.ts +200 -0
  72. package/src/sentient/__tests__/nexus-ingester.test.ts +138 -0
  73. package/src/sentient/__tests__/proposal-rate-limiter.test.ts +247 -0
  74. package/src/sentient/__tests__/propose-tick.test.ts +296 -0
  75. package/src/sentient/__tests__/test-ingester.test.ts +104 -0
  76. package/src/sentient/daemon-entry.ts +20 -0
  77. package/src/sentient/daemon.ts +471 -0
  78. package/src/sentient/index.ts +18 -0
  79. package/src/sentient/ingesters/brain-ingester.ts +122 -0
  80. package/src/sentient/ingesters/nexus-ingester.ts +171 -0
  81. package/src/sentient/ingesters/test-ingester.ts +205 -0
  82. package/src/sentient/proposal-rate-limiter.ts +172 -0
  83. package/src/sentient/propose-tick.ts +415 -0
  84. package/src/sentient/state.ts +229 -0
  85. package/src/sentient/tick.ts +688 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/sentient/daemon.ts", "../../src/sentient/ingesters/brain-ingester.ts", "../../src/sentient/ingesters/nexus-ingester.ts", "../../src/sentient/ingesters/test-ingester.ts", "../../src/sentient/proposal-rate-limiter.ts", "../../src/sentient/state.ts", "../../src/sentient/propose-tick.ts", "../../src/sentient/tick.ts"],
4
+ "sourcesContent": ["/**\n * Sentient Daemon \u2014 Tier-1 autonomous loop sidecar.\n *\n * Runs as a detached Node.js process and executes `runTick()` every 5\n * minutes. Mirrors the gc/daemon.ts sidecar pattern (ADR-047) and honours\n * the worktree protocol \u2014 all state lives under the project's `.cleo/`.\n *\n * Scoped IN (this module):\n * - Tier-1 execution of unblocked tasks via `cleo orchestrate spawn`\n * - Kill-switch with re-check at every tick checkpoint\n * - Advisory locking via an OS-level lockfile (two daemons cannot coexist)\n * - Stuck detection + self-pause on stuck-rate threshold\n * - fs.watch-based fast kill propagation\n *\n * Scoped OUT (separate epics):\n * - Tier-2 proposal queue (`cleo propose` / status='proposed' generation)\n * - Tier-3 sandbox auto-merge (requires agent-in-container infra)\n * - Ed25519 signing of receipts (handled by Agent B2 llmtxt/identity wiring)\n *\n * @see ADR-054 \u2014 Sentient Loop Tier-1\n * @task T946\n */\n\nimport { spawn } from 'node:child_process';\nimport type { FSWatcher } from 'node:fs';\nimport { createWriteStream, constants as fsConstants, watch } from 'node:fs';\nimport { type FileHandle, open as fsOpen, mkdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport cron from 'node-cron';\nimport { type ProposeTickOptions, safeRunProposeTick } from './propose-tick.js';\nimport { patchSentientState, readSentientState, type SentientState } from './state.js';\nimport { safeRunTick, type TickOptions } from './tick.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Relative subpath under a project root where sentient state lives. */\nexport const SENTIENT_STATE_FILE = '.cleo/sentient-state.json' as const;\n\n/** Relative subpath for the daemon lockfile. */\nexport const SENTIENT_LOCK_FILE = '.cleo/sentient.lock' as const;\n\n/** Cron expression: every 5 minutes (Tier-1 tick). */\nexport const SENTIENT_CRON_EXPR = '*/5 * * * *';\n\n/**\n * Cron expression: every 2 hours (Tier-2 propose tick).\n *\n * Separate from the Tier-1 cron to avoid proposal flooding.\n * Only fires when `tier2Enabled = true` in sentient-state.json.\n *\n * @task T1008\n */\nexport const SENTIENT_PROPOSE_CRON_EXPR = '0 */2 * * *';\n\n/** Subdirectory for daemon logs. */\nexport const SENTIENT_LOG_DIR = '.cleo/logs' as const;\n\n/** Log filename (stdout). */\nexport const SENTIENT_LOG = 'sentient.log' as const;\n\n/** Log filename (stderr). */\nexport const SENTIENT_ERR = 'sentient.err' as const;\n\n// ---------------------------------------------------------------------------\n// Advisory lock\n// ---------------------------------------------------------------------------\n\n/** Handle to an active advisory lock. */\nexport interface LockHandle {\n /** Absolute path to the lockfile. */\n path: string;\n /** Underlying file handle held exclusively by this process. */\n handle: FileHandle;\n}\n\n/**\n * Acquire an exclusive advisory lock on the sentient lockfile.\n *\n * Uses `fs.open` with `O_CREAT | O_EXCL` semantics \u2014 if the file already\n * exists AND its recorded pid is alive, acquisition fails. Stale lockfiles\n * (pid dead) are reclaimed automatically.\n *\n * @param lockPath - Absolute path to `.cleo/sentient.lock`\n * @returns Lock handle, or null if lock is held by a live process\n */\nexport async function acquireLock(lockPath: string): Promise<LockHandle | null> {\n await mkdir(join(lockPath, '..'), { recursive: true });\n\n // First attempt: atomic create with O_EXCL.\n try {\n const handle = await fsOpen(\n lockPath,\n fsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_RDWR,\n 0o644,\n );\n await handle.writeFile(String(process.pid), 'utf-8');\n return { path: lockPath, handle };\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code !== 'EEXIST') throw err;\n }\n\n // Lockfile exists. Check if the recorded pid is alive.\n let existing: FileHandle | null = null;\n try {\n existing = await fsOpen(lockPath, fsConstants.O_RDWR);\n const buf = await existing.readFile({ encoding: 'utf-8' });\n const recordedPid = Number.parseInt(buf.trim(), 10);\n if (Number.isFinite(recordedPid) && recordedPid > 0) {\n try {\n process.kill(recordedPid, 0);\n // Process alive \u2014 lock is held.\n await existing.close();\n return null;\n } catch {\n // Process dead \u2014 fall through to reclaim.\n }\n }\n // Reclaim: truncate, rewind, write our pid, keep the handle.\n await existing.truncate(0);\n const pidBytes = Buffer.from(String(process.pid), 'utf-8');\n await existing.write(pidBytes, 0, pidBytes.length, 0);\n return { path: lockPath, handle: existing };\n } catch (err) {\n if (existing) {\n try {\n await existing.close();\n } catch {\n // ignore\n }\n }\n throw err;\n }\n}\n\n/**\n * Release an advisory lock acquired via {@link acquireLock}.\n * Does NOT remove the lockfile \u2014 the pid inside is a useful diagnostic.\n */\nexport async function releaseLock(lock: LockHandle): Promise<void> {\n try {\n await lock.handle.close();\n } catch {\n // ignore\n }\n}\n\n// ---------------------------------------------------------------------------\n// Daemon bootstrap\n// ---------------------------------------------------------------------------\n\n/**\n * Bootstrap the sentient daemon process.\n *\n * Steps:\n * 1. Acquire advisory lock (fail fast if another daemon is running)\n * 2. Persist our pid + startedAt to state.json\n * 3. Watch state.json for killSwitch changes (fast propagation)\n * 4. Register a SIGTERM handler for graceful shutdown\n * 5. Schedule cron with noOverlap so long ticks don't stack\n *\n * @param projectRoot - Absolute path to the project (contains `.cleo/`)\n */\nexport async function bootstrapDaemon(projectRoot: string): Promise<void> {\n const statePath = join(projectRoot, SENTIENT_STATE_FILE);\n const lockPath = join(projectRoot, SENTIENT_LOCK_FILE);\n\n const lock = await acquireLock(lockPath);\n if (!lock) {\n process.stderr.write(`[CLEO SENTIENT] lock acquisition failed \u2014 another daemon is running\\n`);\n process.exit(2);\n }\n\n // Reset pid + counters baseline for this run.\n await patchSentientState(statePath, {\n pid: process.pid,\n startedAt: new Date().toISOString(),\n // Clear killSwitch on boot only if owner did not explicitly leave it set.\n // We preserve it here: re-starting a killed daemon must not silently\n // resume. Owner explicitly clears via `cleo sentient resume`.\n });\n\n // fs.watch on state file \u2014 lets us surface kill very quickly.\n let watcher: FSWatcher | null = null;\n try {\n watcher = watch(statePath, { persistent: false }, () => {\n // Just log \u2014 actual kill-switch check happens inside each tick. The\n // file watcher exists so that an active tick can notice flipping\n // without waiting for the 5-min cadence. Ticks re-read state at every\n // checkpoint (Round 2 audit \u00A71).\n });\n } catch {\n watcher = null;\n }\n\n // Graceful shutdown.\n const shutdown = async (reason: string): Promise<void> => {\n try {\n watcher?.close();\n } catch {\n // ignore\n }\n try {\n await patchSentientState(statePath, {\n pid: null,\n killSwitchReason: reason,\n });\n } catch {\n // ignore\n }\n try {\n await releaseLock(lock);\n } catch {\n // ignore\n }\n process.exit(0);\n };\n process.on('SIGTERM', () => {\n void shutdown('SIGTERM');\n });\n process.on('SIGINT', () => {\n void shutdown('SIGINT');\n });\n\n // Kick off one tick immediately, then schedule cron.\n const tickOptions: TickOptions = { projectRoot, statePath };\n const outcome = await safeRunTick(tickOptions);\n process.stdout.write(\n `[CLEO SENTIENT] boot tick: ${outcome.kind} ` +\n `(task=${outcome.taskId ?? 'n/a'}) ${outcome.detail}\\n`,\n );\n\n // Tier-1: every 5 minutes\n cron.schedule(\n SENTIENT_CRON_EXPR,\n async () => {\n const result = await safeRunTick(tickOptions);\n process.stdout.write(\n `[CLEO SENTIENT] tick: ${result.kind} ` +\n `(task=${result.taskId ?? 'n/a'}) ${result.detail}\\n`,\n );\n },\n {\n timezone: 'UTC',\n noOverlap: true,\n name: 'cleo-sentient',\n },\n );\n\n // Tier-2: every 2 hours (only when tier2Enabled = true)\n // Runs under the same advisory lock as the Tier-1 cron \u2014 the lock is held\n // for the lifetime of the daemon process, so both crons run inside it.\n const proposeOptions: ProposeTickOptions = { projectRoot, statePath };\n cron.schedule(\n SENTIENT_PROPOSE_CRON_EXPR,\n async () => {\n const state = await readSentientState(statePath);\n if (!state.tier2Enabled) return;\n const result = await safeRunProposeTick(proposeOptions);\n process.stdout.write(\n `[CLEO SENTIENT T2] propose: ${result.kind} ` +\n `(written=${result.written}, count=${result.count}) ${result.detail}\\n`,\n );\n },\n {\n timezone: 'UTC',\n noOverlap: true,\n name: 'cleo-sentient-propose',\n },\n );\n}\n\n// ---------------------------------------------------------------------------\n// Spawn helpers (parent-process side)\n// ---------------------------------------------------------------------------\n\n/** Outcome of {@link spawnSentientDaemon}. */\nexport interface SpawnDaemonResult {\n /** PID of the spawned daemon. */\n pid: number;\n /** Absolute path to the .cleo/sentient-state.json file. */\n statePath: string;\n /** Absolute path to the log file. */\n logPath: string;\n}\n\n/**\n * Spawn the sentient daemon as a detached background process.\n *\n * All three T751 \u00A72.2 requirements:\n * 1. `detached: true` \u2014 process-group leader survives parent exit\n * 2. File-based stdio \u2014 no TTY inheritance\n * 3. `child.unref()` \u2014 parent CLI returns immediately\n *\n * @param projectRoot - Absolute path to the project root (contains `.cleo/`)\n * @returns PID + log paths\n */\nexport async function spawnSentientDaemon(projectRoot: string): Promise<SpawnDaemonResult> {\n const logsDir = join(projectRoot, SENTIENT_LOG_DIR);\n await mkdir(logsDir, { recursive: true });\n\n const logPath = join(logsDir, SENTIENT_LOG);\n const errPath = join(logsDir, SENTIENT_ERR);\n\n const outStream = createWriteStream(logPath, { flags: 'a' });\n const errStream = createWriteStream(errPath, { flags: 'a' });\n\n // Resolve daemon-entry.js in the compiled output (sibling to this module).\n const daemonEntry = join(fileURLToPath(import.meta.url), '..', 'daemon-entry.js');\n\n const child = spawn(process.execPath, [daemonEntry, projectRoot], {\n detached: true,\n stdio: ['ignore', outStream, errStream],\n env: { ...process.env, CLEO_SENTIENT_DAEMON: '1' },\n });\n\n child.unref();\n\n const pid = child.pid ?? 0;\n const statePath = join(projectRoot, SENTIENT_STATE_FILE);\n\n // Pre-persist our pid so subsequent `cleo sentient status` calls can find it\n // even before the child writes its own pid.\n await patchSentientState(statePath, {\n pid,\n startedAt: new Date().toISOString(),\n });\n\n return { pid, statePath, logPath };\n}\n\n/** Outcome of {@link stopSentientDaemon}. */\nexport interface StopDaemonResult {\n /** Whether the stop signal was delivered. */\n stopped: boolean;\n /** Last known pid; null if no pid was recorded. */\n pid: number | null;\n /** Human-readable reason. */\n reason: string;\n}\n\n/**\n * Stop the sentient daemon.\n *\n * Flips killSwitch=true FIRST (so an in-flight tick notices on its next\n * checkpoint re-read), then sends SIGTERM. This gives the daemon a fast,\n * graceful shutdown path even during a long-running spawn.\n *\n * @param projectRoot - Absolute path to the project root\n * @param reason - Optional reason stored on state file for diagnostics\n * @returns Stop result\n */\nexport async function stopSentientDaemon(\n projectRoot: string,\n reason = 'cleo sentient stop',\n): Promise<StopDaemonResult> {\n const statePath = join(projectRoot, SENTIENT_STATE_FILE);\n const state = await readSentientState(statePath);\n\n // Flip killSwitch before signalling so an in-progress tick exits on the\n // next checkpoint, even if SIGTERM is delayed or lost.\n await patchSentientState(statePath, {\n killSwitch: true,\n killSwitchReason: reason,\n });\n\n const pid = state.pid;\n if (!pid) {\n return {\n stopped: false,\n pid: null,\n reason: 'killSwitch set; no daemon pid recorded (no active process to signal)',\n };\n }\n\n try {\n process.kill(pid, 0);\n } catch {\n await patchSentientState(statePath, { pid: null });\n return {\n stopped: true,\n pid,\n reason: `killSwitch set; daemon pid ${pid} was already dead (cleared)`,\n };\n }\n\n try {\n process.kill(pid, 'SIGTERM');\n return { stopped: true, pid, reason: `killSwitch set + SIGTERM delivered to pid ${pid}` };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n stopped: false,\n pid,\n reason: `killSwitch set but SIGTERM failed: ${message}`,\n };\n }\n}\n\n/**\n * Clear the kill switch so the cron schedule resumes executing ticks.\n *\n * Does NOT restart the daemon process \u2014 that is the caller's responsibility\n * via `cleo sentient start` if the process itself exited.\n *\n * @param projectRoot - Absolute path to the project root\n */\nexport async function resumeSentientDaemon(projectRoot: string): Promise<SentientState> {\n const statePath = join(projectRoot, SENTIENT_STATE_FILE);\n return patchSentientState(statePath, {\n killSwitch: false,\n killSwitchReason: null,\n });\n}\n\n/** Status snapshot returned by {@link getSentientDaemonStatus}. */\nexport interface SentientStatus {\n /** Whether the pid on file is currently alive. */\n running: boolean;\n /** Recorded pid (null when never started or cleared on stop). */\n pid: number | null;\n /** ISO-8601 timestamp of last start. */\n startedAt: string | null;\n /** ISO-8601 timestamp of the last completed tick. */\n lastTickAt: string | null;\n /** Kill-switch state. */\n killSwitch: boolean;\n /** Reason supplied with the last kill. */\n killSwitchReason: string | null;\n /** Rolling stats. */\n stats: SentientState['stats'];\n /** Number of currently-stuck tasks. */\n stuckCount: number;\n /** Currently active task id (set mid-tick). */\n activeTaskId: string | null;\n}\n\n/**\n * Return a diagnostic snapshot for `cleo sentient status`.\n *\n * @param projectRoot - Absolute path to the project root\n */\nexport async function getSentientDaemonStatus(projectRoot: string): Promise<SentientStatus> {\n const statePath = join(projectRoot, SENTIENT_STATE_FILE);\n const state = await readSentientState(statePath);\n\n let running = false;\n if (state.pid) {\n try {\n process.kill(state.pid, 0);\n running = true;\n } catch {\n running = false;\n }\n }\n\n return {\n running,\n pid: running ? state.pid : null,\n startedAt: state.startedAt,\n lastTickAt: state.lastTickAt,\n killSwitch: state.killSwitch,\n killSwitchReason: state.killSwitchReason,\n stats: state.stats,\n stuckCount: Object.keys(state.stuckTasks).length,\n activeTaskId: state.activeTaskId,\n };\n}\n", "/**\n * BRAIN Ingester \u2014 Tier-2 proposal candidate source.\n *\n * Queries brain.db for recurring-pain observations (citation_count >= 3,\n * last 7 days, quality_score >= 0.5) and returns ranked ProposalCandidate[].\n *\n * Design principles:\n * - NO LLM calls. All data comes from structured SQL queries.\n * - Title is template-generated: `[T2-BRAIN] Recurring issue: {title}`.\n * This is the prompt-injection defence from T1008 \u00A73.6.\n * - Failures are swallowed: returns empty array + logs warning.\n * Brain.db absence must never crash the propose tick.\n *\n * @task T1008\n * @see ADR-054 \u2014 Sentient Loop Tier-2\n */\n\nimport type { DatabaseSync } from 'node:sqlite';\nimport type { ProposalCandidate } from '@cleocode/contracts';\n\n// ---------------------------------------------------------------------------\n// Brain observation row (raw SQL result)\n// ---------------------------------------------------------------------------\n\ninterface BrainObservationRow {\n id: string;\n title: string | null;\n text: string;\n citation_count: number;\n quality_score: number;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Maximum candidates returned from a single brain ingester pass. */\nexport const BRAIN_INGESTER_LIMIT = 10;\n\n/** Minimum citation count for a brain entry to be considered. */\nexport const BRAIN_MIN_CITATION_COUNT = 3;\n\n/** Minimum quality score for a brain entry to be considered. */\nexport const BRAIN_MIN_QUALITY_SCORE = 0.5;\n\n/** Lookback window in days for brain observations. */\nexport const BRAIN_LOOKBACK_DAYS = 7;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Compute candidate weight from citation_count and quality_score.\n * Formula: `(citation_count / 10) * quality_score` capped at 1.0.\n */\nexport function computeBrainWeight(citationCount: number, qualityScore: number): number {\n return Math.min((citationCount / 10) * qualityScore, 1.0);\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Run the BRAIN ingester against the provided DatabaseSync handle.\n *\n * Returns at most {@link BRAIN_INGESTER_LIMIT} candidates, sorted by weight\n * descending. Returns an empty array if the database has no matching entries\n * or if any error occurs (errors are swallowed to never crash the tick).\n *\n * @param nativeDb - Open DatabaseSync handle to brain.db. May be null if\n * brain.db has not been initialised; this is treated as zero candidates.\n * @returns Ranked ProposalCandidate array (may be empty).\n */\nexport function runBrainIngester(nativeDb: DatabaseSync | null): ProposalCandidate[] {\n if (!nativeDb) {\n return [];\n }\n\n try {\n const stmt = nativeDb.prepare(`\n SELECT id, title, text, citation_count, quality_score\n FROM brain_observations\n WHERE type IN ('bugfix', 'decision')\n AND citation_count >= :minCitations\n AND created_at >= datetime('now', :lookback)\n AND quality_score >= :minQuality\n ORDER BY citation_count DESC, quality_score DESC\n LIMIT :limit\n `);\n\n const rows = stmt.all({\n minCitations: BRAIN_MIN_CITATION_COUNT,\n lookback: `-${BRAIN_LOOKBACK_DAYS} days`,\n minQuality: BRAIN_MIN_QUALITY_SCORE,\n limit: BRAIN_INGESTER_LIMIT,\n }) as unknown as BrainObservationRow[];\n\n const candidates: ProposalCandidate[] = rows.map((row) => {\n const label = row.title ?? row.text.slice(0, 80);\n return {\n source: 'brain' as const,\n sourceId: row.id,\n title: `[T2-BRAIN] Recurring issue: ${label}`,\n rationale: `Brain entry ${row.id} cited ${row.citation_count} times (quality ${row.quality_score.toFixed(2)}) in the last ${BRAIN_LOOKBACK_DAYS} days`,\n weight: computeBrainWeight(row.citation_count, row.quality_score),\n };\n });\n\n // Sort descending by weight (DB ORDER BY handles primary sort, but\n // weight formula may produce different ordering than raw column order).\n candidates.sort((a, b) => b.weight - a.weight);\n\n return candidates;\n } catch (err) {\n // Best-effort: log warning but never throw from an ingester.\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[sentient/brain-ingester] WARNING: ${message}\\n`);\n return [];\n }\n}\n", "/**\n * Nexus Ingester \u2014 Tier-2 proposal candidate source.\n *\n * Queries nexus.db for structural anomalies and returns ranked\n * ProposalCandidate[]. Two query patterns:\n *\n * A. Orphaned callees: functions that have many callers but make no calls\n * themselves (zero-import, high-in-degree). Suggests dead-end sinks\n * that may be candidates for abstraction or documentation.\n *\n * B. Over-coupled nodes: symbols with total degree (in + out edges) > 20,\n * suggesting high coupling that should be refactored.\n *\n * Design principles:\n * - NO LLM calls. All data comes from structured SQL queries.\n * - Title is template-generated: `[T2-NEXUS] ...`. Prompt-injection defence.\n * - Failures are swallowed: returns empty array + logs warning.\n * Nexus.db absence must never crash the propose tick.\n *\n * @task T1008\n * @see ADR-054 \u2014 Sentient Loop Tier-2\n */\n\nimport type { DatabaseSync } from 'node:sqlite';\nimport type { ProposalCandidate } from '@cleocode/contracts';\n\n// ---------------------------------------------------------------------------\n// Raw row types\n// ---------------------------------------------------------------------------\n\ninterface OrphanCalleeRow {\n id: string;\n name: string;\n file_path: string;\n caller_count: number;\n}\n\ninterface HighDegreeRow {\n id: string;\n name: string;\n file_path: string;\n degree: number;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Base weight for all nexus candidates (structural signals, lower priority than brain). */\nexport const NEXUS_BASE_WEIGHT = 0.3;\n\n/** Minimum caller count for orphaned-callee detection. */\nexport const NEXUS_MIN_CALLER_COUNT = 5;\n\n/** Minimum total degree for over-coupling detection. */\nexport const NEXUS_MIN_DEGREE = 20;\n\n/** Maximum results per query. */\nexport const NEXUS_QUERY_LIMIT = 5;\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Compute a deduplication key that is stable across query A and query B.\n * Both queries reference the same nexus node table, so using the node id\n * directly as sourceId is sufficient.\n */\nfunction toFingerprint(nodeId: string): string {\n return nodeId;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Run the Nexus ingester against the provided DatabaseSync handle.\n *\n * Returns candidates from both orphaned-callee (Query A) and high-degree\n * (Query B) detection, merged without duplication. Returns an empty array\n * if the database has no matching entries or if any error occurs.\n *\n * @param nativeDb - Open DatabaseSync handle to nexus.db. May be null if\n * nexus.db has not been initialised; this is treated as zero candidates.\n * @returns Ranked ProposalCandidate array (may be empty).\n */\nexport function runNexusIngester(nativeDb: DatabaseSync | null): ProposalCandidate[] {\n if (!nativeDb) {\n return [];\n }\n\n const seenIds = new Set<string>();\n const candidates: ProposalCandidate[] = [];\n\n try {\n // Query A: orphaned callees (many callers, zero outbound calls).\n const stmtA = nativeDb.prepare(`\n SELECT n.id, n.name, n.file_path, COUNT(r.id) as caller_count\n FROM nexus_nodes n\n JOIN nexus_relations r ON r.target_id = n.id AND r.kind = 'calls'\n WHERE NOT EXISTS (\n SELECT 1 FROM nexus_relations r2\n WHERE r2.source_id = n.id AND r2.kind = 'calls'\n )\n AND n.kind = 'function'\n GROUP BY n.id\n HAVING caller_count > :minCallers\n ORDER BY caller_count DESC\n LIMIT :limit\n `);\n\n const rowsA = stmtA.all({\n minCallers: NEXUS_MIN_CALLER_COUNT,\n limit: NEXUS_QUERY_LIMIT,\n }) as unknown as OrphanCalleeRow[];\n\n for (const row of rowsA) {\n const fp = toFingerprint(row.id);\n if (seenIds.has(fp)) continue;\n seenIds.add(fp);\n candidates.push({\n source: 'nexus' as const,\n sourceId: row.id,\n title: `[T2-NEXUS] Over-coupled symbol: ${row.name} (${row.caller_count} callers)`,\n rationale: `Function ${row.name} in ${row.file_path} has ${row.caller_count} callers but makes no outbound calls \u2014 review for abstraction opportunity`,\n weight: NEXUS_BASE_WEIGHT,\n });\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[sentient/nexus-ingester] WARNING query A: ${message}\\n`);\n }\n\n try {\n // Query B: high-degree nodes (over-coupling).\n const stmtB = nativeDb.prepare(`\n SELECT n.id, n.name, n.file_path, COUNT(r.id) as degree\n FROM nexus_nodes n\n JOIN nexus_relations r ON r.source_id = n.id OR r.target_id = n.id\n GROUP BY n.id\n HAVING degree > :minDegree\n ORDER BY degree DESC\n LIMIT :limit\n `);\n\n const rowsB = stmtB.all({\n minDegree: NEXUS_MIN_DEGREE,\n limit: NEXUS_QUERY_LIMIT,\n }) as unknown as HighDegreeRow[];\n\n for (const row of rowsB) {\n const fp = toFingerprint(row.id);\n if (seenIds.has(fp)) continue;\n seenIds.add(fp);\n candidates.push({\n source: 'nexus' as const,\n sourceId: row.id,\n title: `[T2-NEXUS] Over-coupled symbol: ${row.name} (${row.degree} edges)`,\n rationale: `Symbol ${row.name} in ${row.file_path} has ${row.degree} total edges \u2014 review for over-coupling`,\n weight: NEXUS_BASE_WEIGHT,\n });\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[sentient/nexus-ingester] WARNING query B: ${message}\\n`);\n }\n\n return candidates;\n}\n", "/**\n * Test Ingester \u2014 Tier-2 proposal candidate source.\n *\n * Reads two data sources:\n *\n * Source A \u2014 `.cleo/audit/gates.jsonl`: CLEO evidence gate failure records.\n * Each line is a JSONL record. Lines where `failCount > 0` produce a\n * proposal suggesting a flaky-test guard be added for the failing task.\n *\n * Source B \u2014 `.cleo/coverage-summary.json`: vitest coverage JSON summary.\n * Written by `vitest --coverage --reporter json-summary`. Lines where\n * `lines.pct < 80` produce a proposal suggesting coverage improvement.\n * If the file is absent, Source B returns zero candidates (no error).\n *\n * Design principles:\n * - NO LLM calls. All data comes from structured file reads.\n * - Title is template-generated. Prompt-injection defence (T1008 \u00A73.6).\n * - Failures are swallowed: returns empty array + logs warning.\n *\n * @task T1008\n * @see ADR-054 \u2014 Sentient Loop Tier-2\n */\n\nimport { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { ProposalCandidate } from '@cleocode/contracts';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** A single line from gates.jsonl. */\ninterface GateRecord {\n taskId?: string;\n gate?: string;\n failCount?: number;\n [key: string]: unknown;\n}\n\n/** Coverage summary entry for a single file. */\ninterface CoverageEntry {\n lines?: { pct?: number };\n statements?: { pct?: number };\n functions?: { pct?: number };\n branches?: { pct?: number };\n}\n\n/** Shape of the JSON coverage summary file. */\ntype CoverageSummary = Record<string, CoverageEntry>;\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Relative path from project root to gates.jsonl. */\nexport const GATES_JSONL_PATH = '.cleo/audit/gates.jsonl' as const;\n\n/** Relative path from project root to the coverage summary. */\nexport const COVERAGE_SUMMARY_PATH = '.cleo/coverage-summary.json' as const;\n\n/** Coverage line percentage below which a proposal is emitted. */\nexport const MIN_LINE_COVERAGE_PCT = 80;\n\n/** Base weight for all test ingester candidates. */\nexport const TEST_BASE_WEIGHT = 0.5;\n\n// ---------------------------------------------------------------------------\n// Source A: gates.jsonl\n// ---------------------------------------------------------------------------\n\n/**\n * Parse gates.jsonl and return one candidate per task that has any gate\n * with `failCount > 0`.\n *\n * @param projectRoot - Absolute path to the project root.\n * @returns Proposal candidates (may be empty).\n */\nfunction runGatesIngester(projectRoot: string): ProposalCandidate[] {\n const gatesPath = join(projectRoot, GATES_JSONL_PATH);\n\n let raw: string;\n try {\n raw = readFileSync(gatesPath, 'utf-8');\n } catch {\n // File absent or unreadable \u2014 not an error.\n return [];\n }\n\n const candidates: ProposalCandidate[] = [];\n const seenKeys = new Set<string>();\n\n for (const line of raw.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n let record: GateRecord;\n try {\n record = JSON.parse(trimmed) as GateRecord;\n } catch {\n // Skip malformed lines.\n continue;\n }\n\n const taskId = record.taskId;\n const gate = record.gate ?? 'unknown';\n const failCount = record.failCount ?? 0;\n\n if (typeof taskId !== 'string' || failCount <= 0) continue;\n\n const key = `${taskId}.${gate}`;\n if (seenKeys.has(key)) continue;\n seenKeys.add(key);\n\n candidates.push({\n source: 'test' as const,\n sourceId: key,\n title: `[T2-TEST] Fix flaky gate: ${taskId}.${gate}`,\n rationale: `Gate '${gate}' on task ${taskId} has failed ${failCount} time(s)`,\n weight: TEST_BASE_WEIGHT,\n });\n }\n\n return candidates;\n}\n\n// ---------------------------------------------------------------------------\n// Source B: coverage-summary.json\n// ---------------------------------------------------------------------------\n\n/**\n * Read the vitest coverage summary and return one candidate per file with\n * line coverage below {@link MIN_LINE_COVERAGE_PCT}.\n *\n * @param projectRoot - Absolute path to the project root.\n * @returns Proposal candidates (may be empty; empty if file absent).\n */\nfunction runCoverageIngester(projectRoot: string): ProposalCandidate[] {\n const coveragePath = join(projectRoot, COVERAGE_SUMMARY_PATH);\n\n let summary: CoverageSummary;\n try {\n const raw = readFileSync(coveragePath, 'utf-8');\n summary = JSON.parse(raw) as CoverageSummary;\n } catch {\n // File absent or malformed \u2014 not an error, return zero candidates.\n return [];\n }\n\n const candidates: ProposalCandidate[] = [];\n\n for (const [filePath, entry] of Object.entries(summary)) {\n // Skip the 'total' synthetic key if present.\n if (filePath === 'total') continue;\n\n const pct = entry?.lines?.pct;\n if (typeof pct !== 'number' || pct >= MIN_LINE_COVERAGE_PCT) continue;\n\n candidates.push({\n source: 'test' as const,\n sourceId: filePath,\n title: `[T2-TEST] Increase coverage: ${filePath} (${pct}% lines)`,\n rationale: `File ${filePath} has ${pct}% line coverage (target: ${MIN_LINE_COVERAGE_PCT}%)`,\n weight: TEST_BASE_WEIGHT,\n });\n }\n\n return candidates;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Run the test ingester against both data sources.\n *\n * Merges Source A (gates.jsonl) and Source B (coverage-summary.json) without\n * duplication. Returns an empty array if both sources yield nothing or if\n * errors occur (errors are swallowed).\n *\n * @param projectRoot - Absolute path to the project root.\n * @returns Combined ProposalCandidate array (may be empty).\n */\nexport function runTestIngester(projectRoot: string): ProposalCandidate[] {\n try {\n const gatesCandidates = runGatesIngester(projectRoot);\n const coverageCandidates = runCoverageIngester(projectRoot);\n\n // Merge, deduplicate by sourceId.\n const seenSourceIds = new Set<string>();\n const merged: ProposalCandidate[] = [];\n\n for (const candidate of [...gatesCandidates, ...coverageCandidates]) {\n if (seenSourceIds.has(candidate.sourceId)) continue;\n seenSourceIds.add(candidate.sourceId);\n merged.push(candidate);\n }\n\n return merged;\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[sentient/test-ingester] WARNING: ${message}\\n`);\n return [];\n }\n}\n", "/**\n * Proposal Rate Limiter \u2014 Transactional DB-enforced daily cap.\n *\n * Enforces a maximum of N proposals per day per source tag, using a\n * BEGIN IMMEDIATE transaction with COUNT + conditional INSERT pattern.\n *\n * Design rationale (T1008 \u00A73.2):\n * - In-process counters do not survive daemon restart, and two daemon\n * instances could both allow N/day if enforcement is not in the DB.\n * - The `sentient.lock` advisory lock (daemon.ts) prevents two daemons\n * from running concurrently, but the transactional count check provides\n * belt-and-suspenders protection against TOCTOU races.\n * - SQLite partial unique indexes cannot enforce count > 1, so the\n * BEGIN IMMEDIATE + COUNT + INSERT pattern is used instead.\n *\n * The \"day\" boundary is determined by SQLite's `date('now')` (UTC).\n * Proposals counted include ALL non-terminal statuses (proposed, pending,\n * active, done) \u2014 an accepted proposal still consumes a daily slot.\n *\n * @task T1008\n * @see ADR-054 \u2014 Sentient Loop Tier-2\n */\n\nimport type { DatabaseSync, SQLInputValue } from 'node:sqlite';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * The meta proposedBy tag written to `tasks.metadata_json` by the Tier-2\n * proposer. This tag is the query key for rate-limit counting.\n */\nexport const SENTIENT_TIER2_TAG = 'sentient-tier2' as const;\n\n/**\n * Default maximum number of Tier-2 proposals per UTC day.\n * Can be overridden by callers.\n */\nexport const DEFAULT_DAILY_PROPOSAL_LIMIT = 3;\n\n/**\n * SQL error code string returned when BEGIN IMMEDIATE fails because another\n * write transaction is in progress.\n */\nconst SQLITE_BUSY_CODE = 'SQLITE_BUSY';\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Count the number of Tier-2 proposals created today (UTC).\n *\n * Counts tasks where:\n * - `labels_json` contains `'sentient-tier2'` (the Tier-2 marker label)\n * - `date(created_at) = date('now')`\n * - `status IN ('proposed', 'pending', 'active', 'done')` \u2014 terminal\n * states that were proposed today still count toward the daily cap so\n * that accepted proposals don't free a slot for another proposal.\n *\n * The `LIKE` pattern is intentional: labels_json is a JSON array stored as\n * text, and `'sentient-tier2'` is always a complete JSON string value within\n * that array, making substring matching safe here.\n *\n * @param nativeDb - Open DatabaseSync handle to tasks.db.\n * @returns Number of proposals created today. Returns 0 if DB is null.\n */\nexport function countTodayProposals(nativeDb: DatabaseSync | null): number {\n if (!nativeDb) return 0;\n\n const stmt = nativeDb.prepare(`\n SELECT COUNT(*) as cnt\n FROM tasks\n WHERE labels_json LIKE :labelPattern\n AND date(created_at) = date('now')\n AND status IN ('proposed', 'pending', 'active', 'done')\n `);\n\n const row = stmt.get({ labelPattern: `%${SENTIENT_TIER2_TAG}%` }) as { cnt: number } | undefined;\n return row?.cnt ?? 0;\n}\n\n/**\n * Check whether the daily rate limit has been reached.\n *\n * @param nativeDb - Open DatabaseSync handle to tasks.db.\n * @param limit - Daily cap (defaults to {@link DEFAULT_DAILY_PROPOSAL_LIMIT}).\n * @returns `true` if the limit is reached or exceeded; `false` if capacity remains.\n */\nexport function isRateLimitExceeded(\n nativeDb: DatabaseSync | null,\n limit = DEFAULT_DAILY_PROPOSAL_LIMIT,\n): boolean {\n return countTodayProposals(nativeDb) >= limit;\n}\n\n// ---------------------------------------------------------------------------\n// Transactional INSERT guard\n// ---------------------------------------------------------------------------\n\n/** Result of a transactional insert attempt. */\nexport interface TransactionalInsertResult {\n /** Whether the INSERT was committed. */\n inserted: boolean;\n /**\n * The count read at the start of the transaction. Used for diagnostics\n * and tests \u2014 lets callers verify the guard saw the expected count.\n */\n countBeforeInsert: number;\n /**\n * If `inserted = false` and this is set, the limit was the reason.\n */\n reason?: 'rate-limit' | 'busy';\n}\n\n/**\n * Attempt to insert a pre-built task row inside a BEGIN IMMEDIATE transaction\n * with a count check.\n *\n * Steps:\n * 1. BEGIN IMMEDIATE (exclusive write lock on tasks.db)\n * 2. COUNT proposals created today\n * 3. If count >= limit: ROLLBACK, return `{ inserted: false, reason: 'rate-limit' }`\n * 4. Otherwise: INSERT the row, COMMIT, return `{ inserted: true }`\n *\n * On SQLITE_BUSY: ROLLBACK + return `{ inserted: false, reason: 'busy' }`.\n *\n * @param nativeDb - Open DatabaseSync handle to tasks.db.\n * @param insertSql - Parameterized INSERT SQL string.\n * @param insertParams - Named parameters for the INSERT statement.\n * @param limit - Daily cap.\n * @returns Insert result.\n */\nexport function transactionalInsertProposal(\n nativeDb: DatabaseSync,\n insertSql: string,\n insertParams: Record<string, SQLInputValue>,\n limit = DEFAULT_DAILY_PROPOSAL_LIMIT,\n): TransactionalInsertResult {\n try {\n nativeDb.exec('BEGIN IMMEDIATE');\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (msg.includes(SQLITE_BUSY_CODE)) {\n return { inserted: false, countBeforeInsert: 0, reason: 'busy' };\n }\n throw err;\n }\n\n try {\n const countBeforeInsert = countTodayProposals(nativeDb);\n\n if (countBeforeInsert >= limit) {\n nativeDb.exec('ROLLBACK');\n return { inserted: false, countBeforeInsert, reason: 'rate-limit' };\n }\n\n const stmt = nativeDb.prepare(insertSql);\n stmt.run(insertParams);\n\n nativeDb.exec('COMMIT');\n return { inserted: true, countBeforeInsert };\n } catch (err) {\n try {\n nativeDb.exec('ROLLBACK');\n } catch {\n // ignore rollback failure\n }\n throw err;\n }\n}\n", "/**\n * Sentient Loop State \u2014 Persistent state for the Tier-1 autonomous daemon.\n *\n * Stored in `.cleo/sentient-state.json` (plain JSON, not SQLite) to avoid\n * SQLite WAL conflicts between the long-running daemon process and the\n * main CLEO CLI process. Human-readable for debugging.\n *\n * The file is gitignored (see .gitignore \u00A7.cleo/ section) and survives\n * restarts. Only `killSwitch`, `pid`, and `stats` fields are load-bearing\n * across process boundaries.\n *\n * @see ADR-054 \u2014 Sentient Loop Tier-1 (autonomous task execution)\n * @task T946\n */\n\nimport { mkdir, readFile, rename, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport type { Tier2Stats } from '@cleocode/contracts';\n\n/** Schema version for sentient-state.json. Bump on breaking field changes. */\nexport const SENTIENT_STATE_SCHEMA_VERSION = '1.0' as const;\n\n/**\n * Per-task failure/backoff tracking for stuck detection.\n * Keyed by task id in {@link SentientState.stuckTasks}.\n */\nexport interface StuckTaskRecord {\n /** Number of consecutive failed spawn attempts for this task. */\n attempts: number;\n /** ISO-8601 timestamp of the most recent failure. */\n lastFailureAt: string;\n /** Unix epoch ms when the next retry becomes eligible. */\n nextRetryAt: number;\n /** Last captured failure reason (truncated to 500 chars). */\n lastReason: string;\n}\n\n/**\n * Rolling counters persisted across daemon restarts.\n */\nexport interface SentientStats {\n /** Total tasks picked by the loop since creation. */\n tasksPicked: number;\n /** Total tasks that completed successfully. */\n tasksCompleted: number;\n /** Total tasks whose spawn exited non-zero. */\n tasksFailed: number;\n /** Total ticks executed (including no-op ticks). */\n ticksExecuted: number;\n /** Total ticks aborted early because kill switch was active. */\n ticksKilled: number;\n}\n\n/**\n * Persistent sentient daemon state.\n *\n * Design principles:\n * - `killSwitch` is the single load-bearing kill signal \u2014 the daemon re-checks\n * it between every step of a tick, not just at tick start (Round 2 audit).\n * - `stuckTasks` keys are task ids; values encode backoff + failure counts.\n * - `stuckTimestamps` is a rolling 1-hour window used for the self-pause rule\n * (5 stucks in 1 hour \u2192 killSwitch=true).\n * - `stats` fields are monotonic counters that only ever increase.\n */\nexport interface SentientState {\n /** JSON schema version for forward-compatibility checks. */\n schemaVersion: typeof SENTIENT_STATE_SCHEMA_VERSION;\n /** PID of the currently running daemon process. null = daemon not running. */\n pid: number | null;\n /** ISO-8601 timestamp when the daemon was last started. */\n startedAt: string | null;\n /** ISO-8601 timestamp of the last completed tick (any outcome). */\n lastTickAt: string | null;\n /**\n * Kill-switch flag. When true, the daemon re-checks at every step of a tick\n * and exits cleanly without picking or spawning a task.\n */\n killSwitch: boolean;\n /** Reason supplied when killSwitch was last set (diagnostic only). */\n killSwitchReason: string | null;\n /** Rolling counters; see {@link SentientStats}. */\n stats: SentientStats;\n /** Per-task backoff + failure metadata for retry/stuck detection. */\n stuckTasks: Record<string, StuckTaskRecord>;\n /**\n * Unix-epoch-ms timestamps of `stuck` events within the last hour.\n * When length \u2265 5 the daemon self-pauses (killSwitch=true).\n */\n stuckTimestamps: number[];\n /**\n * Currently-active task id (set while a spawn is in-flight, cleared afterward).\n * Enables `status` to show the in-progress task during a long-running tick.\n */\n activeTaskId: string | null;\n /**\n * Tier-2 proposal queue enabled flag.\n *\n * Default: `false` \u2014 Tier 2 is OFF by default to prevent surprise proposal\n * floods on first daemon start. Owner enables via `cleo sentient propose enable`\n * (patches this flag). See ADR-054 \u00A7Tier-2.\n *\n * @task T1008\n */\n tier2Enabled: boolean;\n /**\n * Rolling counters for Tier-2 proposal activity.\n *\n * @task T1008\n */\n tier2Stats: Tier2Stats;\n}\n\n/** Default (empty) sentient state for fresh initialisation. */\nexport const DEFAULT_SENTIENT_STATE: SentientState = {\n schemaVersion: SENTIENT_STATE_SCHEMA_VERSION,\n pid: null,\n startedAt: null,\n lastTickAt: null,\n killSwitch: false,\n killSwitchReason: null,\n stats: {\n tasksPicked: 0,\n tasksCompleted: 0,\n tasksFailed: 0,\n ticksExecuted: 0,\n ticksKilled: 0,\n },\n stuckTasks: {},\n stuckTimestamps: [],\n activeTaskId: null,\n tier2Enabled: false,\n tier2Stats: {\n proposalsGenerated: 0,\n proposalsAccepted: 0,\n proposalsRejected: 0,\n },\n};\n\n/**\n * Read the sentient state from disk.\n *\n * Returns the default state if the file does not exist or is malformed.\n * Never throws \u2014 absence is not an error.\n *\n * @param statePath - Absolute path to sentient-state.json\n */\nexport async function readSentientState(statePath: string): Promise<SentientState> {\n try {\n const raw = await readFile(statePath, 'utf-8');\n const parsed = JSON.parse(raw) as Partial<SentientState>;\n return {\n ...DEFAULT_SENTIENT_STATE,\n ...parsed,\n stats: { ...DEFAULT_SENTIENT_STATE.stats, ...(parsed.stats ?? {}) },\n stuckTasks: parsed.stuckTasks ?? {},\n stuckTimestamps: parsed.stuckTimestamps ?? [],\n tier2Enabled: parsed.tier2Enabled ?? false,\n tier2Stats: { ...DEFAULT_SENTIENT_STATE.tier2Stats, ...(parsed.tier2Stats ?? {}) },\n };\n } catch {\n return { ...DEFAULT_SENTIENT_STATE };\n }\n}\n\n/**\n * Write the sentient state to disk atomically via tmp-then-rename.\n *\n * Atomic write prevents partial reads if the daemon crashes mid-write.\n *\n * @param statePath - Absolute path to sentient-state.json\n * @param state - State to persist\n */\nexport async function writeSentientState(statePath: string, state: SentientState): Promise<void> {\n const dir = dirname(statePath);\n await mkdir(dir, { recursive: true });\n\n const tmpPath = join(dir, `.sentient-state-${process.pid}.tmp`);\n const json = JSON.stringify(state, null, 2);\n\n await writeFile(tmpPath, json, 'utf-8');\n await rename(tmpPath, statePath);\n}\n\n/**\n * Patch a subset of fields in the sentient state file.\n *\n * Reads current state, merges patch, writes back. Nested `stats` merges\n * with existing stats (never clobbered wholesale).\n *\n * @param statePath - Absolute path to sentient-state.json\n * @param patch - Partial state to merge over the existing state\n * @returns The merged state that was written to disk.\n */\nexport async function patchSentientState(\n statePath: string,\n patch: Partial<SentientState>,\n): Promise<SentientState> {\n const current = await readSentientState(statePath);\n const updated: SentientState = {\n ...current,\n ...patch,\n stats: { ...current.stats, ...(patch.stats ?? {}) },\n };\n await writeSentientState(statePath, updated);\n return updated;\n}\n\n/**\n * Increment stats counters atomically.\n *\n * @param statePath - Absolute path to sentient-state.json\n * @param delta - Partial stats to add to current counters\n */\nexport async function incrementStats(\n statePath: string,\n delta: Partial<SentientStats>,\n): Promise<SentientState> {\n const current = await readSentientState(statePath);\n const nextStats: SentientStats = {\n tasksPicked: current.stats.tasksPicked + (delta.tasksPicked ?? 0),\n tasksCompleted: current.stats.tasksCompleted + (delta.tasksCompleted ?? 0),\n tasksFailed: current.stats.tasksFailed + (delta.tasksFailed ?? 0),\n ticksExecuted: current.stats.ticksExecuted + (delta.ticksExecuted ?? 0),\n ticksKilled: current.stats.ticksKilled + (delta.ticksKilled ?? 0),\n };\n const updated: SentientState = { ...current, stats: nextStats };\n await writeSentientState(statePath, updated);\n return updated;\n}\n", "/**\n * Sentient Loop Propose Tick \u2014 Single-pass Tier-2 proposal generator.\n *\n * Runs inside the daemon cron (every 2 hours) or standalone via\n * `cleo sentient propose run`. Orchestrates the three ingesters,\n * deduplicates candidates by fingerprint, applies the DB-enforced\n * rate limit, and writes accepted candidates as tasks with\n * `status='proposed'` and labels including `'sentient-tier2'`.\n *\n * Scoped IN:\n * - Ingest from brain.db, nexus.db, and .cleo/audit/gates.jsonl\n * - Transactional rate-limit check (BEGIN IMMEDIATE + COUNT + INSERT)\n * - Kill-switch re-check at each checkpoint (Round 2 audit \u00A79)\n * - tier2Enabled guard (default false \u2014 owner opt-in)\n *\n * Scoped OUT:\n * - LLM calls (NONE \u2014 all proposal titles are structured templates)\n * - Tier-3 sandbox/merge (blocked on T992+T993+T995)\n *\n * Title format enforcement:\n * All proposal titles MUST match `/^\\[T2-(BRAIN|NEXUS|TEST)\\]/`.\n * This is the prompt-injection defence from T1008 \u00A73.6 \u2014 no freeform\n * LLM text can enter the task title column from the Tier-2 proposer.\n *\n * @task T1008\n * @see ADR-054 \u2014 Sentient Loop Tier-2\n */\n\nimport type { ProposalCandidate } from '@cleocode/contracts';\nimport { runBrainIngester } from './ingesters/brain-ingester.js';\nimport { runNexusIngester } from './ingesters/nexus-ingester.js';\nimport { runTestIngester } from './ingesters/test-ingester.js';\nimport {\n countTodayProposals,\n DEFAULT_DAILY_PROPOSAL_LIMIT,\n transactionalInsertProposal,\n} from './proposal-rate-limiter.js';\nimport { patchSentientState, readSentientState } from './state.js';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * Regex that ALL proposal titles MUST match.\n * Enforces structured-template-only output (no freeform LLM text).\n */\nexport const PROPOSAL_TITLE_PATTERN = /^\\[T2-(BRAIN|NEXUS|TEST)\\]/;\n\n/**\n * Label applied to every Tier-2 proposal task.\n * Used by the rate limiter to identify proposals.\n */\nexport const TIER2_LABEL = 'sentient-tier2';\n\n// ---------------------------------------------------------------------------\n// Outcome types\n// ---------------------------------------------------------------------------\n\n/** Discriminant for the propose-tick outcome. */\nexport type ProposalTickOutcomeKind =\n | 'killed' // killSwitch active\n | 'disabled' // tier2Enabled = false\n | 'rate-limited' // daily cap already reached\n | 'no-candidates' // all ingesters returned empty\n | 'wrote' // at least one proposal was written\n | 'error'; // unexpected error\n\n/** Structured outcome of a single propose-tick pass. */\nexport interface ProposeTickOutcome {\n /** Discriminant describing how the tick ended. */\n kind: ProposalTickOutcomeKind;\n /** Number of proposals written in this pass. */\n written: number;\n /** Current daily proposal count at the end of the pass. */\n count: number;\n /** Human-readable detail (one line). */\n detail: string;\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/** Options for {@link runProposeTick}. */\nexport interface ProposeTickOptions {\n /** Absolute path to the project root (contains `.cleo/`). */\n projectRoot: string;\n /** Absolute path to sentient-state.json. */\n statePath: string;\n /**\n * Override for the brain DB handle. Injected by tests to avoid\n * opening a real brain.db. When omitted the real getBrainNativeDb() is used.\n */\n brainDb?: import('node:sqlite').DatabaseSync | null;\n /**\n * Override for the nexus DB handle. Injected by tests.\n * When omitted the real getNexusNativeDb() is used.\n */\n nexusDb?: import('node:sqlite').DatabaseSync | null;\n /**\n * Override for the tasks DB handle (used by rate limiter + INSERT).\n * Injected by tests. When omitted the real getNativeTasksDb() is used.\n */\n tasksDb?: import('node:sqlite').DatabaseSync | null;\n /**\n * Override the task ID allocator. Injected by tests.\n * When omitted the real allocateNextTaskId() is used.\n */\n allocateTaskId?: () => Promise<string>;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Compute a deduplication fingerprint for a candidate.\n * Two candidates with the same source + sourceId are considered identical.\n */\nfunction fingerprint(candidate: ProposalCandidate): string {\n return `${candidate.source}:${candidate.sourceId}`;\n}\n\n/**\n * Check whether the kill switch is currently active.\n */\nasync function killSwitchActive(statePath: string): Promise<boolean> {\n const state = await readSentientState(statePath);\n return state.killSwitch === true;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Run a single Tier-2 propose pass.\n *\n * Steps:\n * 1. Check killSwitch \u2192 abort if true\n * 2. Check tier2Enabled \u2192 abort if false\n * 3. Run all three ingesters in parallel\n * 4. Check killSwitch again (post-ingest checkpoint)\n * 5. Merge + deduplicate candidates by fingerprint\n * 6. Validate title format (must match PROPOSAL_TITLE_PATTERN)\n * 7. Score + take top-N candidates (N = limit - countTodayProposals)\n * 8. Check killSwitch again (pre-write checkpoint)\n * 9. For each candidate: transactional INSERT into tasks.db\n * 10. Update tier2Stats in state\n *\n * @param options - Propose tick options (see {@link ProposeTickOptions})\n * @returns Structured outcome describing how the pass ended.\n */\nexport async function runProposeTick(options: ProposeTickOptions): Promise<ProposeTickOutcome> {\n const { projectRoot, statePath } = options;\n\n // Checkpoint 1: killSwitch before any work\n if (await killSwitchActive(statePath)) {\n return { kind: 'killed', written: 0, count: 0, detail: 'killSwitch active before ingest' };\n }\n\n // Check tier2Enabled guard\n const state = await readSentientState(statePath);\n if (!state.tier2Enabled) {\n return {\n kind: 'disabled',\n written: 0,\n count: 0,\n detail: 'tier2Enabled=false; enable via cleo sentient propose enable',\n };\n }\n\n // Resolve DB handles\n let brainDb: import('node:sqlite').DatabaseSync | null;\n let nexusDb: import('node:sqlite').DatabaseSync | null;\n let tasksNativeDb: import('node:sqlite').DatabaseSync | null;\n\n if (options.brainDb !== undefined) {\n brainDb = options.brainDb;\n } else {\n // Ensure brain.db is initialized before calling getBrainNativeDb\n try {\n const { getBrainDb, getBrainNativeDb } = await import('@cleocode/core/internal');\n await getBrainDb(projectRoot);\n brainDb = getBrainNativeDb();\n } catch {\n brainDb = null;\n }\n }\n\n if (options.nexusDb !== undefined) {\n nexusDb = options.nexusDb;\n } else {\n try {\n const { getNexusNativeDb } = await import('@cleocode/core/internal');\n nexusDb = getNexusNativeDb();\n } catch {\n nexusDb = null;\n }\n }\n\n if (options.tasksDb !== undefined) {\n tasksNativeDb = options.tasksDb;\n } else {\n const { getNativeDb, getDb } = await import('@cleocode/core/internal');\n // Ensure tasks.db is initialized\n await getDb(projectRoot);\n tasksNativeDb = getNativeDb();\n }\n\n // Run all three ingesters in parallel\n const [brainCandidates, nexusCandidates, testCandidates] = await Promise.all([\n Promise.resolve(runBrainIngester(brainDb)),\n Promise.resolve(runNexusIngester(nexusDb)),\n Promise.resolve(runTestIngester(projectRoot)),\n ]);\n\n // Checkpoint 2: killSwitch after ingest\n if (await killSwitchActive(statePath)) {\n return {\n kind: 'killed',\n written: 0,\n count: 0,\n detail: 'killSwitch active after ingest phase',\n };\n }\n\n // Merge + deduplicate by fingerprint\n const seenFingerprints = new Set<string>();\n const merged: ProposalCandidate[] = [];\n\n for (const candidate of [...brainCandidates, ...nexusCandidates, ...testCandidates]) {\n // Validate title format \u2014 reject candidates with non-template titles\n if (!PROPOSAL_TITLE_PATTERN.test(candidate.title)) {\n process.stderr.write(\n `[sentient/propose-tick] Rejected candidate with invalid title format: \"${candidate.title}\"\\n`,\n );\n continue;\n }\n\n const fp = fingerprint(candidate);\n if (seenFingerprints.has(fp)) continue;\n seenFingerprints.add(fp);\n merged.push(candidate);\n }\n\n if (merged.length === 0) {\n return { kind: 'no-candidates', written: 0, count: 0, detail: 'no candidates from ingesters' };\n }\n\n // Sort by weight descending\n merged.sort((a, b) => b.weight - a.weight);\n\n // Determine how many slots remain today\n const currentCount = tasksNativeDb ? countTodayProposals(tasksNativeDb) : 0;\n const slotsRemaining = Math.max(0, DEFAULT_DAILY_PROPOSAL_LIMIT - currentCount);\n\n if (slotsRemaining === 0) {\n return {\n kind: 'rate-limited',\n written: 0,\n count: currentCount,\n detail: `daily limit reached (${currentCount}/${DEFAULT_DAILY_PROPOSAL_LIMIT})`,\n };\n }\n\n // Take top-N candidates\n const toWrite = merged.slice(0, slotsRemaining);\n\n // Checkpoint 3: killSwitch before DB writes\n if (await killSwitchActive(statePath)) {\n return {\n kind: 'killed',\n written: 0,\n count: currentCount,\n detail: 'killSwitch active before write phase',\n };\n }\n\n // Write proposals\n let written = 0;\n\n for (const candidate of toWrite) {\n // Allocate task ID\n let taskId: string;\n if (options.allocateTaskId) {\n taskId = await options.allocateTaskId();\n } else {\n const { allocateNextTaskId } = await import('@cleocode/core/internal');\n taskId = await allocateNextTaskId(projectRoot);\n }\n\n const now = new Date().toISOString();\n const labels = JSON.stringify([TIER2_LABEL, `source:${candidate.source}`]);\n\n if (!tasksNativeDb) {\n process.stderr.write('[sentient/propose-tick] tasks DB not available; skipping write\\n');\n break;\n }\n\n // Use the SQL INSERT path (with metadata_json-equivalent stored in notes_json\n // as a structured first element) and labels to mark the proposal.\n // The rate limiter identifies proposals by: labels_json LIKE '%sentient-tier2%'\n // This avoids needing a new column on the tasks table.\n const notesJson = JSON.stringify([\n JSON.stringify({\n kind: 'proposal-meta',\n proposedBy: 'sentient-tier2',\n source: candidate.source,\n sourceId: candidate.sourceId,\n weight: candidate.weight,\n proposedAt: now,\n }),\n ]);\n\n const insertSql = `\n INSERT INTO tasks (\n id, title, description, status, priority,\n labels_json, notes_json,\n created_at, updated_at,\n role, scope\n ) VALUES (\n :id, :title, :description, :status, :priority,\n :labelsJson, :notesJson,\n :createdAt, :updatedAt,\n :role, :scope\n )\n `;\n\n const insertParams = {\n id: taskId,\n title: candidate.title,\n description: candidate.rationale,\n status: 'proposed',\n priority: 'medium',\n labelsJson: labels,\n notesJson,\n createdAt: now,\n updatedAt: now,\n role: 'work',\n scope: 'feature',\n };\n\n try {\n const result = transactionalInsertProposal(\n tasksNativeDb,\n insertSql,\n insertParams,\n DEFAULT_DAILY_PROPOSAL_LIMIT,\n );\n\n if (result.inserted) {\n written++;\n } else if (result.reason === 'rate-limit') {\n // Rate limit hit mid-loop \u2014 stop writing.\n break;\n }\n // If 'busy', skip this one and continue.\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[sentient/propose-tick] INSERT failed for ${taskId}: ${message}\\n`);\n }\n }\n\n // Update tier2Stats\n if (written > 0) {\n const latestState = await readSentientState(statePath);\n await patchSentientState(statePath, {\n tier2Stats: {\n ...latestState.tier2Stats,\n proposalsGenerated: latestState.tier2Stats.proposalsGenerated + written,\n },\n });\n }\n\n const finalCount = tasksNativeDb ? countTodayProposals(tasksNativeDb) : currentCount + written;\n\n if (written === 0) {\n return {\n kind: 'no-candidates',\n written: 0,\n count: finalCount,\n detail: 'candidates available but none written (rate limit or DB unavailable)',\n };\n }\n\n return {\n kind: 'wrote',\n written,\n count: finalCount,\n detail: `wrote ${written} proposal(s) (${finalCount}/${DEFAULT_DAILY_PROPOSAL_LIMIT} today)`,\n };\n}\n\n/**\n * Safe wrapper for {@link runProposeTick} \u2014 swallows unexpected exceptions.\n * Used by the daemon cron handler.\n *\n * @param options - Propose tick options\n * @returns The propose tick outcome, or an error outcome if the tick threw.\n */\nexport async function safeRunProposeTick(options: ProposeTickOptions): Promise<ProposeTickOutcome> {\n try {\n return await runProposeTick(options);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n kind: 'error',\n written: 0,\n count: 0,\n detail: `propose tick threw: ${message}`,\n };\n }\n}\n", "/**\n * Sentient Loop Tick \u2014 Single-iteration tick runner for the Tier-1 daemon.\n *\n * A tick is one complete pass of:\n * 1. Check killSwitch (abort if true)\n * 2. Pick an unblocked task via @cleocode/core/sdk\n * 3. Check killSwitch again (abort if true)\n * 4. Spawn worker via `cleo orchestrate spawn <taskId> --adapter <adapter>`\n * 5. Check killSwitch again before recording result\n * 6. Record success (receipt + stats) or failure (retry/backoff)\n *\n * Each step re-reads the state file so that a killSwitch flipped mid-tick is\n * honoured on the very next instruction (Round 2 audit \u00A71: \"mid-experiment\n * kill limbo\").\n *\n * Rate limit: driven by the cron schedule (`*\\/5 * * * *` \u2192 \u226412 ticks/hour \u2264\n * 12 spawns/hour). No in-tick sleep is required \u2014 cron provides the cadence.\n *\n * Scoped OUT: Tier 2 (propose) and Tier 3 (sandbox auto-merge) per ADR-054.\n *\n * @task T946\n * @see ADR-054 \u2014 Sentient Loop Tier-1\n */\n\nimport { spawn } from 'node:child_process';\nimport type { Task } from '@cleocode/contracts';\nimport {\n incrementStats,\n patchSentientState,\n readSentientState,\n type SentientState,\n type StuckTaskRecord,\n} from './state.js';\n\n// NOTE: `checkAndDream` is lazy-imported inside `maybeTriggerDream` to keep the\n// test surface small \u2014 tests that don't exercise the dream path never load\n// the brain.db stack.\n\n// ---------------------------------------------------------------------------\n// Dream-cycle trigger constants (T996)\n// ---------------------------------------------------------------------------\n\n/**\n * Number of new brain observations since the last consolidation that causes\n * the tick loop to trigger a dream cycle (volume tier).\n * Configurable via the injected `dreamVolumeThreshold` option.\n */\nexport const DREAM_VOLUME_THRESHOLD_DEFAULT = 50;\n\n/**\n * Number of consecutive no-task ticks before the idle dream trigger fires.\n * Represents \"N idle ticks\" \u2014 when no task has been picked for this many\n * consecutive ticks, the system is considered sufficiently idle.\n */\nexport const DREAM_IDLE_TICKS_DEFAULT = 5;\n\n// NOTE: `@cleocode/core/sdk` and `@cleocode/core/tasks` are lazy-imported\n// inside the helpers that use them (`defaultPickTask`, writeSuccessReceipt,\n// writeFailureReceipt). That keeps the test surface tiny \u2014 tests that inject\n// their own `pickTask` / `spawn` never trigger the real SDK load.\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Default adapter used when spawning workers. */\nexport const DEFAULT_ADAPTER = 'claude-code' as const;\n\n/**\n * Backoff delays between successive retries for the same task (milliseconds).\n * Index n = delay before attempt n+1. After exhaustion the task is `stuck`.\n * 30 s \u2192 5 min \u2192 30 min, then the task is marked stuck.\n */\nexport const RETRY_BACKOFF_MS: readonly number[] = [30_000, 300_000, 1_800_000];\n\n/**\n * Maximum spawn attempts per task before it is classified as `stuck`.\n * Matches RETRY_BACKOFF_MS.length but surfaced as a named constant for\n * readability in tests and status output.\n */\nexport const MAX_TASK_ATTEMPTS = RETRY_BACKOFF_MS.length;\n\n/**\n * Threshold for self-pause: if this many tasks become `stuck` within a\n * rolling 1-hour window, the daemon flips killSwitch=true and exits.\n */\nexport const SELF_PAUSE_STUCK_THRESHOLD = 5;\n\n/** Rolling window (ms) used for stuck-rate calculation. */\nexport const SELF_PAUSE_WINDOW_MS = 60 * 60 * 1000;\n\n/** Reason stored on the state file when self-pause fires. */\nexport const SELF_PAUSE_REASON = 'self-pause: 5 stuck tasks in 1 hour';\n\n/** Max wall-clock time for a single spawn before forceful kill (30 min). */\nexport const SPAWN_TIMEOUT_MS = 30 * 60 * 1000;\n\n// ---------------------------------------------------------------------------\n// Tick outcome types\n// ---------------------------------------------------------------------------\n\n/** Discriminant for the tick outcome. */\nexport type TickOutcomeKind =\n | 'killed' // killSwitch was active at some checkpoint\n | 'no-task' // no unblocked task available\n | 'backoff' // a task is in retry backoff, skipped this tick\n | 'success' // spawn exited 0\n | 'failure' // spawn exited non-zero, retry scheduled\n | 'stuck' // attempts exhausted, task marked stuck\n | 'self-paused' // stuck-rate threshold tripped self-pause\n | 'error'; // unexpected error in tick machinery itself\n\n/** Structured outcome of a single tick. */\nexport interface TickOutcome {\n /** Discriminant describing how the tick ended. */\n kind: TickOutcomeKind;\n /** Task id that was the subject of this tick (if any). */\n taskId: string | null;\n /** Human-readable detail (one line). */\n detail: string;\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/** Options for {@link runTick}. */\nexport interface TickOptions {\n /** Absolute path to the project root (contains `.cleo/`). */\n projectRoot: string;\n /** Absolute path to sentient-state.json. */\n statePath: string;\n /**\n * Adapter to pass to `cleo orchestrate spawn --adapter`. Defaults to\n * {@link DEFAULT_ADAPTER}. Overridden in tests via options.spawn.\n */\n adapter?: string;\n /**\n * Dry-run mode: skip the actual `cleo orchestrate spawn` subprocess and\n * treat the pick as a no-op (still records picked stat). Used by\n * `cleo sentient tick --dry-run`.\n */\n dryRun?: boolean;\n /**\n * Override for the spawn function \u2014 lets tests inject a deterministic fake\n * without forking real subprocesses. Must resolve to an exit code.\n *\n * When omitted, the default implementation spawns\n * `cleo orchestrate spawn <taskId> --adapter <adapter>` and resolves with\n * the child's exit code.\n */\n spawn?: (taskId: string, adapter: string) => Promise<SpawnResult>;\n /**\n * Override for the \"pick next unblocked task\" source. Lets tests return\n * a deterministic task without constructing a SQLite fixture.\n */\n pickTask?: (projectRoot: string) => Promise<Task | null>;\n /**\n * New observation count since last consolidation that triggers the volume\n * dream cycle. Defaults to {@link DREAM_VOLUME_THRESHOLD_DEFAULT}.\n * Pass 0 to disable volume trigger. Injected by tests.\n */\n dreamVolumeThreshold?: number;\n /**\n * Number of consecutive no-task ticks before the idle dream trigger fires.\n * Defaults to {@link DREAM_IDLE_TICKS_DEFAULT}.\n * Pass 0 to disable idle trigger. Injected by tests.\n */\n dreamIdleTicks?: number;\n /**\n * Override for the dream trigger function \u2014 lets tests assert dream calls\n * without touching the real brain.db stack.\n * Signature mirrors `checkAndDream` from `@cleocode/core`.\n */\n checkAndDream?: (\n projectRoot: string,\n opts?: { volumeThreshold?: number; inline?: boolean },\n ) => Promise<{ triggered: boolean; tier: string | null; skippedReason?: string }>;\n}\n\n/** Result of a spawn invocation. */\nexport interface SpawnResult {\n /** Process exit code (0 = success). */\n exitCode: number;\n /** Captured stdout, truncated by the caller if needed. */\n stdout: string;\n /** Captured stderr, truncated by the caller if needed. */\n stderr: string;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Fresh-load state and return true if killSwitch is active. Used at every\n * checkpoint to avoid mid-tick kill limbo (Round 2 audit \u00A71).\n */\nasync function killSwitchActive(statePath: string): Promise<boolean> {\n const state = await readSentientState(statePath);\n return state.killSwitch === true;\n}\n\n/**\n * Build the list of stuck timestamps that fall inside the rolling window.\n */\nfunction pruneStuckWindow(timestamps: readonly number[], now: number): number[] {\n const cutoff = now - SELF_PAUSE_WINDOW_MS;\n return timestamps.filter((t) => t >= cutoff);\n}\n\n/**\n * Default SDK-backed task picker. Delegates to the orchestration domain via\n * the @cleocode/core/sdk facade.\n *\n * Tier-1 scope: we pick any unblocked, non-proposed task regardless of which\n * epic it belongs to \u2014 the picker walks the full task set to find the next\n * actionable item.\n */\nasync function defaultPickTask(projectRoot: string): Promise<Task | null> {\n // Lazy import so unit tests that inject `pickTask` never trigger the SDK\n // load (which pulls in the full @cleocode/core graph).\n const { Cleo } = await import('@cleocode/core/sdk');\n const { getReadyTasks } = await import('@cleocode/core/tasks');\n\n const cleo = await Cleo.init(projectRoot);\n // Use find() to get candidate tasks. We specifically avoid 'proposed' by\n // only filtering on pending/active/blocked. getReadyTasks() from the\n // dependency-check module is authoritative for \"unblocked\".\n const pending = (await cleo.tasks.find({ status: 'pending', limit: 500 })) as {\n success?: boolean;\n data?: { tasks?: Task[] };\n };\n const candidates: Task[] = Array.isArray(pending?.data?.tasks) ? pending.data.tasks : [];\n if (candidates.length === 0) return null;\n\n const ready = getReadyTasks(candidates);\n if (ready.length === 0) return null;\n\n // Deterministic pick: lowest id wins (reproducible for tests).\n ready.sort((a, b) => a.id.localeCompare(b.id));\n return ready[0];\n}\n\n/**\n * Default spawn implementation. Shells out to\n * `cleo orchestrate spawn <taskId> --adapter <adapter>` and captures output.\n *\n * Note: we MUST shell out here \u2014 the spawn verb shells out to\n * claude-code / gemini-cli / ollama as external tools. Using the SDK\n * directly is not possible without re-implementing adapter dispatch.\n */\nfunction defaultSpawn(taskId: string, adapter: string, projectRoot: string): Promise<SpawnResult> {\n return new Promise<SpawnResult>((resolve) => {\n const args = ['orchestrate', 'spawn', taskId, '--adapter', adapter];\n const child = spawn('cleo', args, {\n cwd: projectRoot,\n env: { ...process.env, CLEO_SENTIENT_SPAWN: '1' },\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n let stdout = '';\n let stderr = '';\n\n child.stdout?.on('data', (chunk: Buffer) => {\n stdout += chunk.toString('utf-8');\n });\n child.stderr?.on('data', (chunk: Buffer) => {\n stderr += chunk.toString('utf-8');\n });\n\n const timer = setTimeout(() => {\n child.kill('SIGTERM');\n }, SPAWN_TIMEOUT_MS);\n\n child.on('error', (err: Error) => {\n clearTimeout(timer);\n resolve({\n exitCode: 1,\n stdout,\n stderr: stderr + `\\n[sentient] spawn error: ${err.message}`,\n });\n });\n child.on('exit', (code) => {\n clearTimeout(timer);\n resolve({\n exitCode: code ?? 1,\n stdout: stdout.slice(-4000),\n stderr: stderr.slice(-4000),\n });\n });\n });\n}\n\n/**\n * Record a successful spawn to the brain via `memory.observe`.\n * Swallows errors: receipt write must never break the tick.\n */\nasync function writeSuccessReceipt(\n projectRoot: string,\n taskId: string,\n exitCode: number,\n): Promise<void> {\n try {\n const { Cleo } = await import('@cleocode/core/sdk');\n const cleo = await Cleo.init(projectRoot);\n await cleo.memory.observe({\n text: `sentient-tier1: task ${taskId} completed successfully (exit=${exitCode})`,\n title: `sentient-receipt: ${taskId}`,\n });\n } catch {\n // Receipt is best-effort; do not fail the tick.\n }\n}\n\n// ---------------------------------------------------------------------------\n// Dream-cycle trigger state (T996)\n// ---------------------------------------------------------------------------\n\n/**\n * Number of consecutive no-task ticks since the last successful task pick.\n * Used by the idle dream trigger: when this counter reaches `dreamIdleTicks`,\n * `checkAndDream` is called with the idle tier.\n *\n * Reset to 0 whenever a task is successfully picked.\n */\nlet consecutiveIdleTicks = 0;\n\n/**\n * Evaluate volume + idle dream triggers and call `checkAndDream` when either\n * fires. Errors are swallowed \u2014 dream trigger must never crash the tick.\n *\n * @param projectRoot - Project root for brain.db resolution.\n * @param opts - Tick options (provides thresholds + injectable checkAndDream).\n * @param pickedTask - Whether a task was picked this tick (resets idle counter).\n */\nasync function maybeTriggerDream(\n projectRoot: string,\n opts: TickOptions,\n pickedTask: boolean,\n): Promise<void> {\n const volumeThreshold = opts.dreamVolumeThreshold ?? DREAM_VOLUME_THRESHOLD_DEFAULT;\n const idleTicksThreshold = opts.dreamIdleTicks ?? DREAM_IDLE_TICKS_DEFAULT;\n\n // Disable both triggers when thresholds are 0 (test escape hatch).\n if (volumeThreshold <= 0 && idleTicksThreshold <= 0) return;\n\n if (pickedTask) {\n consecutiveIdleTicks = 0;\n } else {\n consecutiveIdleTicks += 1;\n }\n\n const dreamer =\n opts.checkAndDream ??\n (async (root: string, dreamerOpts?: { volumeThreshold?: number; inline?: boolean }) => {\n const { checkAndDream } = await import('@cleocode/core/internal');\n return checkAndDream(root, dreamerOpts);\n });\n\n try {\n await dreamer(projectRoot, {\n volumeThreshold: volumeThreshold > 0 ? volumeThreshold : undefined,\n inline: false,\n }).catch((err: unknown) => {\n console.warn('[sentient/tick] dream trigger error:', err);\n });\n } catch (err) {\n console.warn('[sentient/tick] dream trigger threw:', err);\n }\n}\n\n/**\n * Record a failure to the brain via `memory.observe`.\n * Swallows errors: receipt write must never break the tick.\n */\nasync function writeFailureReceipt(\n projectRoot: string,\n taskId: string,\n attempt: number,\n exitCode: number,\n reason: string,\n): Promise<void> {\n try {\n const { Cleo } = await import('@cleocode/core/sdk');\n const cleo = await Cleo.init(projectRoot);\n await cleo.memory.observe({\n text:\n `sentient-tier1: task ${taskId} failed (attempt=${attempt}/${MAX_TASK_ATTEMPTS}, ` +\n `exit=${exitCode}). reason=${reason.slice(0, 500)}`,\n title: `sentient-failure: ${taskId}`,\n });\n } catch {\n // Receipt is best-effort.\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Run a single tick of the sentient loop.\n *\n * Every checkpoint re-reads state so that a killSwitch flipped mid-tick is\n * honoured on the next instruction.\n *\n * @param options - Tick options (see {@link TickOptions})\n * @returns Structured outcome describing how the tick ended.\n */\nexport async function runTick(options: TickOptions): Promise<TickOutcome> {\n const { projectRoot, statePath } = options;\n const adapter = options.adapter ?? DEFAULT_ADAPTER;\n const now = Date.now();\n\n // -- Checkpoint 1: killSwitch before any work ------------------------------\n if (await killSwitchActive(statePath)) {\n await incrementStats(statePath, { ticksKilled: 1 });\n await patchSentientState(statePath, { lastTickAt: new Date(now).toISOString() });\n return { kind: 'killed', taskId: null, detail: 'killSwitch active before pick' };\n }\n\n // -- Pick next unblocked task ---------------------------------------------\n const picker = options.pickTask ?? defaultPickTask;\n let task: Task | null;\n try {\n task = await picker(projectRoot);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n await incrementStats(statePath, { ticksExecuted: 1 });\n await patchSentientState(statePath, { lastTickAt: new Date(now).toISOString() });\n return { kind: 'error', taskId: null, detail: `picker threw: ${message}` };\n }\n\n if (task === null) {\n await incrementStats(statePath, { ticksExecuted: 1 });\n await patchSentientState(statePath, { lastTickAt: new Date(now).toISOString() });\n return { kind: 'no-task', taskId: null, detail: 'no unblocked tasks available' };\n }\n\n // -- Respect per-task backoff ---------------------------------------------\n const preSpawnState = await readSentientState(statePath);\n const existingStuck: StuckTaskRecord | undefined = preSpawnState.stuckTasks[task.id];\n if (existingStuck && existingStuck.nextRetryAt > now) {\n await incrementStats(statePath, { ticksExecuted: 1 });\n await patchSentientState(statePath, { lastTickAt: new Date(now).toISOString() });\n return {\n kind: 'backoff',\n taskId: task.id,\n detail: `task ${task.id} in backoff until ${new Date(existingStuck.nextRetryAt).toISOString()}`,\n };\n }\n\n // -- Checkpoint 2: killSwitch before spawn --------------------------------\n if (await killSwitchActive(statePath)) {\n await incrementStats(statePath, { ticksKilled: 1 });\n await patchSentientState(statePath, { lastTickAt: new Date(now).toISOString() });\n return { kind: 'killed', taskId: task.id, detail: 'killSwitch active before spawn' };\n }\n\n // -- Mark task active ------------------------------------------------------\n await incrementStats(statePath, { tasksPicked: 1 });\n await patchSentientState(statePath, { activeTaskId: task.id });\n\n // -- Spawn worker ---------------------------------------------------------\n let spawnResult: SpawnResult;\n if (options.dryRun === true) {\n spawnResult = {\n exitCode: 0,\n stdout: '[dry-run] spawn skipped',\n stderr: '',\n };\n } else {\n try {\n const spawner = options.spawn ?? ((tid, adp) => defaultSpawn(tid, adp, projectRoot));\n spawnResult = await spawner(task.id, adapter);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n spawnResult = { exitCode: 1, stdout: '', stderr: `spawn threw: ${message}` };\n }\n }\n\n // -- Checkpoint 3: killSwitch before recording ----------------------------\n if (await killSwitchActive(statePath)) {\n await incrementStats(statePath, { ticksKilled: 1 });\n await patchSentientState(statePath, {\n lastTickAt: new Date(Date.now()).toISOString(),\n activeTaskId: null,\n });\n return {\n kind: 'killed',\n taskId: task.id,\n detail: 'killSwitch active after spawn; result not recorded',\n };\n }\n\n // -- Classify + record -----------------------------------------------------\n if (spawnResult.exitCode === 0) {\n await writeSuccessReceipt(projectRoot, task.id, spawnResult.exitCode);\n // Clear stuck entry on success.\n const post = await readSentientState(statePath);\n const { [task.id]: _removed, ...rest } = post.stuckTasks;\n void _removed;\n await patchSentientState(statePath, {\n stuckTasks: rest,\n activeTaskId: null,\n lastTickAt: new Date(Date.now()).toISOString(),\n });\n await incrementStats(statePath, { tasksCompleted: 1, ticksExecuted: 1 });\n return {\n kind: 'success',\n taskId: task.id,\n detail: `task ${task.id} completed (exit=0)`,\n };\n }\n\n // -- Failure path: increment attempts, record backoff or stuck -----------\n const currentAttempts = existingStuck?.attempts ?? 0;\n const nextAttempts = currentAttempts + 1;\n const failureReason = spawnResult.stderr.slice(-500) || `exit=${spawnResult.exitCode}`;\n\n await writeFailureReceipt(\n projectRoot,\n task.id,\n nextAttempts,\n spawnResult.exitCode,\n failureReason,\n );\n await incrementStats(statePath, { tasksFailed: 1, ticksExecuted: 1 });\n\n if (nextAttempts >= MAX_TASK_ATTEMPTS) {\n // Mark task stuck. Record timestamp in rolling window; self-pause if \u2265 threshold.\n const windowed = pruneStuckWindow(preSpawnState.stuckTimestamps, now);\n windowed.push(now);\n\n const stuckRecord: StuckTaskRecord = {\n attempts: nextAttempts,\n lastFailureAt: new Date(now).toISOString(),\n nextRetryAt: Number.MAX_SAFE_INTEGER, // owner-only release\n lastReason: failureReason,\n };\n\n const post = await readSentientState(statePath);\n const updatedStuckTasks: Record<string, StuckTaskRecord> = {\n ...post.stuckTasks,\n [task.id]: stuckRecord,\n };\n\n const shouldSelfPause = windowed.length >= SELF_PAUSE_STUCK_THRESHOLD;\n\n await patchSentientState(statePath, {\n stuckTasks: updatedStuckTasks,\n stuckTimestamps: windowed,\n activeTaskId: null,\n lastTickAt: new Date(now).toISOString(),\n ...(shouldSelfPause ? { killSwitch: true, killSwitchReason: SELF_PAUSE_REASON } : {}),\n });\n\n if (shouldSelfPause) {\n return {\n kind: 'self-paused',\n taskId: task.id,\n detail:\n `task ${task.id} is stuck; self-pause fired ` +\n `(${windowed.length}/${SELF_PAUSE_STUCK_THRESHOLD} stucks in window)`,\n };\n }\n\n return {\n kind: 'stuck',\n taskId: task.id,\n detail:\n `task ${task.id} stuck after ${nextAttempts} attempts; ` +\n `owner must re-enable via \\`cleo sentient resume\\``,\n };\n }\n\n // Schedule next retry with backoff.\n const backoff =\n RETRY_BACKOFF_MS[nextAttempts - 1] ?? RETRY_BACKOFF_MS[RETRY_BACKOFF_MS.length - 1];\n const stuckRecord: StuckTaskRecord = {\n attempts: nextAttempts,\n lastFailureAt: new Date(now).toISOString(),\n nextRetryAt: now + backoff,\n lastReason: failureReason,\n };\n const post = await readSentientState(statePath);\n await patchSentientState(statePath, {\n stuckTasks: { ...post.stuckTasks, [task.id]: stuckRecord },\n activeTaskId: null,\n lastTickAt: new Date(now).toISOString(),\n });\n\n return {\n kind: 'failure',\n taskId: task.id,\n detail:\n `task ${task.id} failed (attempt=${nextAttempts}/${MAX_TASK_ATTEMPTS}); ` +\n `retry scheduled at ${new Date(now + backoff).toISOString()}`,\n };\n}\n\n/**\n * Convenience wrapper used by the daemon cron handler and the `cleo sentient\n * tick` CLI command. Reads state, runs a tick, swallows any unexpected\n * exception so the cron scheduler never sees a rejection.\n *\n * After the tick completes, evaluates volume + idle dream triggers via\n * {@link maybeTriggerDream}. Dream errors are swallowed independently so\n * they never affect the tick outcome.\n *\n * @param options - Tick options\n * @returns The tick outcome (or an `error` outcome if the tick itself threw).\n */\nexport async function safeRunTick(options: TickOptions): Promise<TickOutcome> {\n let outcome: TickOutcome;\n try {\n outcome = await runTick(options);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n try {\n await incrementStats(options.statePath, { ticksExecuted: 1 });\n } catch {\n // ignore\n }\n outcome = { kind: 'error', taskId: null, detail: `tick threw: ${message}` };\n }\n\n // Dream trigger: fire volume + idle checks after every tick.\n // A task was \"picked\" when the tick progressed past the no-task check\n // (i.e. kind is not 'no-task', 'killed', or 'error').\n const pickedTask =\n outcome.kind !== 'no-task' &&\n outcome.kind !== 'killed' &&\n outcome.kind !== 'error' &&\n outcome.taskId !== null;\n\n await maybeTriggerDream(options.projectRoot, options, pickedTask).catch(() => {\n // Dream errors must never propagate to the tick caller.\n });\n\n return outcome;\n}\n\n/**\n * Type-narrowing helper used by tests and status rendering to identify tick\n * outcomes that consumed a retry attempt.\n */\nexport function isFailureOutcome(\n outcome: TickOutcome,\n): outcome is TickOutcome & { kind: 'failure' | 'stuck' | 'self-paused' } {\n return outcome.kind === 'failure' || outcome.kind === 'stuck' || outcome.kind === 'self-paused';\n}\n\n/**\n * Returns a shallow view of the current state's kill status.\n * Exposed for diagnostic/test consumers.\n */\nexport async function getKillStatus(\n statePath: string,\n): Promise<Pick<SentientState, 'killSwitch' | 'killSwitchReason'>> {\n const state = await readSentientState(statePath);\n return { killSwitch: state.killSwitch, killSwitchReason: state.killSwitchReason };\n}\n\n/**\n * Reset dream-cycle in-process state.\n *\n * Intended for test teardown only \u2014 clears `consecutiveIdleTicks` so that\n * successive test cases start from a clean slate.\n *\n * @internal\n */\nexport function _resetDreamTickState(): void {\n consecutiveIdleTicks = 0;\n}\n\n/**\n * Return the current consecutive-idle-tick counter value.\n *\n * Read-only accessor for test assertions. The counter is reset to 0 whenever\n * a task is picked, and incremented on each no-task tick.\n *\n * @internal\n */\nexport function _getConsecutiveIdleTicks(): number {\n return consecutiveIdleTicks;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["spawn", "mkdir", "join", "join", "killSwitchActive", "post", "stuckRecord", "mkdir", "join", "spawn"]
7
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @cleocode/core/sentient — Tier-1/Tier-2 sentient daemon public API.
3
+ *
4
+ * Provides the autonomous loop logic: tick execution, Tier-2 proposal
5
+ * generation, state management, rate limiting, and ingesters.
6
+ *
7
+ * @see ADR-054 — Sentient Loop Tier-1
8
+ * @package @cleocode/core
9
+ */
10
+ export * from './daemon.js';
11
+ export * from './ingesters/brain-ingester.js';
12
+ export * from './ingesters/nexus-ingester.js';
13
+ export * from './ingesters/test-ingester.js';
14
+ export * from './proposal-rate-limiter.js';
15
+ export * from './propose-tick.js';
16
+ export * from './state.js';
17
+ export * from './tick.js';
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sentient/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,cAAc,aAAa,CAAC;AAC5B,cAAc,+BAA+B,CAAC;AAC9C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC"}