@cleocode/cleo 2026.4.130 → 2026.4.132

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
@@ -363,13 +363,72 @@ var init_attachment_schema = __esm({
363
363
  });
364
364
 
365
365
  // packages/contracts/src/branch-lock.ts
366
+ var BRANCH_LOCK_ERROR_CODES;
366
367
  var init_branch_lock = __esm({
367
368
  "packages/contracts/src/branch-lock.ts"() {
368
369
  "use strict";
370
+ BRANCH_LOCK_ERROR_CODES = {
371
+ /** L2: git shim blocked a branch-mutating operation. */
372
+ E_GIT_OP_BLOCKED: "E_GIT_OP_BLOCKED",
373
+ /** L1: spawn attempted without a worktree handle. */
374
+ E_WORKTREE_REQUIRED: "E_WORKTREE_REQUIRED",
375
+ /** L1: worktree path does not exist or is not a valid git worktree. */
376
+ E_WORKTREE_INVALID: "E_WORKTREE_INVALID",
377
+ /** L1: cherry-pick failed during worktree.complete. */
378
+ E_CHERRY_PICK_FAILED: "E_CHERRY_PICK_FAILED",
379
+ /** L3: filesystem harden failed. */
380
+ E_FS_HARDEN_FAILED: "E_FS_HARDEN_FAILED",
381
+ /** L4a: HMAC token invalid or missing. */
382
+ E_OVERRIDE_TOKEN_INVALID: "E_OVERRIDE_TOKEN_INVALID",
383
+ /** L4b: caller has CLEO_AGENT_ROLE=worker|lead|subagent — override forbidden. */
384
+ E_OVERRIDE_FORBIDDEN_AGENT_ROLE: "E_OVERRIDE_FORBIDDEN_AGENT_ROLE",
385
+ /** L4c: override requires TTY but stdin/stderr is not a TTY. */
386
+ E_OVERRIDE_NEEDS_TTY: "E_OVERRIDE_NEEDS_TTY",
387
+ /** L4d: session override limit exceeded. */
388
+ E_OVERRIDE_RATE_LIMIT: "E_OVERRIDE_RATE_LIMIT"
389
+ };
369
390
  }
370
391
  });
371
392
 
372
393
  // packages/contracts/src/exit-codes.ts
394
+ function isErrorCode(code) {
395
+ return code >= 1 && code < 100;
396
+ }
397
+ function isSuccessCode(code) {
398
+ return code === 0 || code >= 100;
399
+ }
400
+ function isNoChangeCode(code) {
401
+ return code === 102 /* NO_CHANGE */;
402
+ }
403
+ function isRecoverableCode(code) {
404
+ const nonRecoverable = /* @__PURE__ */ new Set([
405
+ 3 /* FILE_ERROR */,
406
+ 5 /* DEPENDENCY_ERROR */,
407
+ 14 /* CIRCULAR_REFERENCE */,
408
+ 18 /* CASCADE_FAILED */,
409
+ 37 /* SESSION_CLOSE_BLOCKED */,
410
+ 46 /* VERIFICATION_LOCKED */,
411
+ 50 /* CONTEXT_WARNING */,
412
+ 51 /* CONTEXT_CAUTION */,
413
+ 52 /* CONTEXT_CRITICAL */,
414
+ 53 /* CONTEXT_EMERGENCY */,
415
+ 54 /* CONTEXT_STALE */,
416
+ 64 /* AUTONOMOUS_BOUNDARY */,
417
+ 65 /* HANDOFF_REQUIRED */,
418
+ 72 /* NEXUS_PERMISSION_DENIED */,
419
+ 75 /* NEXUS_REGISTRY_CORRUPT */,
420
+ 82 /* CIRCULAR_VALIDATION */,
421
+ 83 /* LIFECYCLE_TRANSITION_INVALID */,
422
+ 85 /* ARTIFACT_TYPE_UNKNOWN */,
423
+ 89 /* ARTIFACT_ROLLBACK_FAILED */,
424
+ 93 /* DIGEST_MISMATCH */
425
+ ]);
426
+ if (!isErrorCode(code)) return false;
427
+ return !nonRecoverable.has(code);
428
+ }
429
+ function getExitCodeName(code) {
430
+ return ExitCode[code] ?? "UNKNOWN";
431
+ }
373
432
  var ExitCode;
374
433
  var init_exit_codes = __esm({
375
434
  "packages/contracts/src/exit-codes.ts"() {
@@ -471,6 +530,18 @@ var init_exit_codes = __esm({
471
530
  });
472
531
 
473
532
  // packages/contracts/src/errors.ts
533
+ function normalizeError(error, fallbackMessage = "An unexpected error occurred") {
534
+ if (error instanceof Error) {
535
+ return error;
536
+ }
537
+ if (typeof error === "string") {
538
+ return new Error(error);
539
+ }
540
+ if (error !== null && typeof error === "object" && "message" in error && typeof error.message === "string") {
541
+ return new Error(error.message);
542
+ }
543
+ return new Error(fallbackMessage);
544
+ }
474
545
  function getErrorMessage(error, fallback = "Unknown error") {
475
546
  if (error instanceof Error) {
476
547
  return error.message;
@@ -483,10 +554,87 @@ function getErrorMessage(error, fallback = "Unknown error") {
483
554
  }
484
555
  return fallback;
485
556
  }
557
+ function formatError(error, context, includeStack = false) {
558
+ const message = getErrorMessage(error);
559
+ const prefix = context ? `[${context}] ` : "";
560
+ let result = `${prefix}${message}`;
561
+ if (includeStack && error instanceof Error && error.stack) {
562
+ result += `
563
+ ${error.stack}`;
564
+ }
565
+ return result;
566
+ }
567
+ function isErrorType(error, codeOrName) {
568
+ if (error instanceof Error) {
569
+ if (error.name === codeOrName) {
570
+ return true;
571
+ }
572
+ if ("code" in error && error.code === codeOrName) {
573
+ return true;
574
+ }
575
+ }
576
+ return false;
577
+ }
578
+ function createErrorResult(error) {
579
+ return {
580
+ success: false,
581
+ error: getErrorMessage(error)
582
+ };
583
+ }
584
+ function createSuccessResult() {
585
+ return { success: true };
586
+ }
587
+ function isErrorResult(result) {
588
+ return !result.success;
589
+ }
590
+ var ThinAgentViolationError, LifecycleScopeDeniedError;
486
591
  var init_errors = __esm({
487
592
  "packages/contracts/src/errors.ts"() {
488
593
  "use strict";
489
594
  init_exit_codes();
595
+ ThinAgentViolationError = class extends Error {
596
+ /**
597
+ * @param agentId - The offending agent's unique identifier.
598
+ * @param role - The offending agent's declared role (lead, worker, etc.).
599
+ * @param attemptedAction - The spawn action that was blocked.
600
+ */
601
+ constructor(agentId, role, attemptedAction) {
602
+ super(
603
+ `E_THIN_AGENT_VIOLATION: agent '${agentId}' (role=${role}) attempted '${attemptedAction}'. Only orchestrators may spawn. Escalate via playbook approval gate.`
604
+ );
605
+ this.agentId = agentId;
606
+ this.role = role;
607
+ this.attemptedAction = attemptedAction;
608
+ this.name = "ThinAgentViolationError";
609
+ }
610
+ agentId;
611
+ role;
612
+ attemptedAction;
613
+ /** Stable LAFS error code string for envelope emission. */
614
+ code = "E_THIN_AGENT_VIOLATION";
615
+ /** Numeric exit code aligned with {@link ExitCode.THIN_AGENT_VIOLATION}. */
616
+ exitCode = 68 /* THIN_AGENT_VIOLATION */;
617
+ };
618
+ LifecycleScopeDeniedError = class extends Error {
619
+ /**
620
+ * @param epicId - The epic whose lifecycle mutation was blocked.
621
+ * @param sessionScope - Human-readable description of the current session scope.
622
+ */
623
+ constructor(epicId, sessionScope) {
624
+ super(
625
+ `E_LIFECYCLE_SCOPE_DENIED: lifecycle stage advancement for epic '${epicId}' requires a session scoped to that epic or an owner override. Current session scope: ${sessionScope}. Use CLEO_OWNER_OVERRIDE=1 if this is an authorized operation.`
626
+ );
627
+ this.epicId = epicId;
628
+ this.sessionScope = sessionScope;
629
+ this.name = "LifecycleScopeDeniedError";
630
+ }
631
+ epicId;
632
+ sessionScope;
633
+ /** Stable LAFS error code string for envelope emission. */
634
+ code = "E_LIFECYCLE_SCOPE_DENIED";
635
+ /** Numeric exit code aligned with {@link ExitCode.TASK_NOT_IN_SCOPE} (34). */
636
+ exitCode = 34 /* TASK_NOT_IN_SCOPE */;
637
+ };
490
638
  }
491
639
  });
492
640
 
@@ -553,13 +701,49 @@ var init_evidence_record_schema = __esm({
553
701
  });
554
702
 
555
703
  // packages/contracts/src/facade.ts
704
+ var BRAIN_OBSERVATION_TYPES, AGENT_INSTANCE_STATUSES, AGENT_TYPES;
556
705
  var init_facade = __esm({
557
706
  "packages/contracts/src/facade.ts"() {
558
707
  "use strict";
708
+ BRAIN_OBSERVATION_TYPES = [
709
+ "discovery",
710
+ "change",
711
+ "feature",
712
+ "bugfix",
713
+ "decision",
714
+ "refactor",
715
+ "diary"
716
+ ];
717
+ AGENT_INSTANCE_STATUSES = [
718
+ "starting",
719
+ "active",
720
+ "idle",
721
+ "error",
722
+ "crashed",
723
+ "stopped"
724
+ ];
725
+ AGENT_TYPES = [
726
+ "orchestrator",
727
+ "executor",
728
+ "researcher",
729
+ "architect",
730
+ "validator",
731
+ "documentor",
732
+ "custom"
733
+ ];
559
734
  }
560
735
  });
561
736
 
562
737
  // packages/contracts/src/lafs.ts
738
+ function isLafsSuccess(envelope) {
739
+ return envelope.success === true;
740
+ }
741
+ function isLafsError(envelope) {
742
+ return envelope.success === false;
743
+ }
744
+ function isGatewayEnvelope(envelope) {
745
+ return "_meta" in envelope && envelope._meta !== void 0;
746
+ }
563
747
  var init_lafs = __esm({
564
748
  "packages/contracts/src/lafs.ts"() {
565
749
  "use strict";
@@ -720,6 +904,10 @@ var init_worktree = __esm({
720
904
  });
721
905
 
722
906
  // packages/contracts/src/operations/index.ts
907
+ var operations_exports = {};
908
+ __export(operations_exports, {
909
+ paramsToCittyArgs: () => paramsToCittyArgs
910
+ });
723
911
  var init_operations = __esm({
724
912
  "packages/contracts/src/operations/index.ts"() {
725
913
  "use strict";
@@ -745,6 +933,21 @@ var init_operations = __esm({
745
933
  });
746
934
 
747
935
  // packages/contracts/src/peer.ts
936
+ function isPeerIdentity(value) {
937
+ if (typeof value !== "object" || value === null) return false;
938
+ const v = value;
939
+ return typeof v["peerId"] === "string" && v["peerId"].length > 0 && typeof v["peerKind"] === "string" && ["orchestrator", "lead", "worker", "subagent"].includes(v["peerKind"]) && typeof v["cantFile"] === "string" && v["cantFile"].length > 0 && typeof v["displayName"] === "string" && typeof v["description"] === "string";
940
+ }
941
+ function assertPeerIdentity(value) {
942
+ if (!isPeerIdentity(value)) {
943
+ throw new TypeError(
944
+ `Expected PeerIdentity { peerId, peerKind, cantFile, displayName, description } \u2014 got: ${JSON.stringify(value)}`
945
+ );
946
+ }
947
+ }
948
+ function filterPeerIdentities(values) {
949
+ return values.filter(isPeerIdentity);
950
+ }
748
951
  var init_peer = __esm({
749
952
  "packages/contracts/src/peer.ts"() {
750
953
  "use strict";
@@ -752,9 +955,69 @@ var init_peer = __esm({
752
955
  });
753
956
 
754
957
  // packages/contracts/src/session.ts
958
+ var SessionView;
755
959
  var init_session2 = __esm({
756
960
  "packages/contracts/src/session.ts"() {
757
961
  "use strict";
962
+ SessionView = class _SessionView {
963
+ _sessions;
964
+ constructor(sessions2) {
965
+ this._sessions = sessions2;
966
+ }
967
+ /** Create a SessionView from a Session array. */
968
+ static from(sessions2) {
969
+ return new _SessionView(sessions2);
970
+ }
971
+ /** All sessions in the view (readonly). */
972
+ get all() {
973
+ return this._sessions;
974
+ }
975
+ /** Number of sessions. */
976
+ get length() {
977
+ return this._sessions.length;
978
+ }
979
+ /** Find the currently active session (if any). */
980
+ findActive() {
981
+ return this._sessions.find((s) => s.status === "active");
982
+ }
983
+ /** Find a session by ID. */
984
+ findById(id) {
985
+ return this._sessions.find((s) => s.id === id);
986
+ }
987
+ /** Filter sessions by one or more statuses. */
988
+ filterByStatus(...statuses) {
989
+ return this._sessions.filter((s) => statuses.includes(s.status));
990
+ }
991
+ /** Find sessions matching a scope type and optional rootTaskId. */
992
+ findByScope(type2, rootTaskId) {
993
+ return this._sessions.filter((s) => {
994
+ if (s.scope?.type !== type2) return false;
995
+ if (rootTaskId && s.scope?.rootTaskId !== rootTaskId) return false;
996
+ return true;
997
+ });
998
+ }
999
+ /** Sort sessions by a date field. Returns a new array (does not mutate). */
1000
+ sortByDate(field, descending = true) {
1001
+ return [...this._sessions].sort((a, b) => {
1002
+ const aDate = new Date(a[field] || "").getTime();
1003
+ const bDate = new Date(b[field] || "").getTime();
1004
+ return descending ? bDate - aDate : aDate - bDate;
1005
+ });
1006
+ }
1007
+ /** Get the most recently started session. */
1008
+ mostRecent() {
1009
+ if (this._sessions.length === 0) return void 0;
1010
+ return this.sortByDate("startedAt", true)[0];
1011
+ }
1012
+ /** Convert back to a plain Session array (shallow copy). */
1013
+ toArray() {
1014
+ return [...this._sessions];
1015
+ }
1016
+ /** Support for-of iteration. */
1017
+ [Symbol.iterator]() {
1018
+ return this._sessions[Symbol.iterator]();
1019
+ }
1020
+ };
758
1021
  }
759
1022
  });
760
1023
 
@@ -819,10 +1082,95 @@ var init_session_journal = __esm({
819
1082
  });
820
1083
 
821
1084
  // packages/contracts/src/status-registry.ts
822
- var TASK_STATUS_SYMBOLS_UNICODE, TASK_STATUS_SYMBOLS_ASCII;
1085
+ function isValidStatus(entityType, value) {
1086
+ return STATUS_REGISTRY[entityType].includes(value);
1087
+ }
1088
+ var TASK_STATUSES, SESSION_STATUSES, LIFECYCLE_PIPELINE_STATUSES, LIFECYCLE_STAGE_STATUSES, ADR_STATUSES, GATE_STATUSES, MANIFEST_STATUSES, TERMINAL_TASK_STATUSES, TERMINAL_PIPELINE_STATUSES, TERMINAL_STAGE_STATUSES, STATUS_REGISTRY, PIPELINE_STATUS_ICONS, STAGE_STATUS_ICONS, TASK_STATUS_SYMBOLS_UNICODE, TASK_STATUS_SYMBOLS_ASCII;
823
1089
  var init_status_registry = __esm({
824
1090
  "packages/contracts/src/status-registry.ts"() {
825
1091
  "use strict";
1092
+ TASK_STATUSES = [
1093
+ "pending",
1094
+ "active",
1095
+ "blocked",
1096
+ "done",
1097
+ "cancelled",
1098
+ "archived",
1099
+ "proposed"
1100
+ ];
1101
+ SESSION_STATUSES = ["active", "ended", "orphaned", "suspended"];
1102
+ LIFECYCLE_PIPELINE_STATUSES = [
1103
+ "active",
1104
+ "completed",
1105
+ "blocked",
1106
+ "failed",
1107
+ "cancelled",
1108
+ "aborted"
1109
+ ];
1110
+ LIFECYCLE_STAGE_STATUSES = [
1111
+ "not_started",
1112
+ "in_progress",
1113
+ "blocked",
1114
+ "completed",
1115
+ "skipped",
1116
+ "failed"
1117
+ ];
1118
+ ADR_STATUSES = ["proposed", "accepted", "superseded", "deprecated"];
1119
+ GATE_STATUSES = ["pending", "passed", "failed", "waived"];
1120
+ MANIFEST_STATUSES = ["completed", "partial", "blocked", "archived"];
1121
+ TERMINAL_TASK_STATUSES = /* @__PURE__ */ new Set([
1122
+ "done",
1123
+ "cancelled",
1124
+ "archived"
1125
+ ]);
1126
+ TERMINAL_PIPELINE_STATUSES = /* @__PURE__ */ new Set([
1127
+ "completed",
1128
+ "failed",
1129
+ "cancelled",
1130
+ "aborted"
1131
+ ]);
1132
+ TERMINAL_STAGE_STATUSES = /* @__PURE__ */ new Set([
1133
+ "completed",
1134
+ "skipped",
1135
+ "failed"
1136
+ ]);
1137
+ STATUS_REGISTRY = {
1138
+ task: TASK_STATUSES,
1139
+ session: SESSION_STATUSES,
1140
+ lifecycle_pipeline: LIFECYCLE_PIPELINE_STATUSES,
1141
+ lifecycle_stage: LIFECYCLE_STAGE_STATUSES,
1142
+ adr: ADR_STATUSES,
1143
+ gate: GATE_STATUSES,
1144
+ manifest: MANIFEST_STATUSES
1145
+ };
1146
+ PIPELINE_STATUS_ICONS = {
1147
+ active: "\u25B6",
1148
+ // pipeline is running
1149
+ completed: "\u2713",
1150
+ // all stages done successfully
1151
+ blocked: "\u23F8",
1152
+ // cannot advance; waiting
1153
+ failed: "\u2717",
1154
+ // terminal failure
1155
+ cancelled: "\u2298",
1156
+ // user-initiated abandonment
1157
+ aborted: "\u23F9"
1158
+ // system-forced termination
1159
+ };
1160
+ STAGE_STATUS_ICONS = {
1161
+ not_started: "\u23F9",
1162
+ // not yet entered
1163
+ in_progress: "\u25B6",
1164
+ // actively running
1165
+ blocked: "\u23F8",
1166
+ // paused / waiting
1167
+ completed: "\u2713",
1168
+ // finished successfully
1169
+ skipped: "\u23ED",
1170
+ // intentionally bypassed
1171
+ failed: "\u2717"
1172
+ // terminal failure
1173
+ };
826
1174
  TASK_STATUS_SYMBOLS_UNICODE = {
827
1175
  pending: "\u25CB",
828
1176
  // ○ not yet started
@@ -908,6 +1256,90 @@ var init_task_evidence = __esm({
908
1256
  });
909
1257
 
910
1258
  // packages/contracts/src/index.ts
1259
+ var src_exports = {};
1260
+ __export(src_exports, {
1261
+ ADR_STATUSES: () => ADR_STATUSES,
1262
+ AGENT_INSTANCE_STATUSES: () => AGENT_INSTANCE_STATUSES,
1263
+ AGENT_TYPES: () => AGENT_TYPES,
1264
+ BRAIN_OBSERVATION_TYPES: () => BRAIN_OBSERVATION_TYPES,
1265
+ BRANCH_LOCK_ERROR_CODES: () => BRANCH_LOCK_ERROR_CODES,
1266
+ ExitCode: () => ExitCode,
1267
+ GATE_STATUSES: () => GATE_STATUSES,
1268
+ LIFECYCLE_PIPELINE_STATUSES: () => LIFECYCLE_PIPELINE_STATUSES,
1269
+ LIFECYCLE_STAGE_STATUSES: () => LIFECYCLE_STAGE_STATUSES,
1270
+ LifecycleScopeDeniedError: () => LifecycleScopeDeniedError,
1271
+ MANIFEST_STATUSES: () => MANIFEST_STATUSES,
1272
+ PIPELINE_STATUS_ICONS: () => PIPELINE_STATUS_ICONS,
1273
+ SESSION_JOURNAL_SCHEMA_VERSION: () => SESSION_JOURNAL_SCHEMA_VERSION,
1274
+ SESSION_STATUSES: () => SESSION_STATUSES,
1275
+ STAGE_STATUS_ICONS: () => STAGE_STATUS_ICONS,
1276
+ STATUS_REGISTRY: () => STATUS_REGISTRY,
1277
+ SessionView: () => SessionView,
1278
+ TASK_STATUSES: () => TASK_STATUSES,
1279
+ TASK_STATUS_SYMBOLS_ASCII: () => TASK_STATUS_SYMBOLS_ASCII,
1280
+ TASK_STATUS_SYMBOLS_UNICODE: () => TASK_STATUS_SYMBOLS_UNICODE,
1281
+ TERMINAL_PIPELINE_STATUSES: () => TERMINAL_PIPELINE_STATUSES,
1282
+ TERMINAL_STAGE_STATUSES: () => TERMINAL_STAGE_STATUSES,
1283
+ TERMINAL_TASK_STATUSES: () => TERMINAL_TASK_STATUSES,
1284
+ ThinAgentViolationError: () => ThinAgentViolationError,
1285
+ acceptanceArraySchema: () => acceptanceArraySchema,
1286
+ acceptanceGateResultSchema: () => acceptanceGateResultSchema,
1287
+ acceptanceGateSchema: () => acceptanceGateSchema,
1288
+ acceptanceItemSchema: () => acceptanceItemSchema,
1289
+ assertPeerIdentity: () => assertPeerIdentity,
1290
+ attachmentMetadataSchema: () => attachmentMetadataSchema,
1291
+ attachmentRefSchema: () => attachmentRefSchema,
1292
+ attachmentSchema: () => attachmentSchema,
1293
+ blobAttachmentSchema: () => blobAttachmentSchema,
1294
+ commandGateSchema: () => commandGateSchema,
1295
+ commandOutputEvidenceSchema: () => commandOutputEvidenceSchema,
1296
+ commandOutputRecordSchema: () => commandOutputRecordSchema,
1297
+ createErrorResult: () => createErrorResult,
1298
+ createSuccessResult: () => createSuccessResult,
1299
+ evidenceRecordSchema: () => evidenceRecordSchema,
1300
+ fileAssertionSchema: () => fileAssertionSchema,
1301
+ fileEvidenceSchema: () => fileEvidenceSchema,
1302
+ fileGateSchema: () => fileGateSchema,
1303
+ filterPeerIdentities: () => filterPeerIdentities,
1304
+ formatError: () => formatError,
1305
+ gateBaseSchema: () => gateBaseSchema,
1306
+ gateResultDetailsSchema: () => gateResultDetailsSchema,
1307
+ getErrorMessage: () => getErrorMessage,
1308
+ getExitCodeName: () => getExitCodeName,
1309
+ httpGateSchema: () => httpGateSchema,
1310
+ implDiffRecordSchema: () => implDiffRecordSchema,
1311
+ isErrorCode: () => isErrorCode,
1312
+ isErrorResult: () => isErrorResult,
1313
+ isErrorType: () => isErrorType,
1314
+ isGatewayEnvelope: () => isGatewayEnvelope,
1315
+ isLafsError: () => isLafsError,
1316
+ isLafsSuccess: () => isLafsSuccess,
1317
+ isNoChangeCode: () => isNoChangeCode,
1318
+ isPeerIdentity: () => isPeerIdentity,
1319
+ isRecoverableCode: () => isRecoverableCode,
1320
+ isSuccessCode: () => isSuccessCode,
1321
+ isValidStatus: () => isValidStatus,
1322
+ lintGateSchema: () => lintGateSchema,
1323
+ lintReportRecordSchema: () => lintReportRecordSchema,
1324
+ llmsTxtAttachmentSchema: () => llmsTxtAttachmentSchema,
1325
+ llmtxtDocAttachmentSchema: () => llmtxtDocAttachmentSchema,
1326
+ localFileAttachmentSchema: () => localFileAttachmentSchema,
1327
+ logEvidenceSchema: () => logEvidenceSchema,
1328
+ manualGateSchema: () => manualGateSchema,
1329
+ normalizeError: () => normalizeError,
1330
+ ops: () => operations_exports,
1331
+ paramsToCittyArgs: () => paramsToCittyArgs,
1332
+ screenshotEvidenceSchema: () => screenshotEvidenceSchema,
1333
+ sessionJournalDebriefSummarySchema: () => sessionJournalDebriefSummarySchema,
1334
+ sessionJournalDoctorSummarySchema: () => sessionJournalDoctorSummarySchema,
1335
+ sessionJournalEntrySchema: () => sessionJournalEntrySchema,
1336
+ taskEvidenceSchema: () => taskEvidenceSchema,
1337
+ testGateSchema: () => testGateSchema,
1338
+ testOutputEvidenceSchema: () => testOutputEvidenceSchema,
1339
+ testOutputRecordSchema: () => testOutputRecordSchema,
1340
+ urlAttachmentSchema: () => urlAttachmentSchema,
1341
+ validateSpecCheckRecordSchema: () => validateSpecCheckRecordSchema
1342
+ });
911
1343
  var init_src = __esm({
912
1344
  "packages/contracts/src/index.ts"() {
913
1345
  init_acceptance_gate_schema();
@@ -4716,6 +5148,43 @@ var init_registry = __esm({
4716
5148
  }
4717
5149
  ]
4718
5150
  },
5151
+ // T1147 W7 — BRAIN noise sweep (shadow-write envelope, self-healing gate)
5152
+ {
5153
+ gateway: "query",
5154
+ domain: "memory",
5155
+ operation: "sweep",
5156
+ description: "memory.sweep (query/mutate) \u2014 T1147 W7 BRAIN noise sweep. dry-run: detect noise candidates and write sample JSON to agent-outputs. approve <runId>: apply sweep to live brain tables (sets killSwitch during tx). status: list all noise-sweep-2440 runs. rollback <runId>: discard a staged run without applying changes.",
5157
+ tier: 0,
5158
+ idempotent: false,
5159
+ sessionRequired: false,
5160
+ requiredParams: [],
5161
+ params: [
5162
+ {
5163
+ name: "dry-run",
5164
+ type: "boolean",
5165
+ required: false,
5166
+ description: "Detect noise candidates without staging DB writes."
5167
+ },
5168
+ {
5169
+ name: "approve",
5170
+ type: "string",
5171
+ required: false,
5172
+ description: "Run ID to approve and apply to live tables."
5173
+ },
5174
+ {
5175
+ name: "status",
5176
+ type: "boolean",
5177
+ required: false,
5178
+ description: "List recent noise-sweep-2440 runs."
5179
+ },
5180
+ {
5181
+ name: "rollback",
5182
+ type: "string",
5183
+ required: false,
5184
+ description: "Run ID to roll back without applying changes."
5185
+ }
5186
+ ]
5187
+ },
4719
5188
  // T791 — LLM extraction backend status
4720
5189
  {
4721
5190
  gateway: "query",
@@ -9068,6 +9537,18 @@ async function sessionStart(projectRoot, params) {
9068
9537
  ...previousDebrief && { previousDebrief },
9069
9538
  ...previousHandoff && { previousHandoff }
9070
9539
  };
9540
+ import("@cleocode/core/sessions/session-journal.js").then(async ({ appendSessionJournalEntry }) => {
9541
+ const { SESSION_JOURNAL_SCHEMA_VERSION: SESSION_JOURNAL_SCHEMA_VERSION2 } = await Promise.resolve().then(() => (init_src(), src_exports));
9542
+ await appendSessionJournalEntry(projectRoot, {
9543
+ schemaVersion: SESSION_JOURNAL_SCHEMA_VERSION2,
9544
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
9545
+ sessionId,
9546
+ eventType: "session_start",
9547
+ agentIdentifier: agentIdentifier ?? void 0,
9548
+ scope: params.scope
9549
+ });
9550
+ }).catch(() => {
9551
+ });
9071
9552
  return { success: true, data: enrichedSession };
9072
9553
  } catch {
9073
9554
  return engineError("E_NOT_INITIALIZED", "Task database not initialized");
@@ -9128,6 +9609,38 @@ async function sessionEnd(projectRoot, notes, params) {
9128
9609
  }
9129
9610
  } catch {
9130
9611
  }
9612
+ try {
9613
+ const { appendSessionJournalEntry } = await import("@cleocode/core/sessions/session-journal.js");
9614
+ const { SESSION_JOURNAL_SCHEMA_VERSION: SESSION_JOURNAL_SCHEMA_VERSION2 } = await Promise.resolve().then(() => (init_src(), src_exports));
9615
+ let doctorSummary;
9616
+ try {
9617
+ const { scanBrainNoise } = await import("@cleocode/core/memory/brain-doctor.js");
9618
+ const scanResult = await scanBrainNoise(projectRoot);
9619
+ doctorSummary = {
9620
+ isClean: scanResult.isClean,
9621
+ findingsCount: scanResult.findings.length,
9622
+ patterns: scanResult.findings.map((f) => f.pattern),
9623
+ totalScanned: scanResult.totalScanned
9624
+ };
9625
+ } catch {
9626
+ }
9627
+ const agentIdentifier = process.env.CLEO_AGENT_ID ?? process.env.CLAUDE_CODE_AGENT_ID ?? void 0;
9628
+ const duration = Math.floor(
9629
+ (Date.now() - new Date(activeSession.startedAt).getTime()) / 1e3
9630
+ );
9631
+ await appendSessionJournalEntry(projectRoot, {
9632
+ schemaVersion: SESSION_JOURNAL_SCHEMA_VERSION2,
9633
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
9634
+ sessionId,
9635
+ eventType: "session_end",
9636
+ agentIdentifier,
9637
+ providerId: activeSession.providerId ?? void 0,
9638
+ duration,
9639
+ tasksCompleted: activeSession.tasksCompleted ?? [],
9640
+ ...doctorSummary !== void 0 ? { doctorSummary } : {}
9641
+ });
9642
+ } catch {
9643
+ }
9131
9644
  return {
9132
9645
  success: true,
9133
9646
  data: { sessionId, ended: true, ...memoryPrompt && { memoryPrompt } }
@@ -18539,19 +19052,141 @@ var init_memory2 = __esm({
18539
19052
  const { scanBrainNoise } = await import("@cleocode/core/memory/brain-doctor.js");
18540
19053
  const result = await scanBrainNoise(projectRoot);
18541
19054
  const assertClean = params?.["assert-clean"];
18542
- if (assertClean && !result.isClean) {
18543
- return errorResult(
19055
+ if (assertClean) {
19056
+ let pendingCandidates = 0;
19057
+ try {
19058
+ const { getBrainNativeDb: _getDoctorNativeDb } = await import("@cleocode/core/store/memory-sqlite.js");
19059
+ const doctorNativeDb = _getDoctorNativeDb();
19060
+ if (doctorNativeDb) {
19061
+ const tableExists = doctorNativeDb.prepare(
19062
+ `SELECT name FROM sqlite_master WHERE type='table' AND name='brain_v2_candidate'`
19063
+ ).get();
19064
+ if (tableExists) {
19065
+ const countRow = doctorNativeDb.prepare(
19066
+ `SELECT COUNT(*) AS cnt FROM brain_v2_candidate WHERE validation_status = 'pending'`
19067
+ ).get();
19068
+ pendingCandidates = countRow?.cnt ?? 0;
19069
+ }
19070
+ }
19071
+ } catch {
19072
+ }
19073
+ if (!result.isClean) {
19074
+ return errorResult(
19075
+ "query",
19076
+ "memory",
19077
+ operation,
19078
+ "E_BRAIN_NOISE_DETECTED",
19079
+ `Brain noise detected: ${result.findings.length} pattern(s) across ${result.totalScanned} entries. ` + result.findings.map((f) => `${f.pattern}(${f.count})`).join(", ") + ". Run `cleo memory doctor` for details. Fix noise before enabling Sentient v1 (M7 gate).",
19080
+ startTime
19081
+ );
19082
+ }
19083
+ if (pendingCandidates > 0) {
19084
+ return errorResult(
19085
+ "query",
19086
+ "memory",
19087
+ operation,
19088
+ "E_SWEEP_PENDING",
19089
+ `Staged sweep has ${pendingCandidates} pending brain_v2_candidate rows. Run \`cleo memory sweep --status\` to review, then \`cleo memory sweep --approve <runId>\` to apply.`,
19090
+ startTime
19091
+ );
19092
+ }
19093
+ } else if (!result.isClean) {
19094
+ }
19095
+ return wrapResult(
19096
+ { success: true, data: { ...result, pendingCandidates: 0 } },
19097
+ "query",
19098
+ "memory",
19099
+ operation,
19100
+ startTime
19101
+ );
19102
+ }
19103
+ // T1147 W7 — BRAIN noise sweep (shadow-write envelope, self-healing gate)
19104
+ case "sweep": {
19105
+ const dryRun = params?.["dry-run"] === true || params?.dryRun === true;
19106
+ const approveRunId = params?.approve;
19107
+ const statusQuery = params?.status === true;
19108
+ const rollbackRunId = params?.rollback;
19109
+ if (statusQuery) {
19110
+ const { getBrainDb: _getBrainDb2, getBrainNativeDb: _getNativeDb2 } = await import("@cleocode/core/store/memory-sqlite.js");
19111
+ const sweepDb = await _getBrainDb2(projectRoot);
19112
+ const nativeDb2 = _getNativeDb2();
19113
+ if (!nativeDb2) {
19114
+ return errorResult(
19115
+ "query",
19116
+ "memory",
19117
+ operation,
19118
+ "E_DB_UNAVAILABLE",
19119
+ "brain.db not available",
19120
+ startTime
19121
+ );
19122
+ }
19123
+ const rows = nativeDb2.prepare(
19124
+ `SELECT id, status, rows_affected, created_at, approved_at, approved_by
19125
+ FROM brain_backfill_runs
19126
+ WHERE kind = 'noise-sweep-2440'
19127
+ ORDER BY created_at DESC
19128
+ LIMIT 20`
19129
+ ).all();
19130
+ void sweepDb;
19131
+ return wrapResult(
19132
+ { success: true, data: { runs: rows } },
18544
19133
  "query",
18545
19134
  "memory",
18546
19135
  operation,
18547
- "E_BRAIN_NOISE_DETECTED",
18548
- `Brain noise detected: ${result.findings.length} pattern(s) across ${result.totalScanned} entries. ` + result.findings.map((f) => `${f.pattern}(${f.count})`).join(", ") + ". Run `cleo memory doctor` for details. Fix noise before enabling Sentient v1 (M7 gate).",
18549
19136
  startTime
18550
19137
  );
18551
19138
  }
19139
+ if (approveRunId) {
19140
+ const { executeSweep } = await import("@cleocode/core/memory/brain-sweep-executor.js");
19141
+ const result = await executeSweep({
19142
+ projectRoot,
19143
+ runId: approveRunId,
19144
+ approvedBy: "cleo-cli"
19145
+ });
19146
+ if (!result.success) {
19147
+ return errorResult(
19148
+ "mutate",
19149
+ "memory",
19150
+ operation,
19151
+ "E_SWEEP_FAILED",
19152
+ result.errorMessage ?? "Sweep failed",
19153
+ startTime
19154
+ );
19155
+ }
19156
+ return wrapResult(
19157
+ { success: true, data: result },
19158
+ "mutate",
19159
+ "memory",
19160
+ operation,
19161
+ startTime
19162
+ );
19163
+ }
19164
+ if (rollbackRunId) {
19165
+ const { rollbackSweep } = await import("@cleocode/core/memory/brain-sweep-executor.js");
19166
+ const rolled = await rollbackSweep(projectRoot, rollbackRunId);
19167
+ if (!rolled) {
19168
+ return errorResult(
19169
+ "mutate",
19170
+ "memory",
19171
+ operation,
19172
+ "E_SWEEP_ROLLBACK_FAILED",
19173
+ `Run '${rollbackRunId}' not found or not in staged status`,
19174
+ startTime
19175
+ );
19176
+ }
19177
+ return wrapResult(
19178
+ { success: true, data: { rolledBack: rollbackRunId } },
19179
+ "mutate",
19180
+ "memory",
19181
+ operation,
19182
+ startTime
19183
+ );
19184
+ }
19185
+ const { detectNoiseCandidates } = await import("@cleocode/core/memory/brain-noise-detector.js");
19186
+ const detectResult = await detectNoiseCandidates(projectRoot, { dryRun });
18552
19187
  return wrapResult(
18553
- { success: true, data: result },
18554
- "query",
19188
+ { success: true, data: detectResult },
19189
+ dryRun ? "query" : "mutate",
18555
19190
  "memory",
18556
19191
  operation,
18557
19192
  startTime
@@ -19655,8 +20290,10 @@ var init_memory2 = __esm({
19655
20290
  "code.links",
19656
20291
  "code.memories-for-code",
19657
20292
  "code.for-memory",
19658
- // T1262 — brain noise detector (E1-parallel, read-only)
20293
+ // T1262 — brain noise detector (E1-parallel, read-only) + assert-clean gate
19659
20294
  "doctor",
20295
+ // T1147 W7 — sweep status query (dry-run mode)
20296
+ "sweep",
19660
20297
  // T791 — LLM extraction backend status
19661
20298
  "llm-status",
19662
20299
  // T792 — pending verification queue
@@ -25983,8 +26620,8 @@ var init_runtime = __esm({
25983
26620
  });
25984
26621
 
25985
26622
  // packages/playbooks/src/index.ts
25986
- var src_exports = {};
25987
- __export(src_exports, {
26623
+ var src_exports2 = {};
26624
+ __export(src_exports2, {
25988
26625
  DEFAULT_POLICY_RULES: () => DEFAULT_POLICY_RULES,
25989
26626
  E_APPROVAL_ALREADY_DECIDED: () => E_APPROVAL_ALREADY_DECIDED,
25990
26627
  E_APPROVAL_NOT_FOUND: () => E_APPROVAL_NOT_FOUND,
@@ -26937,7 +27574,7 @@ async function handleApproveGate(params, startTime) {
26937
27574
  );
26938
27575
  }
26939
27576
  const db = await acquirePlaybookDb();
26940
- const { approveGate: approveGate2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
27577
+ const { approveGate: approveGate2 } = await Promise.resolve().then(() => (init_src2(), src_exports2));
26941
27578
  const updated = approveGate2(db, resumeToken, approver, reason);
26942
27579
  return {
26943
27580
  meta: dispatchMeta("mutate", "orchestrate", "approve", startTime),
@@ -27027,7 +27664,7 @@ async function handleRejectGate(params, startTime) {
27027
27664
  );
27028
27665
  }
27029
27666
  const db = await acquirePlaybookDb();
27030
- const { rejectGate: rejectGate2 } = await Promise.resolve().then(() => (init_src2(), src_exports));
27667
+ const { rejectGate: rejectGate2 } = await Promise.resolve().then(() => (init_src2(), src_exports2));
27031
27668
  const updated = rejectGate2(db, resumeToken, approver, reason);
27032
27669
  return {
27033
27670
  meta: dispatchMeta("mutate", "orchestrate", "reject", startTime),