openhermes 4.12.1 → 4.13.0

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 (73) hide show
  1. package/CONTEXT.md +6 -6
  2. package/ETHOS.md +2 -2
  3. package/README.md +11 -17
  4. package/bootstrap.ts +118 -126
  5. package/docs/HOW-IT-WORKS.md +162 -0
  6. package/docs/adr/ADR-0001-rebuild-vs-increment.md +30 -0
  7. package/docs/adr/ADR-0002-routing-graph-vs-linear-chain.md +36 -0
  8. package/docs/adr/ADR-0003-per-directory-plan-storage.md +34 -0
  9. package/docs/adr/ADR-0004-composer-fragment-architecture.md +42 -0
  10. package/docs/adr/ADR-0005-hook-system-design.md +42 -0
  11. package/docs/adr/README.md +9 -0
  12. package/harness/codex/AUTOPILOT.md +35 -40
  13. package/harness/codex/CHARTER.md +3 -3
  14. package/harness/lib/composer/compose.test.ts +29 -29
  15. package/harness/lib/composer/fragments/02-delegation.md +5 -5
  16. package/harness/lib/composer/fragments/04-task-flow.md +13 -13
  17. package/harness/lib/composer/fragments/08-routing.md +1 -1
  18. package/harness/lib/composer/fragments/09-guardrails.md +25 -25
  19. package/harness/lib/composer/index.ts +1 -1
  20. package/harness/lib/guards/guard-config.ts +72 -72
  21. package/harness/lib/hooks/builtins/confidence-gate-hook.ts +9 -9
  22. package/harness/lib/hooks/builtins/delegation-depth-hook.ts +1 -1
  23. package/harness/lib/hooks/builtins/dynamic-route-hook.ts +99 -99
  24. package/harness/lib/hooks/builtins/next-route-hook.ts +24 -24
  25. package/harness/lib/hooks/builtins/plan-check-hook.ts +5 -5
  26. package/harness/lib/hooks/builtins/route-tracking-hook.ts +1 -1
  27. package/harness/lib/hooks/hooks.test.ts +160 -324
  28. package/harness/lib/hooks/index.ts +38 -42
  29. package/harness/lib/hooks/registry.ts +309 -416
  30. package/harness/lib/hooks/types.ts +116 -119
  31. package/harness/lib/plans/plan-location.ts +134 -134
  32. package/harness/lib/routing/index.ts +21 -21
  33. package/harness/lib/routing/route-guidance.ts +147 -147
  34. package/harness/lib/routing/route-resolver.ts +58 -58
  35. package/harness/lib/routing/routing.test.ts +195 -195
  36. package/harness/lib/routing/skill-frontmatter.ts +125 -125
  37. package/harness/lib/routing/types.ts +52 -52
  38. package/harness/skills/oh-ascii/SKILL.md +1 -1
  39. package/harness/skills/oh-fusion/DEEP.md +109 -109
  40. package/harness/skills/oh-fusion/SKILL.md +47 -47
  41. package/harness/skills/oh-init/DEEP.md +2 -2
  42. package/harness/skills/oh-plan-review/DEEP.md +1 -1
  43. package/harness/skills/oh-planner/DEEP.md +3 -3
  44. package/harness/skills/oh-review/DEEP.md +5 -5
  45. package/package.json +56 -53
  46. package/harness/lib/background/background.test.ts +0 -216
  47. package/harness/lib/background/index.ts +0 -7
  48. package/harness/lib/background/interfaces.ts +0 -31
  49. package/harness/lib/background/manager.ts +0 -320
  50. package/harness/lib/hooks/builtins/error-recovery-hook.ts +0 -107
  51. package/harness/lib/hooks/builtins/memory-sync-hook.ts +0 -73
  52. package/harness/lib/hooks/builtins/sanity-check-hook.ts +0 -52
  53. package/harness/lib/hooks/builtins/subagent-failure-hook.ts +0 -93
  54. package/harness/lib/memory/index.ts +0 -18
  55. package/harness/lib/memory/interfaces.ts +0 -53
  56. package/harness/lib/memory/memory-manager.ts +0 -205
  57. package/harness/lib/memory/memory.test.ts +0 -485
  58. package/harness/lib/memory/plan-store.ts +0 -346
  59. package/harness/lib/recovery/handler.ts +0 -243
  60. package/harness/lib/recovery/index.ts +0 -14
  61. package/harness/lib/recovery/interfaces.ts +0 -48
  62. package/harness/lib/recovery/patterns.ts +0 -149
  63. package/harness/lib/recovery/recovery.test.ts +0 -312
  64. package/harness/lib/sanity/anomaly-tracker.ts +0 -127
  65. package/harness/lib/sanity/checker.ts +0 -189
  66. package/harness/lib/sanity/index.ts +0 -13
  67. package/harness/lib/sanity/interfaces.ts +0 -24
  68. package/harness/lib/sanity/sanity.test.ts +0 -472
  69. package/harness/lib/sync/file-watcher.ts +0 -175
  70. package/harness/lib/sync/index.ts +0 -11
  71. package/harness/lib/sync/interfaces.ts +0 -27
  72. package/harness/lib/sync/plan-sync.ts +0 -533
  73. package/harness/lib/sync/sync.test.ts +0 -858
@@ -1,147 +1,147 @@
1
- import {
2
- ROUTE_ACTIONS,
3
- ROUTE_OUTCOMES,
4
- ROUTE_VERIFICATIONS,
5
- ROUTE_WORK_TYPES,
6
- } from "./types.ts";
7
- import type {
8
- RouteAction,
9
- RouteOutcome,
10
- RouteResolution,
11
- RouteVerification,
12
- RouteWork,
13
- RuntimeRouteDecision,
14
- } from "./types.ts";
15
-
16
- export const ROUTE_GUIDANCE_PREFIX = "ROUTE_GUIDANCE:";
17
- export const NEXT_ROUTE_PREFIX = "NEXT_ROUTE:";
18
-
19
- interface ConsumedRouteGuidance {
20
- output: string;
21
- selected: string | null;
22
- }
23
-
24
- const runtimeRouteState = new Map<string, RuntimeRouteDecision>();
25
-
26
- function isRouteOutcome(value: unknown): value is RouteOutcome {
27
- return typeof value === "string" && ROUTE_OUTCOMES.includes(value as RouteOutcome);
28
- }
29
-
30
- function isRouteVerification(value: unknown): value is RouteVerification {
31
- return typeof value === "string" && ROUTE_VERIFICATIONS.includes(value as RouteVerification);
32
- }
33
-
34
- function isRouteAction(value: unknown): value is RouteAction {
35
- return typeof value === "string" && ROUTE_ACTIONS.includes(value as RouteAction);
36
- }
37
-
38
- function isRouteWork(value: unknown): value is RouteWork {
39
- return typeof value === "string" && ROUTE_WORK_TYPES.includes(value as RouteWork);
40
- }
41
-
42
- export function extractRouteGuidance(output: string): RouteResolution | null {
43
- const guidanceLine = output
44
- .split(/\r?\n/)
45
- .map((line) => line.trim())
46
- .find((line) => line.startsWith(ROUTE_GUIDANCE_PREFIX));
47
-
48
- if (!guidanceLine) return null;
49
-
50
- const raw = guidanceLine.slice(ROUTE_GUIDANCE_PREFIX.length).trim();
51
- if (!raw) return null;
52
-
53
- try {
54
- const parsed = JSON.parse(raw) as Partial<RouteResolution>;
55
- if (!isRouteOutcome(parsed.outcome)) return null;
56
- if (parsed.verification !== undefined && !isRouteVerification(parsed.verification)) return null;
57
- if (parsed.action !== undefined && !isRouteAction(parsed.action)) return null;
58
- if (parsed.work !== undefined && !isRouteWork(parsed.work)) return null;
59
- if (!Array.isArray(parsed.candidates) || !parsed.candidates.every((candidate) => typeof candidate === "string")) {
60
- return null;
61
- }
62
- if (parsed.selected !== null && parsed.selected !== undefined && typeof parsed.selected !== "string") {
63
- return null;
64
- }
65
- if (typeof parsed.reason !== "string") return null;
66
-
67
- return {
68
- outcome: parsed.outcome,
69
- ...(parsed.verification ? { verification: parsed.verification } : {}),
70
- ...(parsed.action ? { action: parsed.action } : {}),
71
- ...(parsed.work ? { work: parsed.work } : {}),
72
- candidates: parsed.candidates,
73
- selected: parsed.selected ?? null,
74
- reason: parsed.reason,
75
- };
76
- } catch {
77
- return null;
78
- }
79
- }
80
-
81
- export function consumeRouteGuidance(output: string): ConsumedRouteGuidance {
82
- const guidance = extractRouteGuidance(output);
83
- if (!guidance?.selected || output.includes(NEXT_ROUTE_PREFIX)) {
84
- return { output, selected: guidance?.selected ?? null };
85
- }
86
-
87
- return {
88
- output: `${output.trimEnd()}\n${NEXT_ROUTE_PREFIX} ${guidance.selected}`,
89
- selected: guidance.selected,
90
- };
91
- }
92
-
93
- function extractExplicitNextRoute(output: string): string | null {
94
- const routeLine = output
95
- .split(/\r?\n/)
96
- .map((line) => line.trim())
97
- .find((line) => line.startsWith(NEXT_ROUTE_PREFIX));
98
-
99
- if (!routeLine) return null;
100
-
101
- const raw = routeLine.slice(NEXT_ROUTE_PREFIX.length).trim();
102
- if (!raw || /\s/.test(raw)) return null;
103
- return raw;
104
- }
105
-
106
- export function extractRuntimeRouteDecision(output: string): RuntimeRouteDecision | null {
107
- const explicitNextRoute = extractExplicitNextRoute(output);
108
- if (explicitNextRoute) {
109
- return {
110
- selected: explicitNextRoute,
111
- source: "next_route",
112
- };
113
- }
114
-
115
- const guidance = extractRouteGuidance(output);
116
- if (!guidance?.selected) return null;
117
-
118
- return {
119
- selected: guidance.selected,
120
- source: "route_guidance",
121
- outcome: guidance.outcome,
122
- verification: guidance.verification,
123
- action: guidance.action,
124
- work: guidance.work,
125
- candidates: guidance.candidates,
126
- reason: guidance.reason,
127
- };
128
- }
129
-
130
- export function rememberRuntimeRouteDecision(sessionId: string, output: string): RuntimeRouteDecision | null {
131
- const decision = extractRuntimeRouteDecision(output);
132
- if (!decision) {
133
- runtimeRouteState.delete(sessionId);
134
- return null;
135
- }
136
-
137
- runtimeRouteState.set(sessionId, decision);
138
- return decision;
139
- }
140
-
141
- export function getRuntimeRouteDecision(sessionId: string): RuntimeRouteDecision | null {
142
- return runtimeRouteState.get(sessionId) ?? null;
143
- }
144
-
145
- export function clearRuntimeRouteDecision(sessionId: string): void {
146
- runtimeRouteState.delete(sessionId);
147
- }
1
+ import {
2
+ ROUTE_ACTIONS,
3
+ ROUTE_OUTCOMES,
4
+ ROUTE_VERIFICATIONS,
5
+ ROUTE_WORK_TYPES,
6
+ } from "./types.ts";
7
+ import type {
8
+ RouteAction,
9
+ RouteOutcome,
10
+ RouteResolution,
11
+ RouteVerification,
12
+ RouteWork,
13
+ RuntimeRouteDecision,
14
+ } from "./types.ts";
15
+
16
+ export const ROUTE_GUIDANCE_PREFIX = "ROUTE_GUIDANCE:";
17
+ export const NEXT_ROUTE_PREFIX = "NEXT_ROUTE:";
18
+
19
+ interface ConsumedRouteGuidance {
20
+ output: string;
21
+ selected: string | null;
22
+ }
23
+
24
+ const runtimeRouteState = new Map<string, RuntimeRouteDecision>();
25
+
26
+ function isRouteOutcome(value: unknown): value is RouteOutcome {
27
+ return typeof value === "string" && ROUTE_OUTCOMES.includes(value as RouteOutcome);
28
+ }
29
+
30
+ function isRouteVerification(value: unknown): value is RouteVerification {
31
+ return typeof value === "string" && ROUTE_VERIFICATIONS.includes(value as RouteVerification);
32
+ }
33
+
34
+ function isRouteAction(value: unknown): value is RouteAction {
35
+ return typeof value === "string" && ROUTE_ACTIONS.includes(value as RouteAction);
36
+ }
37
+
38
+ function isRouteWork(value: unknown): value is RouteWork {
39
+ return typeof value === "string" && ROUTE_WORK_TYPES.includes(value as RouteWork);
40
+ }
41
+
42
+ export function extractRouteGuidance(output: string): RouteResolution | null {
43
+ const guidanceLine = output
44
+ .split(/\r?\n/)
45
+ .map((line) => line.trim())
46
+ .find((line) => line.startsWith(ROUTE_GUIDANCE_PREFIX));
47
+
48
+ if (!guidanceLine) return null;
49
+
50
+ const raw = guidanceLine.slice(ROUTE_GUIDANCE_PREFIX.length).trim();
51
+ if (!raw) return null;
52
+
53
+ try {
54
+ const parsed = JSON.parse(raw) as Partial<RouteResolution>;
55
+ if (!isRouteOutcome(parsed.outcome)) return null;
56
+ if (parsed.verification !== undefined && !isRouteVerification(parsed.verification)) return null;
57
+ if (parsed.action !== undefined && !isRouteAction(parsed.action)) return null;
58
+ if (parsed.work !== undefined && !isRouteWork(parsed.work)) return null;
59
+ if (!Array.isArray(parsed.candidates) || !parsed.candidates.every((candidate) => typeof candidate === "string")) {
60
+ return null;
61
+ }
62
+ if (parsed.selected !== null && parsed.selected !== undefined && typeof parsed.selected !== "string") {
63
+ return null;
64
+ }
65
+ if (typeof parsed.reason !== "string") return null;
66
+
67
+ return {
68
+ outcome: parsed.outcome,
69
+ ...(parsed.verification ? { verification: parsed.verification } : {}),
70
+ ...(parsed.action ? { action: parsed.action } : {}),
71
+ ...(parsed.work ? { work: parsed.work } : {}),
72
+ candidates: parsed.candidates,
73
+ selected: parsed.selected ?? null,
74
+ reason: parsed.reason,
75
+ };
76
+ } catch {
77
+ return null;
78
+ }
79
+ }
80
+
81
+ export function consumeRouteGuidance(output: string): ConsumedRouteGuidance {
82
+ const guidance = extractRouteGuidance(output);
83
+ if (!guidance?.selected || output.includes(NEXT_ROUTE_PREFIX)) {
84
+ return { output, selected: guidance?.selected ?? null };
85
+ }
86
+
87
+ return {
88
+ output: `${output.trimEnd()}\n${NEXT_ROUTE_PREFIX} ${guidance.selected}`,
89
+ selected: guidance.selected,
90
+ };
91
+ }
92
+
93
+ function extractExplicitNextRoute(output: string): string | null {
94
+ const routeLine = output
95
+ .split(/\r?\n/)
96
+ .map((line) => line.trim())
97
+ .find((line) => line.startsWith(NEXT_ROUTE_PREFIX));
98
+
99
+ if (!routeLine) return null;
100
+
101
+ const raw = routeLine.slice(NEXT_ROUTE_PREFIX.length).trim();
102
+ if (!raw || /\s/.test(raw)) return null;
103
+ return raw;
104
+ }
105
+
106
+ export function extractRuntimeRouteDecision(output: string): RuntimeRouteDecision | null {
107
+ const explicitNextRoute = extractExplicitNextRoute(output);
108
+ if (explicitNextRoute) {
109
+ return {
110
+ selected: explicitNextRoute,
111
+ source: "next_route",
112
+ };
113
+ }
114
+
115
+ const guidance = extractRouteGuidance(output);
116
+ if (!guidance?.selected) return null;
117
+
118
+ return {
119
+ selected: guidance.selected,
120
+ source: "route_guidance",
121
+ outcome: guidance.outcome,
122
+ verification: guidance.verification,
123
+ action: guidance.action,
124
+ work: guidance.work,
125
+ candidates: guidance.candidates,
126
+ reason: guidance.reason,
127
+ };
128
+ }
129
+
130
+ export function rememberRuntimeRouteDecision(sessionId: string, output: string): RuntimeRouteDecision | null {
131
+ const decision = extractRuntimeRouteDecision(output);
132
+ if (!decision) {
133
+ runtimeRouteState.delete(sessionId);
134
+ return null;
135
+ }
136
+
137
+ runtimeRouteState.set(sessionId, decision);
138
+ return decision;
139
+ }
140
+
141
+ export function getRuntimeRouteDecision(sessionId: string): RuntimeRouteDecision | null {
142
+ return runtimeRouteState.get(sessionId) ?? null;
143
+ }
144
+
145
+ export function clearRuntimeRouteDecision(sessionId: string): void {
146
+ runtimeRouteState.delete(sessionId);
147
+ }
@@ -1,58 +1,58 @@
1
- import type { RouteEvidence, RouteResolution, SkillRouteMap } from "./types.ts";
2
-
3
- function findCandidate(candidates: string[], fragment: string): string | null {
4
- return candidates.find((candidate) => candidate.includes(fragment)) ?? null;
5
- }
6
-
7
- function buildResolution(evidence: RouteEvidence, candidates: string[], selected: string | null, reason: string): RouteResolution {
8
- return {
9
- outcome: evidence.outcome,
10
- ...(evidence.verification ? { verification: evidence.verification } : {}),
11
- ...(evidence.action ? { action: evidence.action } : {}),
12
- ...(evidence.work ? { work: evidence.work } : {}),
13
- candidates,
14
- selected,
15
- reason,
16
- };
17
- }
18
-
19
- export function resolveRoute(routeMap: SkillRouteMap, evidence: RouteEvidence): RouteResolution {
20
- const candidates = [...routeMap[evidence.outcome]];
21
- if (candidates.length === 0) {
22
- return buildResolution(evidence, candidates, null, `No route candidates declared for outcome \"${evidence.outcome}\".`);
23
- }
24
-
25
- if (evidence.target && candidates.includes(evidence.target)) {
26
- return buildResolution(evidence, candidates, evidence.target, `Selected \"${evidence.target}\" from output evidence.`);
27
- }
28
-
29
- if (evidence.action === "fixable" || evidence.work === "implement") {
30
- const builderCandidate = findCandidate(candidates, "builder");
31
- if (builderCandidate) {
32
- return buildResolution(evidence, candidates, builderCandidate, `Selected \"${builderCandidate}\" for fixable implementation work.`);
33
- }
34
- }
35
-
36
- if (evidence.verification === "unverified") {
37
- const gauntletCandidate = findCandidate(candidates, "gauntlet");
38
- if (gauntletCandidate) {
39
- return buildResolution(evidence, candidates, gauntletCandidate, `Selected \"${gauntletCandidate}\" because work is still unverified.`);
40
- }
41
- }
42
-
43
- if (evidence.work === "verify") {
44
- const gauntletCandidate = findCandidate(candidates, "gauntlet");
45
- if (gauntletCandidate) {
46
- return buildResolution(evidence, candidates, gauntletCandidate, `Selected \"${gauntletCandidate}\" for verification work.`);
47
- }
48
- }
49
-
50
- if (evidence.work === "ship" && evidence.verification === "verified" && evidence.action === "done") {
51
- const shipCandidate = findCandidate(candidates, "ship");
52
- if (shipCandidate) {
53
- return buildResolution(evidence, candidates, shipCandidate, `Selected \"${shipCandidate}\" for verified ship-ready work.`);
54
- }
55
- }
56
-
57
- return buildResolution(evidence, candidates, candidates[0], `Selected first declared route for outcome \"${evidence.outcome}\".`);
58
- }
1
+ import type { RouteEvidence, RouteResolution, SkillRouteMap } from "./types.ts";
2
+
3
+ function findCandidate(candidates: string[], fragment: string): string | null {
4
+ return candidates.find((candidate) => candidate.includes(fragment)) ?? null;
5
+ }
6
+
7
+ function buildResolution(evidence: RouteEvidence, candidates: string[], selected: string | null, reason: string): RouteResolution {
8
+ return {
9
+ outcome: evidence.outcome,
10
+ ...(evidence.verification ? { verification: evidence.verification } : {}),
11
+ ...(evidence.action ? { action: evidence.action } : {}),
12
+ ...(evidence.work ? { work: evidence.work } : {}),
13
+ candidates,
14
+ selected,
15
+ reason,
16
+ };
17
+ }
18
+
19
+ export function resolveRoute(routeMap: SkillRouteMap, evidence: RouteEvidence): RouteResolution {
20
+ const candidates = [...routeMap[evidence.outcome]];
21
+ if (candidates.length === 0) {
22
+ return buildResolution(evidence, candidates, null, `No route candidates declared for outcome \"${evidence.outcome}\".`);
23
+ }
24
+
25
+ if (evidence.target && candidates.includes(evidence.target)) {
26
+ return buildResolution(evidence, candidates, evidence.target, `Selected \"${evidence.target}\" from output evidence.`);
27
+ }
28
+
29
+ if (evidence.action === "fixable" || evidence.work === "implement") {
30
+ const builderCandidate = findCandidate(candidates, "builder");
31
+ if (builderCandidate) {
32
+ return buildResolution(evidence, candidates, builderCandidate, `Selected \"${builderCandidate}\" for fixable implementation work.`);
33
+ }
34
+ }
35
+
36
+ if (evidence.verification === "unverified") {
37
+ const gauntletCandidate = findCandidate(candidates, "gauntlet");
38
+ if (gauntletCandidate) {
39
+ return buildResolution(evidence, candidates, gauntletCandidate, `Selected \"${gauntletCandidate}\" because work is still unverified.`);
40
+ }
41
+ }
42
+
43
+ if (evidence.work === "verify") {
44
+ const gauntletCandidate = findCandidate(candidates, "gauntlet");
45
+ if (gauntletCandidate) {
46
+ return buildResolution(evidence, candidates, gauntletCandidate, `Selected \"${gauntletCandidate}\" for verification work.`);
47
+ }
48
+ }
49
+
50
+ if (evidence.work === "ship" && evidence.verification === "verified" && evidence.action === "done") {
51
+ const shipCandidate = findCandidate(candidates, "ship");
52
+ if (shipCandidate) {
53
+ return buildResolution(evidence, candidates, shipCandidate, `Selected \"${shipCandidate}\" for verified ship-ready work.`);
54
+ }
55
+ }
56
+
57
+ return buildResolution(evidence, candidates, candidates[0], `Selected first declared route for outcome \"${evidence.outcome}\".`);
58
+ }