@cleocode/core 2026.3.72 → 2026.3.74

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 (56) hide show
  1. package/dist/cleo.d.ts.map +1 -1
  2. package/dist/hooks/handlers/agent-hooks.d.ts +48 -0
  3. package/dist/hooks/handlers/agent-hooks.d.ts.map +1 -0
  4. package/dist/hooks/handlers/context-hooks.d.ts +53 -0
  5. package/dist/hooks/handlers/context-hooks.d.ts.map +1 -0
  6. package/dist/hooks/handlers/error-hooks.d.ts +4 -4
  7. package/dist/hooks/handlers/error-hooks.d.ts.map +1 -1
  8. package/dist/hooks/handlers/file-hooks.d.ts +3 -3
  9. package/dist/hooks/handlers/file-hooks.d.ts.map +1 -1
  10. package/dist/hooks/handlers/index.d.ts +8 -1
  11. package/dist/hooks/handlers/index.d.ts.map +1 -1
  12. package/dist/hooks/handlers/mcp-hooks.d.ts +29 -7
  13. package/dist/hooks/handlers/mcp-hooks.d.ts.map +1 -1
  14. package/dist/hooks/handlers/session-hooks.d.ts +5 -5
  15. package/dist/hooks/handlers/session-hooks.d.ts.map +1 -1
  16. package/dist/hooks/handlers/task-hooks.d.ts +5 -5
  17. package/dist/hooks/handlers/task-hooks.d.ts.map +1 -1
  18. package/dist/hooks/handlers/work-capture-hooks.d.ts +7 -7
  19. package/dist/hooks/handlers/work-capture-hooks.d.ts.map +1 -1
  20. package/dist/hooks/payload-schemas.d.ts +177 -11
  21. package/dist/hooks/payload-schemas.d.ts.map +1 -1
  22. package/dist/hooks/provider-hooks.d.ts +33 -7
  23. package/dist/hooks/provider-hooks.d.ts.map +1 -1
  24. package/dist/hooks/registry.d.ts +26 -6
  25. package/dist/hooks/registry.d.ts.map +1 -1
  26. package/dist/hooks/types.d.ts +132 -38
  27. package/dist/hooks/types.d.ts.map +1 -1
  28. package/dist/index.js +818 -233
  29. package/dist/index.js.map +4 -4
  30. package/dist/nexus/index.d.ts +2 -0
  31. package/dist/nexus/index.d.ts.map +1 -1
  32. package/dist/nexus/workspace.d.ts +128 -0
  33. package/dist/nexus/workspace.d.ts.map +1 -0
  34. package/dist/sessions/snapshot.d.ts.map +1 -1
  35. package/package.json +6 -6
  36. package/src/cleo.ts +14 -0
  37. package/src/hooks/handlers/__tests__/hook-automation-e2e.test.ts +634 -0
  38. package/src/hooks/handlers/agent-hooks.ts +148 -0
  39. package/src/hooks/handlers/context-hooks.ts +156 -0
  40. package/src/hooks/handlers/error-hooks.ts +8 -5
  41. package/src/hooks/handlers/file-hooks.ts +6 -4
  42. package/src/hooks/handlers/index.ts +12 -1
  43. package/src/hooks/handlers/mcp-hooks.ts +74 -9
  44. package/src/hooks/handlers/session-hooks.ts +7 -7
  45. package/src/hooks/handlers/task-hooks.ts +7 -7
  46. package/src/hooks/handlers/work-capture-hooks.ts +12 -12
  47. package/src/hooks/payload-schemas.ts +96 -26
  48. package/src/hooks/provider-hooks.ts +50 -9
  49. package/src/hooks/registry.ts +86 -23
  50. package/src/hooks/types.ts +175 -39
  51. package/src/nexus/index.ts +15 -0
  52. package/src/nexus/workspace.ts +508 -0
  53. package/src/sessions/index.ts +4 -4
  54. package/src/sessions/snapshot.ts +4 -2
  55. package/src/store/json.ts +2 -2
  56. package/src/task-work/index.ts +4 -4
package/dist/index.js CHANGED
@@ -631,6 +631,13 @@ var init_discovery = __esm({
631
631
  });
632
632
 
633
633
  // packages/core/src/logger.ts
634
+ var logger_exports = {};
635
+ __export(logger_exports, {
636
+ closeLogger: () => closeLogger,
637
+ getLogDir: () => getLogDir,
638
+ getLogger: () => getLogger,
639
+ initLogger: () => initLogger
640
+ });
634
641
  import { existsSync as existsSync2 } from "node:fs";
635
642
  import { dirname, join as join3 } from "node:path";
636
643
  import pino from "pino";
@@ -732,22 +739,42 @@ __export(registry_exports, {
732
739
  HookRegistry: () => HookRegistry,
733
740
  hooks: () => hooks
734
741
  });
735
- var DEFAULT_HOOK_CONFIG, HookRegistry, hooks;
742
+ var LEGACY_EVENT_MAP, DEFAULT_HOOK_CONFIG, HookRegistry, hooks;
736
743
  var init_registry = __esm({
737
744
  "packages/core/src/hooks/registry.ts"() {
738
745
  "use strict";
739
746
  init_logger();
747
+ LEGACY_EVENT_MAP = {
748
+ onSessionStart: "SessionStart",
749
+ onSessionEnd: "SessionEnd",
750
+ onToolStart: "PreToolUse",
751
+ onToolComplete: "PostToolUse",
752
+ onFileChange: "Notification",
753
+ onError: "PostToolUseFailure",
754
+ onPromptSubmit: "PromptSubmit",
755
+ onResponseComplete: "ResponseComplete"
756
+ };
740
757
  DEFAULT_HOOK_CONFIG = {
741
758
  enabled: true,
742
759
  events: {
743
- onSessionStart: true,
744
- onSessionEnd: true,
745
- onToolStart: true,
746
- onToolComplete: true,
747
- onFileChange: true,
748
- onError: true,
749
- onPromptSubmit: true,
750
- onResponseComplete: true,
760
+ // CAAMP canonical events (16)
761
+ SessionStart: true,
762
+ SessionEnd: true,
763
+ PromptSubmit: true,
764
+ ResponseComplete: true,
765
+ PreToolUse: true,
766
+ PostToolUse: true,
767
+ PostToolUseFailure: true,
768
+ PermissionRequest: true,
769
+ SubagentStart: true,
770
+ SubagentStop: true,
771
+ PreModel: true,
772
+ PostModel: true,
773
+ PreCompact: true,
774
+ PostCompact: true,
775
+ Notification: true,
776
+ ConfigChange: true,
777
+ // CLEO internal coordination events (5)
751
778
  onWorkAvailable: true,
752
779
  onAgentSpawn: true,
753
780
  onAgentComplete: true,
@@ -758,12 +785,33 @@ var init_registry = __esm({
758
785
  HookRegistry = class {
759
786
  handlers = /* @__PURE__ */ new Map();
760
787
  config = DEFAULT_HOOK_CONFIG;
788
+ /**
789
+ * Resolve a potentially-legacy event name to its canonical equivalent.
790
+ *
791
+ * If the event name matches a known legacy `on`-prefix name, it is
792
+ * remapped and a deprecation warning is logged. Unknown names pass through
793
+ * unchanged so callers using the new canonical names are unaffected.
794
+ */
795
+ resolveEvent(event) {
796
+ const canonical = LEGACY_EVENT_MAP[event];
797
+ if (canonical) {
798
+ getLogger("hooks").warn(
799
+ { legacyEvent: event, canonicalEvent: canonical },
800
+ `[DEPRECATED] Hook event '${event}' has been renamed to '${canonical}'. Update your handler registration.`
801
+ );
802
+ return canonical;
803
+ }
804
+ return event;
805
+ }
761
806
  /**
762
807
  * Register a hook handler for a specific event.
763
808
  *
764
809
  * Handlers are sorted by priority (highest first) and executed
765
810
  * in parallel when the event is dispatched.
766
811
  *
812
+ * Backward compatibility: legacy `on`-prefix event names are automatically
813
+ * remapped to their canonical equivalents.
814
+ *
767
815
  * @param registration - The hook registration containing event, handler, priority, and ID
768
816
  * @returns A function to unregister the handler
769
817
  *
@@ -771,7 +819,7 @@ var init_registry = __esm({
771
819
  * ```typescript
772
820
  * const unregister = hooks.register({
773
821
  * id: 'my-handler',
774
- * event: 'onSessionStart',
822
+ * event: 'SessionStart',
775
823
  * handler: async (root, payload) => { console.log('Session started'); },
776
824
  * priority: 100
777
825
  * });
@@ -780,12 +828,14 @@ var init_registry = __esm({
780
828
  * ```
781
829
  */
782
830
  register(registration) {
783
- const list = this.handlers.get(registration.event) || [];
784
- list.push(registration);
831
+ const resolvedEvent = this.resolveEvent(registration.event);
832
+ const resolvedRegistration = { ...registration, event: resolvedEvent };
833
+ const list = this.handlers.get(resolvedEvent) || [];
834
+ list.push(resolvedRegistration);
785
835
  list.sort((a, b) => b.priority - a.priority);
786
- this.handlers.set(registration.event, list);
836
+ this.handlers.set(resolvedEvent, list);
787
837
  return () => {
788
- const handlers = this.handlers.get(registration.event);
838
+ const handlers = this.handlers.get(resolvedEvent);
789
839
  if (handlers) {
790
840
  const idx = handlers.findIndex((h) => h.id === registration.id);
791
841
  if (idx !== -1) handlers.splice(idx, 1);
@@ -799,14 +849,17 @@ var init_registry = __esm({
799
849
  * execution. Errors in individual handlers are logged but do not block
800
850
  * other handlers or propagate to the caller.
801
851
  *
802
- * @param event - The CAAMP hook event to dispatch
852
+ * Backward compatibility: legacy `on`-prefix event names are automatically
853
+ * remapped to their canonical equivalents.
854
+ *
855
+ * @param event - The CAAMP canonical hook event to dispatch
803
856
  * @param projectRoot - The project root directory path
804
857
  * @param payload - The event payload (typed by event)
805
858
  * @returns Promise that resolves when all handlers have completed
806
859
  *
807
860
  * @example
808
861
  * ```typescript
809
- * await hooks.dispatch('onSessionStart', '/project', {
862
+ * await hooks.dispatch('SessionStart', '/project', {
810
863
  * timestamp: new Date().toISOString(),
811
864
  * sessionId: 'sess-123',
812
865
  * name: 'My Session',
@@ -816,15 +869,19 @@ var init_registry = __esm({
816
869
  */
817
870
  async dispatch(event, projectRoot, payload) {
818
871
  if (!this.config.enabled) return;
819
- if (!this.config.events[event]) return;
820
- const handlers = this.handlers.get(event);
872
+ const resolvedEvent = this.resolveEvent(event);
873
+ if (!this.config.events[resolvedEvent]) return;
874
+ const handlers = this.handlers.get(resolvedEvent);
821
875
  if (!handlers || handlers.length === 0) return;
822
876
  await Promise.allSettled(
823
877
  handlers.map(async (reg) => {
824
878
  try {
825
879
  await reg.handler(projectRoot, payload);
826
880
  } catch (error40) {
827
- getLogger("hooks").warn({ err: error40, hookId: reg.id, event }, "Hook handler failed");
881
+ getLogger("hooks").warn(
882
+ { err: error40, hookId: reg.id, event: resolvedEvent },
883
+ "Hook handler failed"
884
+ );
828
885
  }
829
886
  })
830
887
  );
@@ -833,12 +890,14 @@ var init_registry = __esm({
833
890
  * Check if a specific event is currently enabled.
834
891
  *
835
892
  * Both the global enabled flag and the per-event flag must be true.
893
+ * Automatically resolves legacy `on`-prefix event names.
836
894
  *
837
895
  * @param event - The CAAMP hook event to check
838
896
  * @returns True if the event is enabled
839
897
  */
840
898
  isEnabled(event) {
841
- return this.config.enabled && this.config.events[event];
899
+ const resolvedEvent = this.resolveEvent(event);
900
+ return this.config.enabled && this.config.events[resolvedEvent];
842
901
  }
843
902
  /**
844
903
  * Update the hook system configuration.
@@ -850,7 +909,7 @@ var init_registry = __esm({
850
909
  * @example
851
910
  * ```typescript
852
911
  * hooks.setConfig({ enabled: false }); // Disable all hooks
853
- * hooks.setConfig({ events: { onError: false } }); // Disable specific event
912
+ * hooks.setConfig({ events: { PostToolUseFailure: false } }); // Disable specific event
854
913
  * ```
855
914
  */
856
915
  setConfig(config2) {
@@ -868,12 +927,14 @@ var init_registry = __esm({
868
927
  * List all registered handlers for a specific event.
869
928
  *
870
929
  * Returns handlers in priority order (highest first).
930
+ * Automatically resolves legacy `on`-prefix event names.
871
931
  *
872
932
  * @param event - The CAAMP hook event
873
933
  * @returns Array of hook registrations
874
934
  */
875
935
  listHandlers(event) {
876
- return [...this.handlers.get(event) || []];
936
+ const resolvedEvent = this.resolveEvent(event);
937
+ return [...this.handlers.get(resolvedEvent) || []];
877
938
  }
878
939
  };
879
940
  hooks = new HookRegistry();
@@ -13584,7 +13645,7 @@ async function saveJson(filePath, data, options) {
13584
13645
  }
13585
13646
  await atomicWriteJson(filePath, data, { indent: options?.indent });
13586
13647
  Promise.resolve().then(() => (init_registry(), registry_exports)).then(
13587
- ({ hooks: h }) => h.dispatch("onFileChange", process.cwd(), {
13648
+ ({ hooks: h }) => h.dispatch("Notification", process.cwd(), {
13588
13649
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
13589
13650
  filePath,
13590
13651
  changeType: "write"
@@ -18379,13 +18440,13 @@ var init_session_hooks = __esm({
18379
18440
  init_memory_bridge_refresh();
18380
18441
  hooks.register({
18381
18442
  id: "brain-session-start",
18382
- event: "onSessionStart",
18443
+ event: "SessionStart",
18383
18444
  handler: handleSessionStart,
18384
18445
  priority: 100
18385
18446
  });
18386
18447
  hooks.register({
18387
18448
  id: "brain-session-end",
18388
- event: "onSessionEnd",
18449
+ event: "SessionEnd",
18389
18450
  handler: handleSessionEnd,
18390
18451
  priority: 100
18391
18452
  });
@@ -18432,13 +18493,13 @@ var init_task_hooks = __esm({
18432
18493
  init_memory_bridge_refresh();
18433
18494
  hooks.register({
18434
18495
  id: "brain-tool-start",
18435
- event: "onToolStart",
18496
+ event: "PreToolUse",
18436
18497
  handler: handleToolStart,
18437
18498
  priority: 100
18438
18499
  });
18439
18500
  hooks.register({
18440
18501
  id: "brain-tool-complete",
18441
- event: "onToolComplete",
18502
+ event: "PostToolUse",
18442
18503
  handler: handleToolComplete,
18443
18504
  priority: 100
18444
18505
  });
@@ -18468,7 +18529,7 @@ var init_error_hooks = __esm({
18468
18529
  init_registry();
18469
18530
  hooks.register({
18470
18531
  id: "brain-error",
18471
- event: "onError",
18532
+ event: "PostToolUseFailure",
18472
18533
  handler: handleError,
18473
18534
  priority: 100
18474
18535
  });
@@ -18499,6 +18560,7 @@ async function isFileCaptureEnabled(projectRoot) {
18499
18560
  }
18500
18561
  }
18501
18562
  async function handleFileChange(projectRoot, payload) {
18563
+ if (!payload.filePath || !payload.changeType) return;
18502
18564
  if (!await isFileCaptureEnabled(projectRoot)) return;
18503
18565
  const now = Date.now();
18504
18566
  const lastWrite = recentWrites.get(payload.filePath);
@@ -18545,7 +18607,7 @@ var init_file_hooks = __esm({
18545
18607
  ];
18546
18608
  hooks.register({
18547
18609
  id: "brain-file-change",
18548
- event: "onFileChange",
18610
+ event: "Notification",
18549
18611
  handler: handleFileChange,
18550
18612
  priority: 100
18551
18613
  });
@@ -18599,22 +18661,51 @@ async function handleResponseComplete(projectRoot, payload) {
18599
18661
  if (!isMissingBrainSchemaError4(err)) throw err;
18600
18662
  }
18601
18663
  }
18664
+ async function handleSystemNotification(projectRoot, payload) {
18665
+ if (payload.filePath || payload.changeType) return;
18666
+ if (!payload.message) return;
18667
+ try {
18668
+ const { loadConfig: loadConfig3 } = await Promise.resolve().then(() => (init_config(), config_exports));
18669
+ const config2 = await loadConfig3(projectRoot);
18670
+ if (!config2.brain?.autoCapture) return;
18671
+ } catch {
18672
+ return;
18673
+ }
18674
+ const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
18675
+ try {
18676
+ await observeBrain2(projectRoot, {
18677
+ text: `System notification: ${payload.message}`,
18678
+ title: `Notification: ${payload.message.slice(0, 60)}`,
18679
+ type: "discovery",
18680
+ sourceSessionId: payload.sessionId,
18681
+ sourceType: "agent"
18682
+ });
18683
+ } catch (err) {
18684
+ if (!isMissingBrainSchemaError4(err)) throw err;
18685
+ }
18686
+ }
18602
18687
  var init_mcp_hooks = __esm({
18603
18688
  "packages/core/src/hooks/handlers/mcp-hooks.ts"() {
18604
18689
  "use strict";
18605
18690
  init_registry();
18606
18691
  hooks.register({
18607
18692
  id: "brain-prompt-submit",
18608
- event: "onPromptSubmit",
18693
+ event: "PromptSubmit",
18609
18694
  handler: handlePromptSubmit,
18610
18695
  priority: 100
18611
18696
  });
18612
18697
  hooks.register({
18613
18698
  id: "brain-response-complete",
18614
- event: "onResponseComplete",
18699
+ event: "ResponseComplete",
18615
18700
  handler: handleResponseComplete,
18616
18701
  priority: 100
18617
18702
  });
18703
+ hooks.register({
18704
+ id: "brain-system-notification",
18705
+ event: "Notification",
18706
+ handler: handleSystemNotification,
18707
+ priority: 90
18708
+ });
18618
18709
  }
18619
18710
  });
18620
18711
 
@@ -18691,19 +18782,158 @@ var init_work_capture_hooks = __esm({
18691
18782
  ]);
18692
18783
  hooks.register({
18693
18784
  id: "work-capture-prompt-submit",
18694
- event: "onPromptSubmit",
18785
+ event: "PromptSubmit",
18695
18786
  handler: handleWorkPromptSubmit,
18696
18787
  priority: 90
18697
18788
  });
18698
18789
  hooks.register({
18699
18790
  id: "work-capture-response-complete",
18700
- event: "onResponseComplete",
18791
+ event: "ResponseComplete",
18701
18792
  handler: handleWorkResponseComplete,
18702
18793
  priority: 90
18703
18794
  });
18704
18795
  }
18705
18796
  });
18706
18797
 
18798
+ // packages/core/src/hooks/handlers/agent-hooks.ts
18799
+ function isMissingBrainSchemaError6(err) {
18800
+ if (!(err instanceof Error)) return false;
18801
+ const message = String(err.message || "").toLowerCase();
18802
+ return message.includes("no such table") && message.includes("brain_");
18803
+ }
18804
+ async function isAutoCaptureEnabled(projectRoot) {
18805
+ try {
18806
+ const { loadConfig: loadConfig3 } = await Promise.resolve().then(() => (init_config(), config_exports));
18807
+ const config2 = await loadConfig3(projectRoot);
18808
+ return config2.brain?.autoCapture ?? false;
18809
+ } catch {
18810
+ return false;
18811
+ }
18812
+ }
18813
+ async function handleSubagentStart(projectRoot, payload) {
18814
+ if (!await isAutoCaptureEnabled(projectRoot)) return;
18815
+ const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
18816
+ const rolePart = payload.role ? ` role=${payload.role}` : "";
18817
+ const taskPart = payload.taskId ? ` task=${payload.taskId}` : "";
18818
+ try {
18819
+ await observeBrain2(projectRoot, {
18820
+ text: `Subagent spawned: ${payload.agentId}${rolePart}${taskPart}`,
18821
+ title: `Subagent start: ${payload.agentId}`,
18822
+ type: "discovery",
18823
+ sourceSessionId: payload.sessionId,
18824
+ sourceType: "agent"
18825
+ });
18826
+ } catch (err) {
18827
+ if (!isMissingBrainSchemaError6(err)) throw err;
18828
+ }
18829
+ }
18830
+ async function handleSubagentStop(projectRoot, payload) {
18831
+ if (!await isAutoCaptureEnabled(projectRoot)) return;
18832
+ const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
18833
+ const statusPart = payload.status ? ` status=${payload.status}` : "";
18834
+ const taskPart = payload.taskId ? ` task=${payload.taskId}` : "";
18835
+ const summaryPart = payload.summary ? `
18836
+ Summary: ${payload.summary}` : "";
18837
+ try {
18838
+ await observeBrain2(projectRoot, {
18839
+ text: `Subagent completed: ${payload.agentId}${statusPart}${taskPart}${summaryPart}`,
18840
+ title: `Subagent stop: ${payload.agentId}`,
18841
+ type: "change",
18842
+ sourceSessionId: payload.sessionId,
18843
+ sourceType: "agent"
18844
+ });
18845
+ } catch (err) {
18846
+ if (!isMissingBrainSchemaError6(err)) throw err;
18847
+ }
18848
+ }
18849
+ var init_agent_hooks = __esm({
18850
+ "packages/core/src/hooks/handlers/agent-hooks.ts"() {
18851
+ "use strict";
18852
+ init_registry();
18853
+ hooks.register({
18854
+ id: "brain-subagent-start",
18855
+ event: "SubagentStart",
18856
+ handler: handleSubagentStart,
18857
+ priority: 100
18858
+ });
18859
+ hooks.register({
18860
+ id: "brain-subagent-stop",
18861
+ event: "SubagentStop",
18862
+ handler: handleSubagentStop,
18863
+ priority: 100
18864
+ });
18865
+ }
18866
+ });
18867
+
18868
+ // packages/core/src/hooks/handlers/context-hooks.ts
18869
+ function isMissingBrainSchemaError7(err) {
18870
+ if (!(err instanceof Error)) return false;
18871
+ const message = String(err.message || "").toLowerCase();
18872
+ return message.includes("no such table") && message.includes("brain_");
18873
+ }
18874
+ async function isAutoCaptureEnabled2(projectRoot) {
18875
+ try {
18876
+ const { loadConfig: loadConfig3 } = await Promise.resolve().then(() => (init_config(), config_exports));
18877
+ const config2 = await loadConfig3(projectRoot);
18878
+ return config2.brain?.autoCapture ?? false;
18879
+ } catch {
18880
+ return false;
18881
+ }
18882
+ }
18883
+ async function handlePreCompact(projectRoot, payload) {
18884
+ if (!await isAutoCaptureEnabled2(projectRoot)) return;
18885
+ const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
18886
+ const tokensPart = payload.tokensBefore != null ? ` (~${payload.tokensBefore.toLocaleString()} tokens)` : "";
18887
+ const reasonPart = payload.reason ? ` Reason: ${payload.reason}` : "";
18888
+ try {
18889
+ await observeBrain2(projectRoot, {
18890
+ text: `Context compaction about to begin${tokensPart}.${reasonPart}`,
18891
+ title: "Pre-compaction context snapshot",
18892
+ type: "discovery",
18893
+ sourceSessionId: payload.sessionId,
18894
+ sourceType: "agent"
18895
+ });
18896
+ } catch (err) {
18897
+ if (!isMissingBrainSchemaError7(err)) throw err;
18898
+ }
18899
+ }
18900
+ async function handlePostCompact(projectRoot, payload) {
18901
+ if (!await isAutoCaptureEnabled2(projectRoot)) return;
18902
+ const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
18903
+ const statusPart = payload.success ? "succeeded" : "failed";
18904
+ const beforePart = payload.tokensBefore != null ? ` before=${payload.tokensBefore.toLocaleString()}` : "";
18905
+ const afterPart = payload.tokensAfter != null ? ` after=${payload.tokensAfter.toLocaleString()}` : "";
18906
+ try {
18907
+ await observeBrain2(projectRoot, {
18908
+ text: `Context compaction ${statusPart}${beforePart}${afterPart}`,
18909
+ title: "Post-compaction record",
18910
+ type: "change",
18911
+ sourceSessionId: payload.sessionId,
18912
+ sourceType: "agent"
18913
+ });
18914
+ } catch (err) {
18915
+ if (!isMissingBrainSchemaError7(err)) throw err;
18916
+ }
18917
+ }
18918
+ var init_context_hooks = __esm({
18919
+ "packages/core/src/hooks/handlers/context-hooks.ts"() {
18920
+ "use strict";
18921
+ init_registry();
18922
+ hooks.register({
18923
+ id: "brain-pre-compact",
18924
+ event: "PreCompact",
18925
+ handler: handlePreCompact,
18926
+ priority: 100
18927
+ });
18928
+ hooks.register({
18929
+ id: "brain-post-compact",
18930
+ event: "PostCompact",
18931
+ handler: handlePostCompact,
18932
+ priority: 100
18933
+ });
18934
+ }
18935
+ });
18936
+
18707
18937
  // packages/core/src/hooks/handlers/index.ts
18708
18938
  var init_handlers = __esm({
18709
18939
  "packages/core/src/hooks/handlers/index.ts"() {
@@ -18714,6 +18944,10 @@ var init_handlers = __esm({
18714
18944
  init_file_hooks();
18715
18945
  init_mcp_hooks();
18716
18946
  init_work_capture_hooks();
18947
+ init_agent_hooks();
18948
+ init_context_hooks();
18949
+ init_agent_hooks();
18950
+ init_context_hooks();
18717
18951
  init_error_hooks();
18718
18952
  init_file_hooks();
18719
18953
  init_mcp_hooks();
@@ -20564,7 +20798,7 @@ async function startSession(options, cwd, accessor) {
20564
20798
  });
20565
20799
  }
20566
20800
  const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
20567
- hooks2.dispatch("onSessionStart", cwd ?? process.cwd(), {
20801
+ hooks2.dispatch("SessionStart", cwd ?? process.cwd(), {
20568
20802
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
20569
20803
  sessionId: session.id,
20570
20804
  name: options.name,
@@ -20602,7 +20836,7 @@ async function endSession(options = {}, cwd, accessor) {
20602
20836
  session.endedAt = (/* @__PURE__ */ new Date()).toISOString();
20603
20837
  const duration3 = Math.floor((Date.now() - new Date(session.startedAt).getTime()) / 1e3);
20604
20838
  const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
20605
- hooks2.dispatch("onSessionEnd", cwd ?? process.cwd(), {
20839
+ hooks2.dispatch("SessionEnd", cwd ?? process.cwd(), {
20606
20840
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
20607
20841
  sessionId: session.id,
20608
20842
  duration: duration3,
@@ -21285,6 +21519,54 @@ var init_codebase_map = __esm({
21285
21519
  }
21286
21520
  });
21287
21521
 
21522
+ // packages/core/src/tasks/dependency-check.ts
21523
+ function getUnresolvedDeps(taskId, tasks2) {
21524
+ const task = tasks2.find((t) => t.id === taskId);
21525
+ if (!task?.depends?.length) return [];
21526
+ const completedIds = new Set(
21527
+ tasks2.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
21528
+ );
21529
+ return task.depends.filter((depId) => !completedIds.has(depId));
21530
+ }
21531
+ function topologicalSort2(tasks2) {
21532
+ const inDegree = /* @__PURE__ */ new Map();
21533
+ const adjList = /* @__PURE__ */ new Map();
21534
+ for (const task of tasks2) {
21535
+ inDegree.set(task.id, 0);
21536
+ adjList.set(task.id, []);
21537
+ }
21538
+ for (const task of tasks2) {
21539
+ if (!task.depends?.length) continue;
21540
+ for (const depId of task.depends) {
21541
+ if (adjList.has(depId)) {
21542
+ adjList.get(depId).push(task.id);
21543
+ inDegree.set(task.id, (inDegree.get(task.id) ?? 0) + 1);
21544
+ }
21545
+ }
21546
+ }
21547
+ const queue = [];
21548
+ for (const [id, degree] of inDegree) {
21549
+ if (degree === 0) queue.push(id);
21550
+ }
21551
+ const sorted = [];
21552
+ while (queue.length > 0) {
21553
+ const id = queue.shift();
21554
+ sorted.push(id);
21555
+ for (const dependent of adjList.get(id) ?? []) {
21556
+ const newDegree = (inDegree.get(dependent) ?? 1) - 1;
21557
+ inDegree.set(dependent, newDegree);
21558
+ if (newDegree === 0) queue.push(dependent);
21559
+ }
21560
+ }
21561
+ if (sorted.length !== tasks2.length) return null;
21562
+ return sorted;
21563
+ }
21564
+ var init_dependency_check = __esm({
21565
+ "packages/core/src/tasks/dependency-check.ts"() {
21566
+ "use strict";
21567
+ }
21568
+ });
21569
+
21288
21570
  // packages/core/src/tasks/hierarchy.ts
21289
21571
  function getParentChain(taskId, tasks2) {
21290
21572
  const chain = [];
@@ -24731,7 +25013,150 @@ var init_registry3 = __esm({
24731
25013
  }
24732
25014
  });
24733
25015
 
25016
+ // packages/core/src/task-work/index.ts
25017
+ var task_work_exports = {};
25018
+ __export(task_work_exports, {
25019
+ currentTask: () => currentTask,
25020
+ getTaskHistory: () => getTaskHistory,
25021
+ getWorkHistory: () => getWorkHistory,
25022
+ startTask: () => startTask,
25023
+ stopTask: () => stopTask
25024
+ });
25025
+ async function currentTask(cwd, accessor) {
25026
+ const acc = accessor ?? await getAccessor(cwd);
25027
+ const focus = await acc.getMetaValue("focus_state");
25028
+ return {
25029
+ currentTask: focus?.currentTask ?? null,
25030
+ currentPhase: focus?.currentPhase ?? null,
25031
+ sessionNote: focus?.sessionNote ?? null,
25032
+ nextAction: focus?.nextAction ?? null
25033
+ };
25034
+ }
25035
+ async function startTask(taskId, cwd, accessor) {
25036
+ if (!taskId) {
25037
+ throw new CleoError(2 /* INVALID_INPUT */, "Task ID is required");
25038
+ }
25039
+ const acc = accessor ?? await getAccessor(cwd);
25040
+ const task = await acc.loadSingleTask(taskId);
25041
+ if (!task) {
25042
+ throw new CleoError(4 /* NOT_FOUND */, `Task not found: ${taskId}`, {
25043
+ fix: `Use 'cleo find "${taskId}"' to search`
25044
+ });
25045
+ }
25046
+ const { tasks: allTasks } = await acc.queryTasks({});
25047
+ const unresolvedDeps = getUnresolvedDeps(taskId, allTasks);
25048
+ if (unresolvedDeps.length > 0) {
25049
+ throw new CleoError(
25050
+ 5 /* DEPENDENCY_ERROR */,
25051
+ `Task ${taskId} is blocked by unresolved dependencies: ${unresolvedDeps.join(", ")}`,
25052
+ {
25053
+ fix: `Complete blockers first: ${unresolvedDeps.map((d) => `cleo complete ${d}`).join(", ")}`
25054
+ }
25055
+ );
25056
+ }
25057
+ const focus = await acc.getMetaValue("focus_state") ?? {};
25058
+ const previousTask = focus.currentTask ?? null;
25059
+ focus.currentTask = taskId;
25060
+ focus.currentPhase = task.phase ?? null;
25061
+ const noteEntry = {
25062
+ note: `Started work on ${taskId}: ${task.title}`,
25063
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
25064
+ };
25065
+ if (!focus.sessionNotes) {
25066
+ focus.sessionNotes = [];
25067
+ }
25068
+ focus.sessionNotes.push(noteEntry);
25069
+ await acc.setMetaValue("focus_state", focus);
25070
+ await logOperation(
25071
+ "task_start",
25072
+ taskId,
25073
+ {
25074
+ previousTask,
25075
+ title: task.title
25076
+ },
25077
+ accessor
25078
+ );
25079
+ const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
25080
+ hooks2.dispatch("PreToolUse", cwd ?? process.cwd(), {
25081
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
25082
+ taskId,
25083
+ taskTitle: task.title
25084
+ }).catch(() => {
25085
+ });
25086
+ return {
25087
+ taskId,
25088
+ taskTitle: task.title,
25089
+ previousTask
25090
+ };
25091
+ }
25092
+ async function stopTask(cwd, accessor) {
25093
+ const acc = accessor ?? await getAccessor(cwd);
25094
+ const focus = await acc.getMetaValue("focus_state");
25095
+ const previousTask = focus?.currentTask ?? null;
25096
+ if (!focus) {
25097
+ return { previousTask: null };
25098
+ }
25099
+ const taskId = focus.currentTask;
25100
+ const task = taskId ? await acc.loadSingleTask(taskId) : void 0;
25101
+ focus.currentTask = null;
25102
+ focus.nextAction = null;
25103
+ const now = (/* @__PURE__ */ new Date()).toISOString();
25104
+ if (taskId && task) {
25105
+ const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
25106
+ hooks2.dispatch("PostToolUse", cwd ?? process.cwd(), {
25107
+ timestamp: now,
25108
+ taskId,
25109
+ taskTitle: task.title,
25110
+ status: "done"
25111
+ }).catch(() => {
25112
+ });
25113
+ }
25114
+ await acc.setMetaValue("focus_state", focus);
25115
+ await logOperation(
25116
+ "task_stop",
25117
+ previousTask ?? "none",
25118
+ {
25119
+ previousTask
25120
+ },
25121
+ accessor
25122
+ );
25123
+ return { previousTask };
25124
+ }
25125
+ async function getWorkHistory(cwd, accessor) {
25126
+ const acc = accessor ?? await getAccessor(cwd);
25127
+ const focus = await acc.getMetaValue("focus_state");
25128
+ const notes = focus?.sessionNotes ?? [];
25129
+ const history = [];
25130
+ for (const note of notes) {
25131
+ const match = note.note.match(/^(?:Focus set to|Started work on) (T\d+)/);
25132
+ if (match) {
25133
+ history.push({
25134
+ taskId: match[1],
25135
+ timestamp: note.timestamp
25136
+ });
25137
+ }
25138
+ }
25139
+ return history.reverse();
25140
+ }
25141
+ var getTaskHistory;
25142
+ var init_task_work = __esm({
25143
+ "packages/core/src/task-work/index.ts"() {
25144
+ "use strict";
25145
+ init_src();
25146
+ init_errors3();
25147
+ init_data_accessor();
25148
+ init_add();
25149
+ init_dependency_check();
25150
+ init_handlers();
25151
+ getTaskHistory = getWorkHistory;
25152
+ }
25153
+ });
25154
+
24734
25155
  // packages/core/src/tasks/complete.ts
25156
+ var complete_exports = {};
25157
+ __export(complete_exports, {
25158
+ completeTask: () => completeTask
25159
+ });
24735
25160
  function isVerificationGate(value) {
24736
25161
  return VERIFICATION_GATES.has(value);
24737
25162
  }
@@ -28502,10 +28927,15 @@ __export(hooks_exports, {
28502
28927
  OnWorkAvailablePayloadSchema: () => OnWorkAvailablePayloadSchema,
28503
28928
  handleError: () => handleError,
28504
28929
  handleFileChange: () => handleFileChange,
28930
+ handlePostCompact: () => handlePostCompact,
28931
+ handlePreCompact: () => handlePreCompact,
28505
28932
  handlePromptSubmit: () => handlePromptSubmit,
28506
28933
  handleResponseComplete: () => handleResponseComplete,
28507
28934
  handleSessionEnd: () => handleSessionEnd,
28508
28935
  handleSessionStart: () => handleSessionStart,
28936
+ handleSubagentStart: () => handleSubagentStart,
28937
+ handleSubagentStop: () => handleSubagentStop,
28938
+ handleSystemNotification: () => handleSystemNotification,
28509
28939
  handleToolComplete: () => handleToolComplete,
28510
28940
  handleToolStart: () => handleToolStart,
28511
28941
  handleWorkPromptSubmit: () => handleWorkPromptSubmit,
@@ -39662,33 +40092,42 @@ var HookPayloadSchema = external_exports.object({
39662
40092
  providerId: external_exports.string().optional(),
39663
40093
  metadata: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
39664
40094
  });
39665
- var OnSessionStartPayloadSchema = HookPayloadSchema.extend({
40095
+ var SessionStartPayloadSchema = HookPayloadSchema.extend({
39666
40096
  sessionId: external_exports.string(),
39667
40097
  name: external_exports.string(),
39668
40098
  scope: external_exports.string(),
39669
40099
  agent: external_exports.string().optional()
39670
40100
  });
39671
- var OnSessionEndPayloadSchema = HookPayloadSchema.extend({
40101
+ var OnSessionStartPayloadSchema = SessionStartPayloadSchema;
40102
+ var SessionEndPayloadSchema = HookPayloadSchema.extend({
39672
40103
  sessionId: external_exports.string(),
39673
40104
  duration: external_exports.number(),
39674
40105
  tasksCompleted: external_exports.array(external_exports.string())
39675
40106
  });
39676
- var OnToolStartPayloadSchema = HookPayloadSchema.extend({
40107
+ var OnSessionEndPayloadSchema = SessionEndPayloadSchema;
40108
+ var PreToolUsePayloadSchema = HookPayloadSchema.extend({
39677
40109
  taskId: external_exports.string(),
39678
40110
  taskTitle: external_exports.string(),
39679
- previousTask: external_exports.string().optional()
40111
+ previousTask: external_exports.string().optional(),
40112
+ toolName: external_exports.string().optional(),
40113
+ toolInput: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
39680
40114
  });
39681
- var OnToolCompletePayloadSchema = HookPayloadSchema.extend({
40115
+ var OnToolStartPayloadSchema = PreToolUsePayloadSchema;
40116
+ var PostToolUsePayloadSchema = HookPayloadSchema.extend({
39682
40117
  taskId: external_exports.string(),
39683
40118
  taskTitle: external_exports.string(),
39684
- status: external_exports.enum(["done", "archived", "cancelled"])
40119
+ status: external_exports.enum(["done", "archived", "cancelled"]),
40120
+ toolResult: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
39685
40121
  });
39686
- var OnFileChangePayloadSchema = HookPayloadSchema.extend({
39687
- filePath: external_exports.string(),
39688
- changeType: external_exports.enum(["write", "create", "delete"]),
39689
- sizeBytes: external_exports.number().optional()
40122
+ var OnToolCompletePayloadSchema = PostToolUsePayloadSchema;
40123
+ var NotificationPayloadSchema = HookPayloadSchema.extend({
40124
+ filePath: external_exports.string().optional(),
40125
+ changeType: external_exports.enum(["write", "create", "delete"]).optional(),
40126
+ sizeBytes: external_exports.number().optional(),
40127
+ message: external_exports.string().optional()
39690
40128
  });
39691
- var OnErrorPayloadSchema = HookPayloadSchema.extend({
40129
+ var OnFileChangePayloadSchema = NotificationPayloadSchema;
40130
+ var PostToolUseFailurePayloadSchema = HookPayloadSchema.extend({
39692
40131
  errorCode: external_exports.union([external_exports.number(), external_exports.string()]),
39693
40132
  message: external_exports.string(),
39694
40133
  domain: external_exports.string().optional(),
@@ -39696,13 +40135,15 @@ var OnErrorPayloadSchema = HookPayloadSchema.extend({
39696
40135
  gateway: external_exports.string().optional(),
39697
40136
  stack: external_exports.string().optional()
39698
40137
  });
39699
- var OnPromptSubmitPayloadSchema = HookPayloadSchema.extend({
40138
+ var OnErrorPayloadSchema = PostToolUseFailurePayloadSchema;
40139
+ var PromptSubmitPayloadSchema = HookPayloadSchema.extend({
39700
40140
  gateway: external_exports.string(),
39701
40141
  domain: external_exports.string(),
39702
40142
  operation: external_exports.string(),
39703
40143
  source: external_exports.string().optional()
39704
40144
  });
39705
- var OnResponseCompletePayloadSchema = HookPayloadSchema.extend({
40145
+ var OnPromptSubmitPayloadSchema = PromptSubmitPayloadSchema;
40146
+ var ResponseCompletePayloadSchema = HookPayloadSchema.extend({
39706
40147
  gateway: external_exports.string(),
39707
40148
  domain: external_exports.string(),
39708
40149
  operation: external_exports.string(),
@@ -39710,6 +40151,32 @@ var OnResponseCompletePayloadSchema = HookPayloadSchema.extend({
39710
40151
  durationMs: external_exports.number().optional(),
39711
40152
  errorCode: external_exports.string().optional()
39712
40153
  });
40154
+ var OnResponseCompletePayloadSchema = ResponseCompletePayloadSchema;
40155
+ var SubagentStartPayloadSchema = HookPayloadSchema.extend({
40156
+ agentId: external_exports.string(),
40157
+ role: external_exports.string().optional(),
40158
+ taskId: external_exports.string().optional()
40159
+ });
40160
+ var SubagentStopPayloadSchema = HookPayloadSchema.extend({
40161
+ agentId: external_exports.string(),
40162
+ status: external_exports.enum(["complete", "partial", "blocked", "failed"]).optional(),
40163
+ taskId: external_exports.string().optional(),
40164
+ summary: external_exports.string().optional()
40165
+ });
40166
+ var PreCompactPayloadSchema = HookPayloadSchema.extend({
40167
+ tokensBefore: external_exports.number().optional(),
40168
+ reason: external_exports.string().optional()
40169
+ });
40170
+ var PostCompactPayloadSchema = HookPayloadSchema.extend({
40171
+ tokensBefore: external_exports.number().optional(),
40172
+ tokensAfter: external_exports.number().optional(),
40173
+ success: external_exports.boolean()
40174
+ });
40175
+ var ConfigChangePayloadSchema = HookPayloadSchema.extend({
40176
+ key: external_exports.string(),
40177
+ previousValue: external_exports.unknown().optional(),
40178
+ newValue: external_exports.unknown().optional()
40179
+ });
39713
40180
  var OnWorkAvailablePayloadSchema = HookPayloadSchema.extend({
39714
40181
  taskIds: external_exports.array(external_exports.string()),
39715
40182
  epicId: external_exports.string().optional(),
@@ -39741,14 +40208,21 @@ var OnPatrolPayloadSchema = HookPayloadSchema.extend({
39741
40208
  scope: external_exports.string().optional()
39742
40209
  });
39743
40210
  var EVENT_SCHEMA_MAP = {
39744
- onSessionStart: OnSessionStartPayloadSchema,
39745
- onSessionEnd: OnSessionEndPayloadSchema,
39746
- onToolStart: OnToolStartPayloadSchema,
39747
- onToolComplete: OnToolCompletePayloadSchema,
39748
- onFileChange: OnFileChangePayloadSchema,
39749
- onError: OnErrorPayloadSchema,
39750
- onPromptSubmit: OnPromptSubmitPayloadSchema,
39751
- onResponseComplete: OnResponseCompletePayloadSchema,
40211
+ // CAAMP canonical events (16)
40212
+ SessionStart: SessionStartPayloadSchema,
40213
+ SessionEnd: SessionEndPayloadSchema,
40214
+ PreToolUse: PreToolUsePayloadSchema,
40215
+ PostToolUse: PostToolUsePayloadSchema,
40216
+ Notification: NotificationPayloadSchema,
40217
+ PostToolUseFailure: PostToolUseFailurePayloadSchema,
40218
+ PromptSubmit: PromptSubmitPayloadSchema,
40219
+ ResponseComplete: ResponseCompletePayloadSchema,
40220
+ SubagentStart: SubagentStartPayloadSchema,
40221
+ SubagentStop: SubagentStopPayloadSchema,
40222
+ PreCompact: PreCompactPayloadSchema,
40223
+ PostCompact: PostCompactPayloadSchema,
40224
+ ConfigChange: ConfigChangePayloadSchema,
40225
+ // CLEO internal coordination events (5)
39752
40226
  onWorkAvailable: OnWorkAvailablePayloadSchema,
39753
40227
  onAgentSpawn: OnAgentSpawnPayloadSchema,
39754
40228
  onAgentComplete: OnAgentCompletePayloadSchema,
@@ -40590,50 +41064,8 @@ async function maybeExtractLearning(taskId, task, verification, confidenceScore,
40590
41064
  // packages/core/src/intelligence/impact.ts
40591
41065
  init_data_accessor();
40592
41066
 
40593
- // packages/core/src/tasks/dependency-check.ts
40594
- function getUnresolvedDeps(taskId, tasks2) {
40595
- const task = tasks2.find((t) => t.id === taskId);
40596
- if (!task?.depends?.length) return [];
40597
- const completedIds = new Set(
40598
- tasks2.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
40599
- );
40600
- return task.depends.filter((depId) => !completedIds.has(depId));
40601
- }
40602
- function topologicalSort2(tasks2) {
40603
- const inDegree = /* @__PURE__ */ new Map();
40604
- const adjList = /* @__PURE__ */ new Map();
40605
- for (const task of tasks2) {
40606
- inDegree.set(task.id, 0);
40607
- adjList.set(task.id, []);
40608
- }
40609
- for (const task of tasks2) {
40610
- if (!task.depends?.length) continue;
40611
- for (const depId of task.depends) {
40612
- if (adjList.has(depId)) {
40613
- adjList.get(depId).push(task.id);
40614
- inDegree.set(task.id, (inDegree.get(task.id) ?? 0) + 1);
40615
- }
40616
- }
40617
- }
40618
- const queue = [];
40619
- for (const [id, degree] of inDegree) {
40620
- if (degree === 0) queue.push(id);
40621
- }
40622
- const sorted = [];
40623
- while (queue.length > 0) {
40624
- const id = queue.shift();
40625
- sorted.push(id);
40626
- for (const dependent of adjList.get(id) ?? []) {
40627
- const newDegree = (inDegree.get(dependent) ?? 1) - 1;
40628
- inDegree.set(dependent, newDegree);
40629
- if (newDegree === 0) queue.push(dependent);
40630
- }
40631
- }
40632
- if (sorted.length !== tasks2.length) return null;
40633
- return sorted;
40634
- }
40635
-
40636
41067
  // packages/core/src/tasks/graph-ops.ts
41068
+ init_dependency_check();
40637
41069
  function getCriticalPath(tasks2) {
40638
41070
  const activeTasks = tasks2.filter((t) => t.status !== "done" && t.status !== "cancelled");
40639
41071
  if (activeTasks.length === 0) return [];
@@ -46510,6 +46942,7 @@ __export(nexus_exports, {
46510
46942
  nexusSyncAll: () => nexusSyncAll,
46511
46943
  nexusUnregister: () => nexusUnregister,
46512
46944
  orphanDetection: () => orphanDetection,
46945
+ parseDirective: () => parseDirective,
46513
46946
  parseQuery: () => parseQuery,
46514
46947
  permissionLevel: () => permissionLevel,
46515
46948
  previewTransfer: () => previewTransfer,
@@ -46520,10 +46953,13 @@ __export(nexus_exports, {
46520
46953
  resolveCrossDeps: () => resolveCrossDeps,
46521
46954
  resolveProjectPath: () => resolveProjectPath2,
46522
46955
  resolveTask: () => resolveTask,
46956
+ routeDirective: () => routeDirective,
46523
46957
  searchAcrossProjects: () => searchAcrossProjects,
46524
46958
  setPermission: () => setPermission,
46525
46959
  syncGitignore: () => syncGitignore,
46526
- validateSyntax: () => validateSyntax
46960
+ validateSyntax: () => validateSyntax,
46961
+ workspaceAgents: () => workspaceAgents,
46962
+ workspaceStatus: () => workspaceStatus
46527
46963
  });
46528
46964
 
46529
46965
  // packages/core/src/nexus/deps.ts
@@ -47749,6 +48185,275 @@ async function executeTransferInternal(params) {
47749
48185
  return result;
47750
48186
  }
47751
48187
 
48188
+ // packages/core/src/nexus/workspace.ts
48189
+ init_src();
48190
+ init_errors3();
48191
+ init_data_accessor();
48192
+ init_registry3();
48193
+ var RATE_LIMIT_WINDOW_MS = 6e4;
48194
+ var RATE_LIMIT_MAX_OPS = 100;
48195
+ var rateLimitCounters = /* @__PURE__ */ new Map();
48196
+ function checkRateLimit(agentId) {
48197
+ const now = Date.now();
48198
+ const entry = rateLimitCounters.get(agentId);
48199
+ if (!entry || now - entry.windowStart > RATE_LIMIT_WINDOW_MS) {
48200
+ rateLimitCounters.set(agentId, { count: 1, windowStart: now });
48201
+ return;
48202
+ }
48203
+ entry.count++;
48204
+ if (entry.count > RATE_LIMIT_MAX_OPS) {
48205
+ throw new CleoError(
48206
+ 1 /* GENERAL_ERROR */,
48207
+ `Agent '${agentId}' exceeded rate limit: ${RATE_LIMIT_MAX_OPS} routing ops per ${RATE_LIMIT_WINDOW_MS / 1e3}s`
48208
+ );
48209
+ }
48210
+ }
48211
+ var DEFAULT_ACL = { authorizedAgents: ["*"] };
48212
+ async function loadProjectACL(projectPath) {
48213
+ try {
48214
+ const { loadConfig: loadConfig3 } = await Promise.resolve().then(() => (init_config(), config_exports));
48215
+ const config2 = await loadConfig3(projectPath);
48216
+ const agents = config2?.authorizedAgents;
48217
+ if (Array.isArray(agents) && agents.length > 0) {
48218
+ return { authorizedAgents: agents };
48219
+ }
48220
+ } catch {
48221
+ }
48222
+ return DEFAULT_ACL;
48223
+ }
48224
+ function isAuthorized(acl, agentId) {
48225
+ if (acl.authorizedAgents.includes("*")) return true;
48226
+ return acl.authorizedAgents.includes(agentId);
48227
+ }
48228
+ var TASK_REF_PATTERN = /\bT(\d+)\b/g;
48229
+ function parseDirective(message) {
48230
+ const content = message.content;
48231
+ const verbMatch = content.match(/^\/(\w+)/);
48232
+ if (!verbMatch) return null;
48233
+ const verb = verbMatch[1];
48234
+ const taskRefs = [];
48235
+ const pattern = new RegExp(TASK_REF_PATTERN.source, "g");
48236
+ for (const m of content.matchAll(pattern)) {
48237
+ taskRefs.push(`T${m[1]}`);
48238
+ }
48239
+ const metaRefs = message.metadata?.taskRefs;
48240
+ if (Array.isArray(metaRefs)) {
48241
+ for (const ref of metaRefs) {
48242
+ if (typeof ref === "string" && !taskRefs.includes(ref)) {
48243
+ taskRefs.push(ref);
48244
+ }
48245
+ }
48246
+ }
48247
+ if (taskRefs.length === 0) return null;
48248
+ return {
48249
+ verb,
48250
+ taskRefs,
48251
+ agentId: message.from,
48252
+ messageId: message.id,
48253
+ timestamp: message.timestamp
48254
+ };
48255
+ }
48256
+ var VERB_TO_OPERATION = {
48257
+ claim: "tasks.start",
48258
+ done: "tasks.complete",
48259
+ complete: "tasks.complete",
48260
+ blocked: "tasks.update",
48261
+ // Update status to blocked
48262
+ start: "tasks.start",
48263
+ stop: "tasks.stop"
48264
+ };
48265
+ async function routeDirective(directive) {
48266
+ checkRateLimit(directive.agentId);
48267
+ const results = [];
48268
+ const operation = VERB_TO_OPERATION[directive.verb];
48269
+ if (!operation) {
48270
+ return results;
48271
+ }
48272
+ const projects = await nexusList();
48273
+ for (const taskRef of directive.taskRefs) {
48274
+ const result = await routeSingleTask(taskRef, directive, operation, projects);
48275
+ results.push(result);
48276
+ }
48277
+ return results;
48278
+ }
48279
+ async function routeSingleTask(taskId, directive, operation, projects) {
48280
+ let targetProject = null;
48281
+ let targetAccessor = null;
48282
+ for (const project of projects) {
48283
+ try {
48284
+ const acc = await getAccessor(project.path);
48285
+ const { tasks: tasks2 } = await acc.queryTasks({});
48286
+ const task = tasks2.find((t) => t.id === taskId);
48287
+ if (task) {
48288
+ targetProject = project;
48289
+ targetAccessor = acc;
48290
+ break;
48291
+ }
48292
+ } catch {
48293
+ }
48294
+ }
48295
+ if (!targetProject || !targetAccessor) {
48296
+ return {
48297
+ success: false,
48298
+ project: "unknown",
48299
+ projectPath: "",
48300
+ taskId,
48301
+ operation,
48302
+ error: `Task ${taskId} not found in any registered project`
48303
+ };
48304
+ }
48305
+ const acl = await loadProjectACL(targetProject.path);
48306
+ if (!isAuthorized(acl, directive.agentId)) {
48307
+ return {
48308
+ success: false,
48309
+ project: targetProject.name,
48310
+ projectPath: targetProject.path,
48311
+ taskId,
48312
+ operation,
48313
+ error: `Agent '${directive.agentId}' not authorized to mutate project '${targetProject.name}'`
48314
+ };
48315
+ }
48316
+ try {
48317
+ await executeOperation(operation, taskId, targetProject.path, targetAccessor, directive);
48318
+ await logRouteAudit(directive, targetProject.name, taskId, operation, true);
48319
+ return {
48320
+ success: true,
48321
+ project: targetProject.name,
48322
+ projectPath: targetProject.path,
48323
+ taskId,
48324
+ operation
48325
+ };
48326
+ } catch (err) {
48327
+ const errorMsg = err instanceof Error ? err.message : String(err);
48328
+ await logRouteAudit(directive, targetProject.name, taskId, operation, false, errorMsg);
48329
+ return {
48330
+ success: false,
48331
+ project: targetProject.name,
48332
+ projectPath: targetProject.path,
48333
+ taskId,
48334
+ operation,
48335
+ error: errorMsg
48336
+ };
48337
+ }
48338
+ }
48339
+ async function executeOperation(operation, taskId, projectPath, accessor, directive) {
48340
+ switch (operation) {
48341
+ case "tasks.start": {
48342
+ const { startTask: startTask2 } = await Promise.resolve().then(() => (init_task_work(), task_work_exports));
48343
+ await startTask2(taskId, projectPath, accessor);
48344
+ break;
48345
+ }
48346
+ case "tasks.complete": {
48347
+ const { completeTask: completeTask2 } = await Promise.resolve().then(() => (init_complete(), complete_exports));
48348
+ await completeTask2(
48349
+ { taskId, notes: `Completed via Conduit directive from ${directive.agentId}` },
48350
+ projectPath,
48351
+ accessor
48352
+ );
48353
+ break;
48354
+ }
48355
+ case "tasks.stop": {
48356
+ const { stopTask: stopTask2 } = await Promise.resolve().then(() => (init_task_work(), task_work_exports));
48357
+ await stopTask2(projectPath, accessor);
48358
+ break;
48359
+ }
48360
+ case "tasks.update": {
48361
+ const { updateTask: updateTask2 } = await Promise.resolve().then(() => (init_update2(), update_exports));
48362
+ await updateTask2(
48363
+ { taskId, notes: `Marked blocked via Conduit directive from ${directive.agentId}` },
48364
+ projectPath,
48365
+ accessor
48366
+ );
48367
+ break;
48368
+ }
48369
+ }
48370
+ }
48371
+ async function logRouteAudit(directive, projectName, taskId, operation, success2, error40) {
48372
+ try {
48373
+ const { getLogger: getLogger2 } = await Promise.resolve().then(() => (init_logger(), logger_exports));
48374
+ const log9 = getLogger2("nexus.route");
48375
+ const level = success2 ? "info" : "warn";
48376
+ log9[level](
48377
+ {
48378
+ directive: directive.verb,
48379
+ agentId: directive.agentId,
48380
+ messageId: directive.messageId,
48381
+ project: projectName,
48382
+ taskId,
48383
+ operation,
48384
+ success: success2,
48385
+ error: error40
48386
+ },
48387
+ `Conduit directive routed: ${directive.verb} ${taskId} \u2192 ${projectName} (${success2 ? "OK" : "FAILED"})`
48388
+ );
48389
+ } catch {
48390
+ }
48391
+ }
48392
+ async function workspaceStatus() {
48393
+ const projects = await nexusList();
48394
+ const summaries = [];
48395
+ const totals = { pending: 0, active: 0, done: 0, total: 0 };
48396
+ for (const project of projects) {
48397
+ try {
48398
+ const acc = await getAccessor(project.path);
48399
+ const { tasks: tasks2 } = await acc.queryTasks({});
48400
+ const counts = {
48401
+ pending: tasks2.filter((t) => t.status === "pending").length,
48402
+ active: tasks2.filter((t) => t.status === "active").length,
48403
+ done: tasks2.filter((t) => t.status === "done").length,
48404
+ total: tasks2.length
48405
+ };
48406
+ summaries.push({
48407
+ name: project.name,
48408
+ path: project.path,
48409
+ counts,
48410
+ health: project.healthStatus,
48411
+ lastSync: project.lastSync
48412
+ });
48413
+ totals.pending += counts.pending;
48414
+ totals.active += counts.active;
48415
+ totals.done += counts.done;
48416
+ totals.total += counts.total;
48417
+ } catch {
48418
+ summaries.push({
48419
+ name: project.name,
48420
+ path: project.path,
48421
+ counts: { pending: 0, active: 0, done: 0, total: 0 },
48422
+ health: "unreachable",
48423
+ lastSync: project.lastSync
48424
+ });
48425
+ }
48426
+ }
48427
+ return {
48428
+ projectCount: projects.length,
48429
+ projects: summaries,
48430
+ totals,
48431
+ computedAt: (/* @__PURE__ */ new Date()).toISOString()
48432
+ };
48433
+ }
48434
+ async function workspaceAgents() {
48435
+ const projects = await nexusList();
48436
+ const agents = [];
48437
+ for (const project of projects) {
48438
+ try {
48439
+ const { listAgentInstances: listAgentInstances2 } = await Promise.resolve().then(() => (init_registry2(), registry_exports2));
48440
+ const instances = await listAgentInstances2(void 0, project.path);
48441
+ for (const inst of instances) {
48442
+ agents.push({
48443
+ agentId: inst.id,
48444
+ agentType: inst.agentType,
48445
+ status: inst.status,
48446
+ project: project.name,
48447
+ taskId: inst.taskId ?? null,
48448
+ lastHeartbeat: inst.lastHeartbeat
48449
+ });
48450
+ }
48451
+ } catch {
48452
+ }
48453
+ }
48454
+ return agents;
48455
+ }
48456
+
47752
48457
  // packages/core/src/observability/index.ts
47753
48458
  var observability_exports = {};
47754
48459
  __export(observability_exports, {
@@ -60551,137 +61256,8 @@ async function uncancelTask(projectRoot, params) {
60551
61256
  };
60552
61257
  }
60553
61258
 
60554
- // packages/core/src/task-work/index.ts
60555
- var task_work_exports = {};
60556
- __export(task_work_exports, {
60557
- currentTask: () => currentTask,
60558
- getTaskHistory: () => getTaskHistory,
60559
- getWorkHistory: () => getWorkHistory,
60560
- startTask: () => startTask,
60561
- stopTask: () => stopTask
60562
- });
60563
- init_src();
60564
- init_errors3();
60565
- init_data_accessor();
60566
- init_add();
60567
- init_handlers();
60568
- async function currentTask(cwd, accessor) {
60569
- const acc = accessor ?? await getAccessor(cwd);
60570
- const focus = await acc.getMetaValue("focus_state");
60571
- return {
60572
- currentTask: focus?.currentTask ?? null,
60573
- currentPhase: focus?.currentPhase ?? null,
60574
- sessionNote: focus?.sessionNote ?? null,
60575
- nextAction: focus?.nextAction ?? null
60576
- };
60577
- }
60578
- async function startTask(taskId, cwd, accessor) {
60579
- if (!taskId) {
60580
- throw new CleoError(2 /* INVALID_INPUT */, "Task ID is required");
60581
- }
60582
- const acc = accessor ?? await getAccessor(cwd);
60583
- const task = await acc.loadSingleTask(taskId);
60584
- if (!task) {
60585
- throw new CleoError(4 /* NOT_FOUND */, `Task not found: ${taskId}`, {
60586
- fix: `Use 'cleo find "${taskId}"' to search`
60587
- });
60588
- }
60589
- const { tasks: allTasks } = await acc.queryTasks({});
60590
- const unresolvedDeps = getUnresolvedDeps(taskId, allTasks);
60591
- if (unresolvedDeps.length > 0) {
60592
- throw new CleoError(
60593
- 5 /* DEPENDENCY_ERROR */,
60594
- `Task ${taskId} is blocked by unresolved dependencies: ${unresolvedDeps.join(", ")}`,
60595
- {
60596
- fix: `Complete blockers first: ${unresolvedDeps.map((d) => `cleo complete ${d}`).join(", ")}`
60597
- }
60598
- );
60599
- }
60600
- const focus = await acc.getMetaValue("focus_state") ?? {};
60601
- const previousTask = focus.currentTask ?? null;
60602
- focus.currentTask = taskId;
60603
- focus.currentPhase = task.phase ?? null;
60604
- const noteEntry = {
60605
- note: `Started work on ${taskId}: ${task.title}`,
60606
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
60607
- };
60608
- if (!focus.sessionNotes) {
60609
- focus.sessionNotes = [];
60610
- }
60611
- focus.sessionNotes.push(noteEntry);
60612
- await acc.setMetaValue("focus_state", focus);
60613
- await logOperation(
60614
- "task_start",
60615
- taskId,
60616
- {
60617
- previousTask,
60618
- title: task.title
60619
- },
60620
- accessor
60621
- );
60622
- const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
60623
- hooks2.dispatch("onToolStart", cwd ?? process.cwd(), {
60624
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
60625
- taskId,
60626
- taskTitle: task.title
60627
- }).catch(() => {
60628
- });
60629
- return {
60630
- taskId,
60631
- taskTitle: task.title,
60632
- previousTask
60633
- };
60634
- }
60635
- async function stopTask(cwd, accessor) {
60636
- const acc = accessor ?? await getAccessor(cwd);
60637
- const focus = await acc.getMetaValue("focus_state");
60638
- const previousTask = focus?.currentTask ?? null;
60639
- if (!focus) {
60640
- return { previousTask: null };
60641
- }
60642
- const taskId = focus.currentTask;
60643
- const task = taskId ? await acc.loadSingleTask(taskId) : void 0;
60644
- focus.currentTask = null;
60645
- focus.nextAction = null;
60646
- const now = (/* @__PURE__ */ new Date()).toISOString();
60647
- if (taskId && task) {
60648
- const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
60649
- hooks2.dispatch("onToolComplete", cwd ?? process.cwd(), {
60650
- timestamp: now,
60651
- taskId,
60652
- taskTitle: task.title,
60653
- status: "done"
60654
- }).catch(() => {
60655
- });
60656
- }
60657
- await acc.setMetaValue("focus_state", focus);
60658
- await logOperation(
60659
- "task_stop",
60660
- previousTask ?? "none",
60661
- {
60662
- previousTask
60663
- },
60664
- accessor
60665
- );
60666
- return { previousTask };
60667
- }
60668
- async function getWorkHistory(cwd, accessor) {
60669
- const acc = accessor ?? await getAccessor(cwd);
60670
- const focus = await acc.getMetaValue("focus_state");
60671
- const notes = focus?.sessionNotes ?? [];
60672
- const history = [];
60673
- for (const note of notes) {
60674
- const match = note.note.match(/^(?:Focus set to|Started work on) (T\d+)/);
60675
- if (match) {
60676
- history.push({
60677
- taskId: match[1],
60678
- timestamp: note.timestamp
60679
- });
60680
- }
60681
- }
60682
- return history.reverse();
60683
- }
60684
- var getTaskHistory = getWorkHistory;
61259
+ // packages/core/src/index.ts
61260
+ init_task_work();
60685
61261
 
60686
61262
  // packages/core/src/tasks/index.ts
60687
61263
  var tasks_exports = {};
@@ -65364,7 +65940,7 @@ async function restoreSession(projectRoot, snapshot, options = {}, accessor) {
65364
65940
  await acc.upsertSingleSession(restoredSession);
65365
65941
  try {
65366
65942
  const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
65367
- await hooks2.dispatch("onSessionStart", projectRoot, {
65943
+ await hooks2.dispatch("SessionStart", projectRoot, {
65368
65944
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
65369
65945
  sessionId: restoredSession.id,
65370
65946
  name: restoredSession.name,
@@ -65380,6 +65956,7 @@ async function restoreSession(projectRoot, snapshot, options = {}, accessor) {
65380
65956
 
65381
65957
  // packages/core/src/cleo.ts
65382
65958
  init_data_accessor();
65959
+ init_task_work();
65383
65960
  init_add();
65384
65961
  init_complete();
65385
65962
  init_update2();
@@ -65604,7 +66181,14 @@ var Cleo = class _Cleo {
65604
66181
  discover: (p) => discoverRelated(p.query, p.method, p.limit),
65605
66182
  search: (p) => searchAcrossProjects(p.pattern, p.project, p.limit),
65606
66183
  setPermission: (p) => setPermission(p.name, p.level),
65607
- sharingStatus: () => getSharingStatus()
66184
+ sharingStatus: () => getSharingStatus(),
66185
+ route: (message) => {
66186
+ const directive = parseDirective(message);
66187
+ if (!directive) return Promise.resolve([]);
66188
+ return routeDirective(directive);
66189
+ },
66190
+ workspaceStatus: () => workspaceStatus(),
66191
+ workspaceAgents: () => workspaceAgents()
65608
66192
  };
65609
66193
  }
65610
66194
  // === Agents ===
@@ -65665,6 +66249,7 @@ init_registry();
65665
66249
  init_brain_retrieval();
65666
66250
  init_brain_search();
65667
66251
  init_sessions();
66252
+ init_task_work();
65668
66253
  init_add();
65669
66254
  init_complete();
65670
66255
  init_update2();