@evo-dev/core 0.0.1-alpha.19 → 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.
@@ -8,13 +8,14 @@ import {
8
8
  createScopedKnowledgeContextPack,
9
9
  formatScopedKnowledgePromptBlock,
10
10
  hasContextInjectionReceipt,
11
+ recordContextInjectionOutcome,
11
12
  writeContextInjectionReceipt,
12
13
  } from "../evolution/knowledge/index.ts";
13
14
  import {
14
15
  enqueueEvolutionTrigger,
15
16
  resolveEvolutionTriggerDecision,
16
17
  } from "../evolution/triggers/index.ts";
17
- import { recordDiscoveredProject } from "../projects/index.ts";
18
+ import { recordDiscoveredProject, resolveProjectWorkspaceFromCwd } from "../projects/index.ts";
18
19
  import { resolveTraceSessionKey, resolveTraceTeamContext } from "../runtime-logs/index.ts";
19
20
  import {
20
21
  type TeamMessageRecord,
@@ -156,6 +157,7 @@ export interface HookRuntimeSessionBinding {
156
157
  sessionKey: string;
157
158
  cwd: string | null;
158
159
  teamRuntimeContextDeliveredAt?: string | null;
160
+ knowledgeContextDeliveredAt?: string | null;
159
161
  updatedAt: string;
160
162
  }
161
163
 
@@ -488,26 +490,20 @@ export async function handleHookRuntime(input: HandleHookRuntimeInput): Promise<
488
490
  : await recordEvolutionTriggerFromHook(input);
489
491
  const messageDiagnostics = await deliverPendingTeamMessagesFromHook(input, result);
490
492
  const scopedContextDiagnostics = await injectScopedKnowledgeContextFromHook(input, result);
493
+ const knowledgeOutcomeDiagnostics = await recordKnowledgeOutcomeFromHook(input);
491
494
  const teamStateDiagnostics = await recordTeamAgentHookStateFromHook(input);
492
- const completed = appendRuntimeDiagnostics(
493
- appendRuntimeDiagnostics(
494
- appendRuntimeDiagnostics(
495
- appendRuntimeDiagnostics(
496
- appendRuntimeDiagnostics(
497
- appendRuntimeDiagnostics(
498
- appendRuntimeDiagnostics(result, diagnostics),
499
- projectDiagnostics,
500
- ),
501
- sessionMemoryDiagnostics,
502
- ),
503
- evolutionDiagnostics,
504
- ),
505
- messageDiagnostics,
506
- ),
507
- scopedContextDiagnostics,
508
- ),
495
+ let completed = appendRuntimeDiagnostics(result, diagnostics);
496
+ for (const entry of [
497
+ projectDiagnostics,
498
+ sessionMemoryDiagnostics,
499
+ evolutionDiagnostics,
500
+ messageDiagnostics,
501
+ scopedContextDiagnostics,
502
+ knowledgeOutcomeDiagnostics,
509
503
  teamStateDiagnostics,
510
- );
504
+ ]) {
505
+ completed = appendRuntimeDiagnostics(completed, entry);
506
+ }
511
507
  return appendDevelopmentHookDiagnostics(input, completed);
512
508
  }
513
509
 
@@ -765,7 +761,8 @@ async function injectScopedKnowledgeContextFromHook(
765
761
  environment: input.environment,
766
762
  payload: input.rawPayload,
767
763
  });
768
- 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: [] };
769
766
 
770
767
  try {
771
768
  if (input.runtimeInjectionEnabled === false) return { stateWrites: [], warnings: [] };
@@ -807,6 +804,112 @@ async function injectScopedKnowledgeContextFromHook(
807
804
  }
808
805
  }
809
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
+
810
913
  function canDeliverTeamMessagesFromHook(
811
914
  target: CodeAgentHookTarget,
812
915
  eventType: CanonicalHookEventType,
@@ -886,6 +989,7 @@ async function handleUserPromptSubmit(input: HandleHookRuntimeInput): Promise<Ho
886
989
  sessionKey,
887
990
  cwd: optionalPayloadString(input.rawPayload.cwd),
888
991
  teamRuntimeContextDeliveredAt,
992
+ knowledgeContextDeliveredAt: previousBinding?.knowledgeContextDeliveredAt ?? null,
889
993
  updatedAt: input.receivedAt ?? new Date().toISOString(),
890
994
  };
891
995