@evo-dev/core 0.0.1-alpha.2 → 0.0.1-alpha.20

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 (79) hide show
  1. package/assets/skills/coding/knowledge-distillation/SKILL.md +5 -3
  2. package/assets/team/agents/code-reviewer.md +48 -0
  3. package/assets/team/agents/docs-maintainer.md +51 -0
  4. package/assets/team/agents/implementation-engineer.md +51 -0
  5. package/assets/team/agents/product-scope-analyst.md +58 -0
  6. package/assets/team/agents/release-engineer.md +55 -0
  7. package/assets/team/agents/security-boundary-reviewer.md +50 -0
  8. package/assets/team/agents/solution-architect.md +51 -0
  9. package/assets/team/agents/verification-engineer.md +51 -0
  10. package/assets/team/team.md +102 -0
  11. package/dist/assets/index.js +5 -5
  12. package/dist/config/index.js +793 -241
  13. package/dist/index.js +20840 -12908
  14. package/dist/plugins/index.js +13 -13
  15. package/package.json +1 -1
  16. package/src/agents/index.ts +1 -265
  17. package/src/code-agent-traces/index.ts +11 -12
  18. package/src/config/index.ts +2 -0
  19. package/src/config/settings.ts +116 -7
  20. package/src/config/store.ts +1 -1
  21. package/src/daemon/index.ts +1 -41
  22. package/src/evolution/candidates/index.ts +730 -0
  23. package/src/evolution/control/index.ts +20 -0
  24. package/src/evolution/evidence/analysis.ts +533 -0
  25. package/src/evolution/evidence/index.ts +3 -0
  26. package/src/evolution/evidence/session-memory/analysis.ts +287 -0
  27. package/src/evolution/evidence/session-memory/constants.ts +9 -0
  28. package/src/evolution/evidence/session-memory/index.ts +9 -0
  29. package/src/evolution/evidence/session-memory/paths.ts +29 -0
  30. package/src/evolution/evidence/session-memory/policy.ts +39 -0
  31. package/src/evolution/evidence/session-memory/retention.ts +643 -0
  32. package/src/evolution/evidence/session-memory/segment.ts +216 -0
  33. package/src/evolution/evidence/session-memory/semantic-packet.ts +408 -0
  34. package/src/evolution/evidence/session-memory/sensitivity.ts +335 -0
  35. package/src/evolution/evidence/session-memory/state-machine.ts +249 -0
  36. package/src/evolution/evidence/session-memory/storage.ts +744 -0
  37. package/src/evolution/evidence/session-memory/types.ts +296 -0
  38. package/src/evolution/evidence/session-memory/updater.ts +199 -0
  39. package/src/evolution/formatters.ts +169 -0
  40. package/src/evolution/imports/apply.ts +435 -0
  41. package/src/evolution/imports/diff.ts +472 -0
  42. package/src/evolution/imports/index.ts +7 -0
  43. package/src/evolution/imports/materialize.ts +640 -0
  44. package/src/evolution/imports/paths.ts +129 -0
  45. package/src/evolution/imports/stage.ts +414 -0
  46. package/src/evolution/imports/storage.ts +952 -0
  47. package/src/evolution/imports/types.ts +226 -0
  48. package/src/evolution/index.ts +19 -2827
  49. package/src/evolution/knowledge/change-store.ts +558 -0
  50. package/src/evolution/knowledge/changes.ts +459 -0
  51. package/src/evolution/knowledge/freshness.ts +69 -0
  52. package/src/{knowledge → evolution/knowledge}/index.ts +1532 -206
  53. package/src/evolution/knowledge/review.ts +446 -0
  54. package/src/evolution/knowledge/support.ts +135 -0
  55. package/src/evolution/paths.ts +44 -0
  56. package/src/evolution/processor/distillation.ts +518 -0
  57. package/src/evolution/processor/index.ts +3 -0
  58. package/src/evolution/processor/process.ts +594 -0
  59. package/src/{learning → evolution/review}/index.ts +10 -14
  60. package/src/evolution/schema.ts +639 -0
  61. package/src/evolution/shared.ts +1053 -0
  62. package/src/evolution/triggers/classification.ts +102 -0
  63. package/src/evolution/triggers/index.ts +295 -0
  64. package/src/hooks/index.ts +281 -197
  65. package/src/index.ts +15 -4
  66. package/src/projects/index.ts +934 -0
  67. package/src/runtime-logs/index.ts +100 -13
  68. package/src/team/index.ts +582 -3
  69. package/src/utils/errors.ts +13 -0
  70. package/src/utils/fs.ts +40 -0
  71. package/src/utils/hash.ts +9 -0
  72. package/src/utils/ids.ts +12 -0
  73. package/src/utils/index.ts +7 -0
  74. package/src/utils/parsing.ts +11 -0
  75. package/src/utils/text.ts +18 -0
  76. package/src/utils/time.ts +5 -0
  77. package/src/workflow/index.ts +3 -21
  78. package/src/project/index.ts +0 -507
  79. package/src/task/index.ts +0 -840
@@ -1,20 +1,22 @@
1
- import { createHash } from "node:crypto";
2
1
  import { mkdir, readFile, writeFile } from "node:fs/promises";
3
2
  import { dirname, join } from "node:path";
4
- import { enqueueEvolutionTrigger, resolveEvolutionTriggerDecision } from "../evolution/index.ts";
3
+ import {
4
+ type SessionMemoryPolicySnapshot,
5
+ updateSessionMemoryFromHook,
6
+ } from "../evolution/evidence/session-memory/index.ts";
5
7
  import {
6
8
  createScopedKnowledgeContextPack,
7
9
  formatScopedKnowledgePromptBlock,
8
10
  hasContextInjectionReceipt,
11
+ recordContextInjectionOutcome,
9
12
  writeContextInjectionReceipt,
10
- } from "../knowledge/index.ts";
11
- import { resolveTraceTeamContext } from "../runtime-logs/index.ts";
13
+ } from "../evolution/knowledge/index.ts";
12
14
  import {
13
- type TaskContract,
14
- createTaskContract,
15
- routeTaskContract,
16
- writeTaskContract,
17
- } from "../task/index.ts";
15
+ enqueueEvolutionTrigger,
16
+ resolveEvolutionTriggerDecision,
17
+ } from "../evolution/triggers/index.ts";
18
+ import { recordDiscoveredProject, resolveProjectWorkspaceFromCwd } from "../projects/index.ts";
19
+ import { resolveTraceSessionKey, resolveTraceTeamContext } from "../runtime-logs/index.ts";
18
20
  import {
19
21
  type TeamMessageRecord,
20
22
  createTeamRoleRuntimeContext,
@@ -24,6 +26,7 @@ import {
24
26
  resolveTeamRunPaths,
25
27
  updateTeamAgentHookState,
26
28
  } from "../team/index.ts";
29
+ import { sha256Short } from "../utils/index.ts";
27
30
 
28
31
  export const CANONICAL_HOOK_EVENT_TYPES = [
29
32
  "SessionStart",
@@ -152,11 +155,9 @@ export interface HookRuntimeSessionBinding {
152
155
  version: 1;
153
156
  target: CodeAgentHookTarget;
154
157
  sessionKey: string;
155
- taskId: string | null;
156
- contractPath: string | null;
157
158
  cwd: string | null;
158
- route: TaskContract["route"] | null;
159
159
  teamRuntimeContextDeliveredAt?: string | null;
160
+ knowledgeContextDeliveredAt?: string | null;
160
161
  updatedAt: string;
161
162
  }
162
163
 
@@ -180,6 +181,7 @@ export interface HandleHookRuntimeInput {
180
181
  receivedAt?: string;
181
182
  environment?: Record<string, string | undefined>;
182
183
  runtimeInjectionEnabled?: boolean;
184
+ sessionMemoryPolicy?: Partial<SessionMemoryPolicySnapshot>;
183
185
  }
184
186
 
185
187
  const DEFAULT_EVENT_SETTINGS: Record<CanonicalHookEventType, boolean> = {
@@ -226,6 +228,12 @@ const CODEX_STOP_EVENTS_WITHOUT_ADDITIONAL_CONTEXT = new Set<CanonicalHookEventT
226
228
  "Stop",
227
229
  "SubagentStop",
228
230
  ]);
231
+ const COMPLETION_EVENTS_WITHOUT_DEVELOPMENT_DIAGNOSTICS = new Set<CanonicalHookEventType>([
232
+ "Stop",
233
+ "SubagentStop",
234
+ "TaskCompleted",
235
+ "TeammateIdle",
236
+ ]);
229
237
 
230
238
  export function createDefaultHookSettings(): HookSettings {
231
239
  return {
@@ -416,12 +424,11 @@ export function formatHookEventDryRun(event: HookEventV1): string {
416
424
  export function resolveHookRuntimeSessionPaths(input: {
417
425
  homeDir: string;
418
426
  sessionKey: string;
419
- }): { sessionDir: string; bindingPath: string; contractPath: string } {
427
+ }): { sessionDir: string; bindingPath: string } {
420
428
  const sessionDir = join(input.homeDir, ".evodev", "STATE", "hooks", "sessions", input.sessionKey);
421
429
  return {
422
430
  sessionDir,
423
431
  bindingPath: join(sessionDir, "binding.json"),
424
- contractPath: join(sessionDir, "contract.json"),
425
432
  };
426
433
  }
427
434
 
@@ -439,7 +446,14 @@ export async function handleHookRuntime(input: HandleHookRuntimeInput): Promise<
439
446
  };
440
447
  }
441
448
 
449
+ if (isActiveClaudeStopHook(input)) {
450
+ return createRuntimeResult(input, null, {
451
+ summary: `${input.event.type} re-entry allowed to complete without hook output.`,
452
+ });
453
+ }
454
+
442
455
  const diagnostics = await recordTeamNativeSessionFromHook(input);
456
+ const projectDiagnostics = await recordDiscoveredProjectFromHook(input);
443
457
  let result: HookRuntimeResult | undefined;
444
458
  if (input.event.type === "SessionStart") result = await handleSessionStart(input);
445
459
  else if (input.event.type === "UserPromptSubmit") result = await handleUserPromptSubmit(input);
@@ -468,23 +482,29 @@ export async function handleHookRuntime(input: HandleHookRuntimeInput): Promise<
468
482
  } else if (result === undefined) {
469
483
  result = await handleAdditionalContext(input, input.event.type);
470
484
  }
471
- const evolutionDiagnostics = await recordEvolutionTriggerFromHook(input);
485
+ const sessionMemoryDiagnostics = await recordSessionMemoryFromHook(input);
486
+ const evolutionDiagnostics =
487
+ sessionMemoryDiagnostics.queuedSegmentTriggerId !== null ||
488
+ shouldSuppressLegacyEvolutionTrigger(input.event.type)
489
+ ? { stateWrites: [], warnings: [] }
490
+ : await recordEvolutionTriggerFromHook(input);
472
491
  const messageDiagnostics = await deliverPendingTeamMessagesFromHook(input, result);
473
492
  const scopedContextDiagnostics = await injectScopedKnowledgeContextFromHook(input, result);
493
+ const knowledgeOutcomeDiagnostics = await recordKnowledgeOutcomeFromHook(input);
474
494
  const teamStateDiagnostics = await recordTeamAgentHookStateFromHook(input);
475
- return appendRuntimeDiagnostics(
476
- appendRuntimeDiagnostics(
477
- appendRuntimeDiagnostics(
478
- appendRuntimeDiagnostics(
479
- appendRuntimeDiagnostics(result, diagnostics),
480
- evolutionDiagnostics,
481
- ),
482
- messageDiagnostics,
483
- ),
484
- scopedContextDiagnostics,
485
- ),
495
+ let completed = appendRuntimeDiagnostics(result, diagnostics);
496
+ for (const entry of [
497
+ projectDiagnostics,
498
+ sessionMemoryDiagnostics,
499
+ evolutionDiagnostics,
500
+ messageDiagnostics,
501
+ scopedContextDiagnostics,
502
+ knowledgeOutcomeDiagnostics,
486
503
  teamStateDiagnostics,
487
- );
504
+ ]) {
505
+ completed = appendRuntimeDiagnostics(completed, entry);
506
+ }
507
+ return appendDevelopmentHookDiagnostics(input, completed);
488
508
  }
489
509
 
490
510
  export function formatHookRuntimeOutput(result: HookRuntimeResult): string {
@@ -508,6 +528,41 @@ interface HookRuntimeDiagnostics {
508
528
  warnings: string[];
509
529
  }
510
530
 
531
+ async function recordDiscoveredProjectFromHook(
532
+ input: HandleHookRuntimeInput,
533
+ ): Promise<HookRuntimeDiagnostics> {
534
+ if (
535
+ input.event.type !== "SessionStart" &&
536
+ input.event.type !== "UserPromptSubmit" &&
537
+ input.event.type !== "CwdChanged"
538
+ ) {
539
+ return { stateWrites: [], warnings: [] };
540
+ }
541
+ const cwd = optionalPayloadString(input.rawPayload.cwd);
542
+ if (cwd === null) return { stateWrites: [], warnings: [] };
543
+
544
+ try {
545
+ const result = await recordDiscoveredProject({
546
+ homeDir: input.homeDir,
547
+ cwd,
548
+ target: input.target,
549
+ sessionKey: resolveTraceSessionKey(input.rawPayload),
550
+ now:
551
+ input.receivedAt === undefined || input.receivedAt === "dry-run"
552
+ ? undefined
553
+ : input.receivedAt,
554
+ });
555
+ return result === null
556
+ ? { stateWrites: [], warnings: [] }
557
+ : { stateWrites: [result.path], warnings: [] };
558
+ } catch {
559
+ return {
560
+ stateWrites: [],
561
+ warnings: ["Local project discovery could not be updated at this hook safe point."],
562
+ };
563
+ }
564
+ }
565
+
511
566
  async function recordTeamNativeSessionFromHook(
512
567
  input: HandleHookRuntimeInput,
513
568
  ): Promise<HookRuntimeDiagnostics> {
@@ -611,6 +666,37 @@ async function recordEvolutionTriggerFromHook(
611
666
  }
612
667
  }
613
668
 
669
+ async function recordSessionMemoryFromHook(
670
+ input: HandleHookRuntimeInput,
671
+ ): Promise<HookRuntimeDiagnostics & { queuedSegmentTriggerId: string | null }> {
672
+ try {
673
+ const result = await updateSessionMemoryFromHook({
674
+ homeDir: input.homeDir,
675
+ target: input.target,
676
+ event: input.event,
677
+ rawPayload: input.rawPayload,
678
+ environment: input.environment,
679
+ receivedAt: input.receivedAt,
680
+ policy: input.sessionMemoryPolicy,
681
+ });
682
+ return {
683
+ stateWrites: result.stateWrites,
684
+ warnings: result.warnings,
685
+ queuedSegmentTriggerId: result.queuedSegmentTriggerId,
686
+ };
687
+ } catch {
688
+ return {
689
+ stateWrites: [],
690
+ warnings: ["Session Memory state could not be updated at this hook safe point."],
691
+ queuedSegmentTriggerId: null,
692
+ };
693
+ }
694
+ }
695
+
696
+ function shouldSuppressLegacyEvolutionTrigger(eventType: CanonicalHookEventType): boolean {
697
+ return eventType === "Stop" || eventType === "SubagentStop" || eventType === "SessionEnd";
698
+ }
699
+
614
700
  async function deliverPendingTeamMessagesFromHook(
615
701
  input: HandleHookRuntimeInput,
616
702
  result: HookRuntimeResult | undefined,
@@ -675,7 +761,8 @@ async function injectScopedKnowledgeContextFromHook(
675
761
  environment: input.environment,
676
762
  payload: input.rawPayload,
677
763
  });
678
- if (team === null || team.roleId !== "main") return { stateWrites: [], warnings: [] };
764
+ if (team === null) return injectOrdinarySessionKnowledge(input, result);
765
+ if (team.roleId !== "main") return { stateWrites: [], warnings: [] };
679
766
 
680
767
  try {
681
768
  if (input.runtimeInjectionEnabled === false) return { stateWrites: [], warnings: [] };
@@ -717,6 +804,112 @@ async function injectScopedKnowledgeContextFromHook(
717
804
  }
718
805
  }
719
806
 
807
+ async function injectOrdinarySessionKnowledge(
808
+ input: HandleHookRuntimeInput,
809
+ result: HookRuntimeResult,
810
+ ): Promise<HookRuntimeDiagnostics> {
811
+ if (input.event.type !== "UserPromptSubmit" || input.runtimeInjectionEnabled === false) {
812
+ return { stateWrites: [], warnings: [] };
813
+ }
814
+ const cwd = optionalPayloadString(input.rawPayload.cwd);
815
+ if (cwd === null) return { stateWrites: [], warnings: [] };
816
+ const sessionKey = resolveHookSessionKey(input.rawPayload);
817
+ const paths = resolveHookRuntimeSessionPaths({ homeDir: input.homeDir, sessionKey });
818
+ const binding = await readSessionBinding(input.homeDir, input.rawPayload);
819
+ if (
820
+ binding?.knowledgeContextDeliveredAt !== undefined &&
821
+ binding.knowledgeContextDeliveredAt !== null
822
+ ) {
823
+ return { stateWrites: [], warnings: [] };
824
+ }
825
+
826
+ try {
827
+ const workspace = await resolveProjectWorkspaceFromCwd({ homeDir: input.homeDir, cwd });
828
+ if (workspace === null) return { stateWrites: [], warnings: [] };
829
+ const queryText = readKnowledgeQueryText(input.rawPayload);
830
+ const pack = await createScopedKnowledgeContextPack({
831
+ homeDir: input.homeDir,
832
+ projectKey: workspace.projectKey,
833
+ roleId: input.target,
834
+ ...(queryText === null ? {} : { queryText }),
835
+ limit: 3,
836
+ inlineOnly: true,
837
+ });
838
+ const deliveredAt = input.receivedAt ?? new Date().toISOString();
839
+ const nextBinding: HookRuntimeSessionBinding = {
840
+ version: 1,
841
+ target: input.target,
842
+ sessionKey,
843
+ cwd,
844
+ teamRuntimeContextDeliveredAt: binding?.teamRuntimeContextDeliveredAt ?? null,
845
+ knowledgeContextDeliveredAt: deliveredAt,
846
+ updatedAt: deliveredAt,
847
+ };
848
+ await writeJsonFile(paths.bindingPath, nextBinding);
849
+ if (pack === null) return { stateWrites: [paths.bindingPath], warnings: [] };
850
+ const receipt = await writeContextInjectionReceipt({
851
+ homeDir: input.homeDir,
852
+ sessionKey,
853
+ pack,
854
+ trigger: "ordinary-session",
855
+ hookEventId: input.event.eventId,
856
+ injectedAt: deliveredAt,
857
+ });
858
+ result.output = appendAdditionalContext(
859
+ result.output,
860
+ input.event.type,
861
+ formatScopedKnowledgePromptBlock(pack),
862
+ );
863
+ return { stateWrites: [paths.bindingPath, receipt.path], warnings: [] };
864
+ } catch {
865
+ return {
866
+ stateWrites: [],
867
+ warnings: ["Ordinary-session knowledge context could not be injected."],
868
+ };
869
+ }
870
+ }
871
+
872
+ function readKnowledgeQueryText(payload: Record<string, unknown>): string | null {
873
+ for (const value of [
874
+ payload.prompt,
875
+ payload.user_prompt,
876
+ payload.userPrompt,
877
+ payload.query,
878
+ payload.message,
879
+ payload.text,
880
+ ]) {
881
+ if (typeof value === "string" && value.trim() !== "") return value.trim().slice(0, 4_000);
882
+ }
883
+ return null;
884
+ }
885
+
886
+ async function recordKnowledgeOutcomeFromHook(
887
+ input: HandleHookRuntimeInput,
888
+ ): Promise<HookRuntimeDiagnostics> {
889
+ if (!isKnowledgeOutcomeSignal(input.event)) return { stateWrites: [], warnings: [] };
890
+ try {
891
+ const paths = await recordContextInjectionOutcome({
892
+ homeDir: input.homeDir,
893
+ sessionKey: resolveHookSessionKey(input.rawPayload),
894
+ eventId: input.event.eventId,
895
+ observedAt: input.receivedAt ?? input.event.time.receivedAt ?? new Date().toISOString(),
896
+ summary: input.event.payload.summary,
897
+ });
898
+ return { stateWrites: paths, warnings: [] };
899
+ } catch {
900
+ return {
901
+ stateWrites: [],
902
+ warnings: ["Knowledge delivery outcome metadata could not be recorded."],
903
+ };
904
+ }
905
+ }
906
+
907
+ function isKnowledgeOutcomeSignal(event: HookEventV1): boolean {
908
+ if (event.type !== "PostToolUse") return false;
909
+ if (event.payload.metadata.commandClass === "test-command") return true;
910
+ return /\b(?:test|lint|typecheck|build|verify|check)\b/iu.test(event.payload.summary);
911
+ }
912
+
720
913
  function canDeliverTeamMessagesFromHook(
721
914
  target: CodeAgentHookTarget,
722
915
  eventType: CanonicalHookEventType,
@@ -778,54 +971,38 @@ async function handleSessionStart(input: HandleHookRuntimeInput): Promise<HookRu
778
971
  }
779
972
 
780
973
  async function handleUserPromptSubmit(input: HandleHookRuntimeInput): Promise<HookRuntimeResult> {
781
- const classification = classifyUserPrompt(input.rawPayload.prompt ?? input.rawPayload.userPrompt);
782
974
  const sessionKey = resolveHookSessionKey(input.rawPayload);
783
975
  const paths = resolveHookRuntimeSessionPaths({ homeDir: input.homeDir, sessionKey });
784
- const contract = routeTaskContract(createHookTaskContract(input, classification));
785
976
  const previousBinding = await readSessionBinding(input.homeDir, input.rawPayload);
786
977
  const teamRuntimeContext =
787
978
  previousBinding?.teamRuntimeContextDeliveredAt === undefined ||
788
979
  previousBinding.teamRuntimeContextDeliveredAt === null
789
980
  ? await createTeamRuntimeContextForUserPrompt(input)
790
981
  : null;
791
- const shouldShowDiagnostics = input.teamRuntimeDisplayMode === "development";
792
982
  const teamRuntimeContextDeliveredAt =
793
- teamRuntimeContext !== null && shouldShowDiagnostics
983
+ teamRuntimeContext !== null
794
984
  ? (input.receivedAt ?? new Date().toISOString())
795
985
  : (previousBinding?.teamRuntimeContextDeliveredAt ?? null);
796
986
  const binding: HookRuntimeSessionBinding = {
797
987
  version: 1,
798
988
  target: input.target,
799
989
  sessionKey,
800
- taskId: contract.taskId,
801
- contractPath: paths.contractPath,
802
990
  cwd: optionalPayloadString(input.rawPayload.cwd),
803
- route: contract.route,
804
991
  teamRuntimeContextDeliveredAt,
992
+ knowledgeContextDeliveredAt: previousBinding?.knowledgeContextDeliveredAt ?? null,
805
993
  updatedAt: input.receivedAt ?? new Date().toISOString(),
806
994
  };
807
995
 
808
- await writeTaskContract(paths.contractPath, contract, { overwrite: true });
809
996
  await writeJsonFile(paths.bindingPath, binding);
810
997
 
811
- const visibleContext = createUserPromptVisibleContext({
812
- classification,
813
- contract,
814
- contractPath: paths.contractPath,
815
- });
816
- let output =
817
- visibleContext === null || !shouldShowDiagnostics
818
- ? null
819
- : hookOutput(input.event.type, {
820
- additionalContext: visibleContext,
821
- });
822
- if (teamRuntimeContext !== null && shouldShowDiagnostics) {
998
+ let output: Record<string, unknown> | null = null;
999
+ if (teamRuntimeContext !== null) {
823
1000
  output = appendAdditionalContext(output, input.event.type, teamRuntimeContext);
824
1001
  }
825
1002
 
826
1003
  return createRuntimeResult(input, output, {
827
- summary: "User prompt observed; Task Contract prepared.",
828
- stateWrites: [paths.contractPath, paths.bindingPath],
1004
+ summary: "User prompt observed; session binding updated.",
1005
+ stateWrites: [paths.bindingPath],
829
1006
  });
830
1007
  }
831
1008
 
@@ -862,46 +1039,14 @@ async function handlePreToolUse(input: HandleHookRuntimeInput): Promise<HookRunt
862
1039
  }
863
1040
 
864
1041
  async function handlePostToolUse(input: HandleHookRuntimeInput): Promise<HookRuntimeResult> {
865
- const contract = await readActiveContract(input.homeDir, input.rawPayload);
866
- const binding = await readSessionBinding(input.homeDir, input.rawPayload);
867
- if (contract === null || binding?.contractPath === null || binding?.contractPath === undefined) {
868
- return handleAdditionalContext(input, input.event.type);
869
- }
870
-
871
- const status = optionalPayloadNumber(input.rawPayload.exit_code ?? input.rawPayload.exitCode);
872
- const nextContract: TaskContract = {
873
- ...contract,
874
- evidence: {
875
- metadataOnly: true,
876
- items: [
877
- ...contract.evidence.items,
878
- {
879
- type: "command-result",
880
- id: input.event.eventId,
881
- status: status === 0 ? "pass" : status === null ? "unknown" : "fail",
882
- summary: input.event.payload.summary,
883
- rawOutputStored: false,
884
- sourceContentStored: false,
885
- },
886
- ],
887
- },
888
- };
889
- await writeTaskContract(binding.contractPath, nextContract, { overwrite: true });
890
-
891
- return createRuntimeResult(input, null, {
892
- summary: "Post-tool metadata evidence recorded.",
893
- stateWrites: [binding.contractPath],
894
- });
1042
+ return handleAdditionalContext(input, input.event.type);
895
1043
  }
896
1044
 
897
1045
  async function handleCompletionObservation(
898
1046
  input: HandleHookRuntimeInput,
899
1047
  ): Promise<HookRuntimeResult> {
900
- const contract = await readActiveContract(input.homeDir, input.rawPayload);
901
- const suffix =
902
- contract === null ? "without an active Task Contract" : `for Task Contract ${contract.taskId}`;
903
1048
  return createRuntimeResult(input, null, {
904
- summary: `${input.event.type} observed ${suffix}; no completion control enforced.`,
1049
+ summary: `${input.event.type} observed as metadata-only hook context.`,
905
1050
  });
906
1051
  }
907
1052
 
@@ -991,6 +1136,56 @@ function appendAdditionalContext(
991
1136
  };
992
1137
  }
993
1138
 
1139
+ function appendDevelopmentHookDiagnostics(
1140
+ input: HandleHookRuntimeInput,
1141
+ result: HookRuntimeResult,
1142
+ ): HookRuntimeResult {
1143
+ if (
1144
+ input.teamRuntimeDisplayMode !== "development" ||
1145
+ COMPLETION_EVENTS_WITHOUT_DEVELOPMENT_DIAGNOSTICS.has(input.event.type) ||
1146
+ !canDeliverTeamMessagesFromHook(input.target, input.event.type)
1147
+ ) {
1148
+ return result;
1149
+ }
1150
+ return {
1151
+ ...result,
1152
+ output: appendAdditionalContext(
1153
+ result.output,
1154
+ input.event.type,
1155
+ formatDevelopmentHookDiagnostics(input),
1156
+ ),
1157
+ };
1158
+ }
1159
+
1160
+ function isActiveClaudeStopHook(input: HandleHookRuntimeInput): boolean {
1161
+ return (
1162
+ input.target === "claude" &&
1163
+ (input.event.type === "Stop" || input.event.type === "SubagentStop") &&
1164
+ input.rawPayload.stop_hook_active === true
1165
+ );
1166
+ }
1167
+
1168
+ function formatDevelopmentHookDiagnostics(input: HandleHookRuntimeInput): string {
1169
+ const metadata = Object.entries(input.event.payload.metadata);
1170
+ const inputFields = Object.keys(input.rawPayload)
1171
+ .map((field) => field.replace(/[^A-Za-z0-9._-]/g, "-").slice(0, 80))
1172
+ .filter((field) => field.length > 0)
1173
+ .slice(0, 30);
1174
+ return [
1175
+ "EvoDev hook development diagnostics",
1176
+ `Target: ${input.target}`,
1177
+ `Event: ${input.event.type}`,
1178
+ `Summary: ${input.event.payload.summary}`,
1179
+ `Input fields: ${inputFields.join(", ") || "none"}`,
1180
+ "Normalized metadata:",
1181
+ ...(metadata.length === 0
1182
+ ? ["- none"]
1183
+ : metadata.map(([key, value]) => `- ${key}: ${formatMetadataValue(value)}`)),
1184
+ `Redactions: ${input.event.payload.redactions.join(", ") || "none"}`,
1185
+ "Raw payload included: false",
1186
+ ].join("\n");
1187
+ }
1188
+
994
1189
  function formatTeamInboxContext(messages: TeamMessageRecord[]): string {
995
1190
  const blocks = messages.map((message) =>
996
1191
  [
@@ -1014,113 +1209,6 @@ function truncateTeamMessageBody(value: string): string {
1014
1209
  return `${value.slice(0, 4000)}...[truncated:${value.length - 4000}]`;
1015
1210
  }
1016
1211
 
1017
- function createUserPromptVisibleContext(input: {
1018
- classification: ReturnType<typeof classifyUserPrompt>;
1019
- contract: TaskContract;
1020
- contractPath: string;
1021
- }): string | null {
1022
- if (!input.classification.needsClarification) return null;
1023
- return [
1024
- "EvoDev advisory: clarification may be needed before broad changes.",
1025
- `Task Contract: ${input.contractPath}`,
1026
- `Suggested mode: ${input.contract.route.mode ?? "unknown"}`,
1027
- `Suggested workflow: ${input.contract.route.workflowId ?? "none"}`,
1028
- `Reason: ${input.contract.route.rationale}`,
1029
- ].join(" ");
1030
- }
1031
-
1032
- function createHookTaskContract(
1033
- input: HandleHookRuntimeInput,
1034
- classification: ReturnType<typeof classifyUserPrompt>,
1035
- ): TaskContract {
1036
- const sessionKey = resolveHookSessionKey(input.rawPayload);
1037
- const targetName = formatHookTargetName(input.target);
1038
- const contract = createTaskContract({
1039
- title: `${targetName} hook task ${sessionKey}`,
1040
- summary: `${targetName} prompt classified as ${classification.kind}; raw prompt is not stored by EvoDev.`,
1041
- projectId: null,
1042
- });
1043
-
1044
- return {
1045
- ...contract,
1046
- currentState: {
1047
- summary: `UserPromptSubmit received through ${targetName} hooks; raw prompt omitted from Task Contract.`,
1048
- evidenceRefs: [],
1049
- },
1050
- targetState: {
1051
- summary: `Complete the ${classification.kind} task through EvoDev-controlled workflow.`,
1052
- nonGoals: ["Do not store raw prompts, transcripts, source content, secrets, or raw output."],
1053
- constraints: [
1054
- `prompt-kind:${classification.kind}`,
1055
- ...classification.riskTerms.map((term) => `risk:${term}`),
1056
- ],
1057
- },
1058
- scope: {
1059
- ...contract.scope,
1060
- requiresUserConfirmation: classification.riskTerms,
1061
- },
1062
- context: {
1063
- ...contract.context,
1064
- assumptions: [
1065
- `hook-session:${sessionKey}`,
1066
- `cwd:${optionalPayloadString(input.rawPayload.cwd) ?? "unknown"}`,
1067
- ],
1068
- openQuestions: classification.needsClarification
1069
- ? ["User request may need clarification before broad changes."]
1070
- : [],
1071
- },
1072
- };
1073
- }
1074
-
1075
- function formatHookTargetName(target: CodeAgentHookTarget): string {
1076
- return target === "codex" ? "Codex" : "Claude";
1077
- }
1078
-
1079
- function classifyUserPrompt(value: unknown): {
1080
- kind: string;
1081
- riskTerms: string[];
1082
- needsClarification: boolean;
1083
- } {
1084
- const text = typeof value === "string" ? value.toLowerCase() : "";
1085
- const riskTerms = [
1086
- "security",
1087
- "release",
1088
- "publish",
1089
- "hook",
1090
- "memory",
1091
- "learning",
1092
- "secret",
1093
- "privacy",
1094
- ].filter((term) => text.includes(term));
1095
- let kind = "feature";
1096
- if (/\bbug|fix|error|failed|failure\b/.test(text)) kind = "bugfix";
1097
- if (/\brefactor|migration|migrate\b/.test(text)) kind = "refactor";
1098
- if (/\breview|audit\b/.test(text)) kind = "review";
1099
- if (/\btest|coverage\b/.test(text)) kind = "test";
1100
- if (/\bdoc|readme|guide\b/.test(text)) kind = "docs";
1101
- if (riskTerms.includes("security") || riskTerms.includes("privacy")) kind = "security";
1102
- if (riskTerms.includes("release") || riskTerms.includes("publish")) kind = "release";
1103
- return {
1104
- kind,
1105
- riskTerms,
1106
- needsClarification: text.trim().length < 12 || /\bmaybe|unclear|not sure\b/.test(text),
1107
- };
1108
- }
1109
-
1110
- async function readActiveContract(
1111
- homeDir: string,
1112
- payload: Record<string, unknown>,
1113
- ): Promise<TaskContract | null> {
1114
- const binding = await readSessionBinding(homeDir, payload);
1115
- if (binding?.contractPath === null || binding?.contractPath === undefined) return null;
1116
- try {
1117
- return JSON.parse(await readFile(binding.contractPath, "utf8")) as TaskContract;
1118
- } catch (error) {
1119
- if (isNotFoundError(error)) return null;
1120
- throw error;
1121
- }
1122
- }
1123
-
1124
1212
  async function readSessionBinding(
1125
1213
  homeDir: string,
1126
1214
  payload: Record<string, unknown>,
@@ -1139,7 +1227,7 @@ function resolveHookSessionKey(payload: Record<string, unknown>): string {
1139
1227
  const sessionId = optionalPayloadString(payload.session_id ?? payload.sessionId);
1140
1228
  const cwd = optionalPayloadString(payload.cwd);
1141
1229
  const source = sessionId ?? cwd ?? "local";
1142
- return `session-${createHash("sha256").update(source).digest("hex").slice(0, 16)}`;
1230
+ return `session-${sha256Short(source)}`;
1143
1231
  }
1144
1232
 
1145
1233
  async function writeJsonFile(path: string, value: unknown): Promise<void> {
@@ -1155,10 +1243,6 @@ function safeDiagnosticId(value: string): string {
1155
1243
  return value.replace(/[^A-Za-z0-9._-]/g, "-").slice(0, 120) || "unknown";
1156
1244
  }
1157
1245
 
1158
- function optionalPayloadNumber(value: unknown): number | null {
1159
- return typeof value === "number" && Number.isFinite(value) ? value : null;
1160
- }
1161
-
1162
1246
  function isNotFoundError(error: unknown): boolean {
1163
1247
  return (
1164
1248
  error instanceof Error && "code" in error && (error as NodeJS.ErrnoException).code === "ENOENT"
@@ -1297,7 +1381,7 @@ function stableEventId(
1297
1381
 
1298
1382
  function hashOptionalIdentifier(value: unknown): string | null {
1299
1383
  if (typeof value !== "string" || value.length === 0) return null;
1300
- return `sha256-${createHash("sha256").update(value).digest("hex").slice(0, 16)}`;
1384
+ return `sha256-${sha256Short(value)}`;
1301
1385
  }
1302
1386
 
1303
1387
  function optionalBoolean(value: unknown, fallback: boolean, path: string): boolean {
package/src/index.ts CHANGED
@@ -4,17 +4,28 @@ export * from "./code-agent-traces/index.ts";
4
4
  export * from "./config/index.ts";
5
5
  export * from "./daemon/index.ts";
6
6
  export * from "./evolution/index.ts";
7
+ export * as evolutionCandidates from "./evolution/candidates/index.ts";
8
+ export * as evolutionControl from "./evolution/control/index.ts";
9
+ export * as evolutionEvidence from "./evolution/evidence/index.ts";
10
+ export * from "./evolution/evidence/session-memory/index.ts";
11
+ export * from "./evolution/knowledge/index.ts";
12
+ export * as evolutionKnowledge from "./evolution/knowledge/index.ts";
13
+ export * from "./evolution/knowledge/changes.ts";
14
+ export * from "./evolution/knowledge/change-store.ts";
15
+ export * from "./evolution/knowledge/freshness.ts";
16
+ export * from "./evolution/knowledge/review.ts";
17
+ export * as evolutionProcessor from "./evolution/processor/index.ts";
18
+ export * from "./evolution/review/index.ts";
19
+ export * as evolutionReview from "./evolution/review/index.ts";
20
+ export * as evolutionTriggers from "./evolution/triggers/index.ts";
7
21
  export * from "./hooks/index.ts";
8
- export * from "./knowledge/index.ts";
9
- export * from "./learning/index.ts";
10
22
  export * from "./observability/index.ts";
11
23
  export * from "./pack/index.ts";
12
24
  export * from "./plugins/index.ts";
13
- export * from "./project/index.ts";
14
25
  export * from "./protected-zones/index.ts";
26
+ export * from "./projects/index.ts";
15
27
  export * from "./runtime-logs/index.ts";
16
28
  export * from "./sync/index.ts";
17
- export * from "./task/index.ts";
18
29
  export * from "./team/index.ts";
19
30
  export * from "./team/mcp.ts";
20
31
  export * from "./workflow/index.ts";