@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.
- package/assets/skills/coding/knowledge-distillation/SKILL.md +5 -3
- package/assets/team/agents/code-reviewer.md +48 -0
- package/assets/team/agents/docs-maintainer.md +51 -0
- package/assets/team/agents/implementation-engineer.md +51 -0
- package/assets/team/agents/product-scope-analyst.md +58 -0
- package/assets/team/agents/release-engineer.md +55 -0
- package/assets/team/agents/security-boundary-reviewer.md +50 -0
- package/assets/team/agents/solution-architect.md +51 -0
- package/assets/team/agents/verification-engineer.md +51 -0
- package/assets/team/team.md +102 -0
- package/dist/assets/index.js +5 -5
- package/dist/config/index.js +793 -241
- package/dist/index.js +20840 -12908
- package/dist/plugins/index.js +13 -13
- package/package.json +1 -1
- package/src/agents/index.ts +1 -265
- package/src/code-agent-traces/index.ts +11 -12
- package/src/config/index.ts +2 -0
- package/src/config/settings.ts +116 -7
- package/src/config/store.ts +1 -1
- package/src/daemon/index.ts +1 -41
- package/src/evolution/candidates/index.ts +730 -0
- package/src/evolution/control/index.ts +20 -0
- package/src/evolution/evidence/analysis.ts +533 -0
- package/src/evolution/evidence/index.ts +3 -0
- package/src/evolution/evidence/session-memory/analysis.ts +287 -0
- package/src/evolution/evidence/session-memory/constants.ts +9 -0
- package/src/evolution/evidence/session-memory/index.ts +9 -0
- package/src/evolution/evidence/session-memory/paths.ts +29 -0
- package/src/evolution/evidence/session-memory/policy.ts +39 -0
- package/src/evolution/evidence/session-memory/retention.ts +643 -0
- package/src/evolution/evidence/session-memory/segment.ts +216 -0
- package/src/evolution/evidence/session-memory/semantic-packet.ts +408 -0
- package/src/evolution/evidence/session-memory/sensitivity.ts +335 -0
- package/src/evolution/evidence/session-memory/state-machine.ts +249 -0
- package/src/evolution/evidence/session-memory/storage.ts +744 -0
- package/src/evolution/evidence/session-memory/types.ts +296 -0
- package/src/evolution/evidence/session-memory/updater.ts +199 -0
- package/src/evolution/formatters.ts +169 -0
- package/src/evolution/imports/apply.ts +435 -0
- package/src/evolution/imports/diff.ts +472 -0
- package/src/evolution/imports/index.ts +7 -0
- package/src/evolution/imports/materialize.ts +640 -0
- package/src/evolution/imports/paths.ts +129 -0
- package/src/evolution/imports/stage.ts +414 -0
- package/src/evolution/imports/storage.ts +952 -0
- package/src/evolution/imports/types.ts +226 -0
- package/src/evolution/index.ts +19 -2827
- package/src/evolution/knowledge/change-store.ts +558 -0
- package/src/evolution/knowledge/changes.ts +459 -0
- package/src/evolution/knowledge/freshness.ts +69 -0
- package/src/{knowledge → evolution/knowledge}/index.ts +1532 -206
- package/src/evolution/knowledge/review.ts +446 -0
- package/src/evolution/knowledge/support.ts +135 -0
- package/src/evolution/paths.ts +44 -0
- package/src/evolution/processor/distillation.ts +518 -0
- package/src/evolution/processor/index.ts +3 -0
- package/src/evolution/processor/process.ts +594 -0
- package/src/{learning → evolution/review}/index.ts +10 -14
- package/src/evolution/schema.ts +639 -0
- package/src/evolution/shared.ts +1053 -0
- package/src/evolution/triggers/classification.ts +102 -0
- package/src/evolution/triggers/index.ts +295 -0
- package/src/hooks/index.ts +281 -197
- package/src/index.ts +15 -4
- package/src/projects/index.ts +934 -0
- package/src/runtime-logs/index.ts +100 -13
- package/src/team/index.ts +582 -3
- package/src/utils/errors.ts +13 -0
- package/src/utils/fs.ts +40 -0
- package/src/utils/hash.ts +9 -0
- package/src/utils/ids.ts +12 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/parsing.ts +11 -0
- package/src/utils/text.ts +18 -0
- package/src/utils/time.ts +5 -0
- package/src/workflow/index.ts +3 -21
- package/src/project/index.ts +0 -507
- package/src/task/index.ts +0 -840
package/src/hooks/index.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
} from "../
|
|
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
|
|
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
|
|
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
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
812
|
-
|
|
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;
|
|
828
|
-
stateWrites: [paths.
|
|
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
|
-
|
|
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
|
|
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-${
|
|
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-${
|
|
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";
|