@soleri/core 2.1.0 → 2.4.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 (207) hide show
  1. package/dist/brain/brain.d.ts +3 -1
  2. package/dist/brain/brain.d.ts.map +1 -1
  3. package/dist/brain/brain.js +60 -4
  4. package/dist/brain/brain.js.map +1 -1
  5. package/dist/brain/intelligence.d.ts +36 -1
  6. package/dist/brain/intelligence.d.ts.map +1 -1
  7. package/dist/brain/intelligence.js +119 -14
  8. package/dist/brain/intelligence.js.map +1 -1
  9. package/dist/brain/types.d.ts +32 -0
  10. package/dist/brain/types.d.ts.map +1 -1
  11. package/dist/control/identity-manager.d.ts +22 -0
  12. package/dist/control/identity-manager.d.ts.map +1 -0
  13. package/dist/control/identity-manager.js +233 -0
  14. package/dist/control/identity-manager.js.map +1 -0
  15. package/dist/control/intent-router.d.ts +32 -0
  16. package/dist/control/intent-router.d.ts.map +1 -0
  17. package/dist/control/intent-router.js +242 -0
  18. package/dist/control/intent-router.js.map +1 -0
  19. package/dist/control/types.d.ts +68 -0
  20. package/dist/control/types.d.ts.map +1 -0
  21. package/dist/control/types.js +9 -0
  22. package/dist/control/types.js.map +1 -0
  23. package/dist/curator/curator.d.ts +29 -0
  24. package/dist/curator/curator.d.ts.map +1 -1
  25. package/dist/curator/curator.js +135 -0
  26. package/dist/curator/curator.js.map +1 -1
  27. package/dist/facades/types.d.ts +1 -1
  28. package/dist/governance/governance.d.ts +42 -0
  29. package/dist/governance/governance.d.ts.map +1 -0
  30. package/dist/governance/governance.js +488 -0
  31. package/dist/governance/governance.js.map +1 -0
  32. package/dist/governance/index.d.ts +3 -0
  33. package/dist/governance/index.d.ts.map +1 -0
  34. package/dist/governance/index.js +2 -0
  35. package/dist/governance/index.js.map +1 -0
  36. package/dist/governance/types.d.ts +102 -0
  37. package/dist/governance/types.d.ts.map +1 -0
  38. package/dist/governance/types.js +3 -0
  39. package/dist/governance/types.js.map +1 -0
  40. package/dist/index.d.ts +32 -3
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +29 -1
  43. package/dist/index.js.map +1 -1
  44. package/dist/logging/logger.d.ts +37 -0
  45. package/dist/logging/logger.d.ts.map +1 -0
  46. package/dist/logging/logger.js +145 -0
  47. package/dist/logging/logger.js.map +1 -0
  48. package/dist/logging/types.d.ts +19 -0
  49. package/dist/logging/types.d.ts.map +1 -0
  50. package/dist/logging/types.js +2 -0
  51. package/dist/logging/types.js.map +1 -0
  52. package/dist/loop/loop-manager.d.ts +49 -0
  53. package/dist/loop/loop-manager.d.ts.map +1 -0
  54. package/dist/loop/loop-manager.js +105 -0
  55. package/dist/loop/loop-manager.js.map +1 -0
  56. package/dist/loop/types.d.ts +35 -0
  57. package/dist/loop/types.d.ts.map +1 -0
  58. package/dist/loop/types.js +8 -0
  59. package/dist/loop/types.js.map +1 -0
  60. package/dist/planning/gap-analysis.d.ts +29 -0
  61. package/dist/planning/gap-analysis.d.ts.map +1 -0
  62. package/dist/planning/gap-analysis.js +265 -0
  63. package/dist/planning/gap-analysis.js.map +1 -0
  64. package/dist/planning/gap-types.d.ts +29 -0
  65. package/dist/planning/gap-types.d.ts.map +1 -0
  66. package/dist/planning/gap-types.js +28 -0
  67. package/dist/planning/gap-types.js.map +1 -0
  68. package/dist/planning/planner.d.ts +150 -1
  69. package/dist/planning/planner.d.ts.map +1 -1
  70. package/dist/planning/planner.js +365 -2
  71. package/dist/planning/planner.js.map +1 -1
  72. package/dist/project/project-registry.d.ts +79 -0
  73. package/dist/project/project-registry.d.ts.map +1 -0
  74. package/dist/project/project-registry.js +276 -0
  75. package/dist/project/project-registry.js.map +1 -0
  76. package/dist/project/types.d.ts +28 -0
  77. package/dist/project/types.d.ts.map +1 -0
  78. package/dist/project/types.js +5 -0
  79. package/dist/project/types.js.map +1 -0
  80. package/dist/runtime/admin-extra-ops.d.ts +13 -0
  81. package/dist/runtime/admin-extra-ops.d.ts.map +1 -0
  82. package/dist/runtime/admin-extra-ops.js +284 -0
  83. package/dist/runtime/admin-extra-ops.js.map +1 -0
  84. package/dist/runtime/admin-ops.d.ts +15 -0
  85. package/dist/runtime/admin-ops.d.ts.map +1 -0
  86. package/dist/runtime/admin-ops.js +322 -0
  87. package/dist/runtime/admin-ops.js.map +1 -0
  88. package/dist/runtime/capture-ops.d.ts +15 -0
  89. package/dist/runtime/capture-ops.d.ts.map +1 -0
  90. package/dist/runtime/capture-ops.js +345 -0
  91. package/dist/runtime/capture-ops.js.map +1 -0
  92. package/dist/runtime/core-ops.d.ts +7 -3
  93. package/dist/runtime/core-ops.d.ts.map +1 -1
  94. package/dist/runtime/core-ops.js +474 -8
  95. package/dist/runtime/core-ops.js.map +1 -1
  96. package/dist/runtime/curator-extra-ops.d.ts +9 -0
  97. package/dist/runtime/curator-extra-ops.d.ts.map +1 -0
  98. package/dist/runtime/curator-extra-ops.js +59 -0
  99. package/dist/runtime/curator-extra-ops.js.map +1 -0
  100. package/dist/runtime/domain-ops.d.ts.map +1 -1
  101. package/dist/runtime/domain-ops.js +59 -13
  102. package/dist/runtime/domain-ops.js.map +1 -1
  103. package/dist/runtime/grading-ops.d.ts +14 -0
  104. package/dist/runtime/grading-ops.d.ts.map +1 -0
  105. package/dist/runtime/grading-ops.js +105 -0
  106. package/dist/runtime/grading-ops.js.map +1 -0
  107. package/dist/runtime/loop-ops.d.ts +13 -0
  108. package/dist/runtime/loop-ops.d.ts.map +1 -0
  109. package/dist/runtime/loop-ops.js +179 -0
  110. package/dist/runtime/loop-ops.js.map +1 -0
  111. package/dist/runtime/memory-cross-project-ops.d.ts +12 -0
  112. package/dist/runtime/memory-cross-project-ops.d.ts.map +1 -0
  113. package/dist/runtime/memory-cross-project-ops.js +165 -0
  114. package/dist/runtime/memory-cross-project-ops.js.map +1 -0
  115. package/dist/runtime/memory-extra-ops.d.ts +13 -0
  116. package/dist/runtime/memory-extra-ops.d.ts.map +1 -0
  117. package/dist/runtime/memory-extra-ops.js +173 -0
  118. package/dist/runtime/memory-extra-ops.js.map +1 -0
  119. package/dist/runtime/orchestrate-ops.d.ts +17 -0
  120. package/dist/runtime/orchestrate-ops.d.ts.map +1 -0
  121. package/dist/runtime/orchestrate-ops.js +240 -0
  122. package/dist/runtime/orchestrate-ops.js.map +1 -0
  123. package/dist/runtime/planning-extra-ops.d.ts +17 -0
  124. package/dist/runtime/planning-extra-ops.d.ts.map +1 -0
  125. package/dist/runtime/planning-extra-ops.js +300 -0
  126. package/dist/runtime/planning-extra-ops.js.map +1 -0
  127. package/dist/runtime/project-ops.d.ts +15 -0
  128. package/dist/runtime/project-ops.d.ts.map +1 -0
  129. package/dist/runtime/project-ops.js +181 -0
  130. package/dist/runtime/project-ops.js.map +1 -0
  131. package/dist/runtime/runtime.d.ts.map +1 -1
  132. package/dist/runtime/runtime.js +44 -1
  133. package/dist/runtime/runtime.js.map +1 -1
  134. package/dist/runtime/types.d.ts +21 -0
  135. package/dist/runtime/types.d.ts.map +1 -1
  136. package/dist/runtime/vault-extra-ops.d.ts +9 -0
  137. package/dist/runtime/vault-extra-ops.d.ts.map +1 -0
  138. package/dist/runtime/vault-extra-ops.js +195 -0
  139. package/dist/runtime/vault-extra-ops.js.map +1 -0
  140. package/dist/telemetry/telemetry.d.ts +48 -0
  141. package/dist/telemetry/telemetry.d.ts.map +1 -0
  142. package/dist/telemetry/telemetry.js +87 -0
  143. package/dist/telemetry/telemetry.js.map +1 -0
  144. package/dist/vault/vault.d.ts +94 -0
  145. package/dist/vault/vault.d.ts.map +1 -1
  146. package/dist/vault/vault.js +340 -1
  147. package/dist/vault/vault.js.map +1 -1
  148. package/package.json +1 -1
  149. package/src/__tests__/admin-extra-ops.test.ts +420 -0
  150. package/src/__tests__/admin-ops.test.ts +271 -0
  151. package/src/__tests__/brain-intelligence.test.ts +205 -0
  152. package/src/__tests__/brain.test.ts +131 -0
  153. package/src/__tests__/capture-ops.test.ts +509 -0
  154. package/src/__tests__/core-ops.test.ts +266 -2
  155. package/src/__tests__/curator-extra-ops.test.ts +359 -0
  156. package/src/__tests__/domain-ops.test.ts +66 -0
  157. package/src/__tests__/governance.test.ts +522 -0
  158. package/src/__tests__/grading-ops.test.ts +340 -0
  159. package/src/__tests__/identity-manager.test.ts +243 -0
  160. package/src/__tests__/intent-router.test.ts +222 -0
  161. package/src/__tests__/logger.test.ts +200 -0
  162. package/src/__tests__/loop-ops.test.ts +398 -0
  163. package/src/__tests__/memory-cross-project-ops.test.ts +246 -0
  164. package/src/__tests__/memory-extra-ops.test.ts +352 -0
  165. package/src/__tests__/orchestrate-ops.test.ts +284 -0
  166. package/src/__tests__/planner.test.ts +331 -0
  167. package/src/__tests__/planning-extra-ops.test.ts +548 -0
  168. package/src/__tests__/project-ops.test.ts +367 -0
  169. package/src/__tests__/vault-extra-ops.test.ts +407 -0
  170. package/src/brain/brain.ts +114 -7
  171. package/src/brain/intelligence.ts +179 -10
  172. package/src/brain/types.ts +38 -0
  173. package/src/control/identity-manager.ts +354 -0
  174. package/src/control/intent-router.ts +326 -0
  175. package/src/control/types.ts +102 -0
  176. package/src/curator/curator.ts +213 -0
  177. package/src/governance/governance.ts +698 -0
  178. package/src/governance/index.ts +18 -0
  179. package/src/governance/types.ts +111 -0
  180. package/src/index.ts +102 -2
  181. package/src/logging/logger.ts +154 -0
  182. package/src/logging/types.ts +21 -0
  183. package/src/loop/loop-manager.ts +130 -0
  184. package/src/loop/types.ts +44 -0
  185. package/src/planning/gap-analysis.ts +506 -0
  186. package/src/planning/gap-types.ts +58 -0
  187. package/src/planning/planner.ts +478 -2
  188. package/src/project/project-registry.ts +358 -0
  189. package/src/project/types.ts +31 -0
  190. package/src/runtime/admin-extra-ops.ts +307 -0
  191. package/src/runtime/admin-ops.ts +329 -0
  192. package/src/runtime/capture-ops.ts +385 -0
  193. package/src/runtime/core-ops.ts +535 -7
  194. package/src/runtime/curator-extra-ops.ts +71 -0
  195. package/src/runtime/domain-ops.ts +65 -13
  196. package/src/runtime/grading-ops.ts +121 -0
  197. package/src/runtime/loop-ops.ts +194 -0
  198. package/src/runtime/memory-cross-project-ops.ts +192 -0
  199. package/src/runtime/memory-extra-ops.ts +186 -0
  200. package/src/runtime/orchestrate-ops.ts +272 -0
  201. package/src/runtime/planning-extra-ops.ts +327 -0
  202. package/src/runtime/project-ops.ts +196 -0
  203. package/src/runtime/runtime.ts +49 -1
  204. package/src/runtime/types.ts +21 -0
  205. package/src/runtime/vault-extra-ops.ts +225 -0
  206. package/src/telemetry/telemetry.ts +118 -0
  207. package/src/vault/vault.ts +412 -1
@@ -25,7 +25,7 @@ export function createDomainFacade(
25
25
  agentId: string,
26
26
  domain: string,
27
27
  ): FacadeConfig {
28
- const { vault, brain } = runtime;
28
+ const { vault, brain, governance } = runtime;
29
29
  const facadeName = `${agentId}_${domain.replace(/-/g, '_')}`;
30
30
 
31
31
  const ops: OpDefinition[] = [
@@ -92,21 +92,73 @@ export function createDomainFacade(
92
92
  counterExample: z.string().optional(),
93
93
  why: z.string().optional(),
94
94
  tags: z.array(z.string()).optional().default([]),
95
+ projectPath: z.string().optional().default('.'),
95
96
  }),
96
97
  handler: async (params) => {
97
- return brain.enrichAndCapture({
98
- id: params.id as string,
99
- type: params.type as 'pattern' | 'anti-pattern' | 'rule',
100
- domain,
101
- title: params.title as string,
102
- severity: params.severity as 'critical' | 'warning' | 'suggestion',
103
- description: params.description as string,
104
- context: params.context as string | undefined,
105
- example: params.example as string | undefined,
106
- counterExample: params.counterExample as string | undefined,
107
- why: params.why as string | undefined,
108
- tags: params.tags as string[],
98
+ const projectPath = (params.projectPath as string | undefined) ?? '.';
99
+ const entryType = params.type as string;
100
+ const title = params.title as string;
101
+
102
+ const decision = governance.evaluateCapture(projectPath, {
103
+ type: entryType,
104
+ category: domain,
105
+ title,
109
106
  });
107
+
108
+ switch (decision.action) {
109
+ case 'capture': {
110
+ const result = brain.enrichAndCapture({
111
+ id: params.id as string,
112
+ type: params.type as 'pattern' | 'anti-pattern' | 'rule',
113
+ domain,
114
+ title,
115
+ severity: params.severity as 'critical' | 'warning' | 'suggestion',
116
+ description: params.description as string,
117
+ context: params.context as string | undefined,
118
+ example: params.example as string | undefined,
119
+ counterExample: params.counterExample as string | undefined,
120
+ why: params.why as string | undefined,
121
+ tags: params.tags as string[],
122
+ });
123
+ return { ...result, governance: { action: 'capture' as const } };
124
+ }
125
+ case 'propose': {
126
+ const proposalId = governance.propose(
127
+ projectPath,
128
+ {
129
+ entryId: params.id as string,
130
+ title,
131
+ type: entryType,
132
+ category: domain,
133
+ data: {
134
+ severity: params.severity,
135
+ description: params.description,
136
+ context: params.context,
137
+ example: params.example,
138
+ counterExample: params.counterExample,
139
+ why: params.why,
140
+ tags: params.tags,
141
+ },
142
+ },
143
+ 'domain-capture',
144
+ );
145
+ return {
146
+ captured: false,
147
+ id: params.id as string,
148
+ autoTags: [],
149
+ governance: { action: 'propose' as const, proposalId, reason: decision.reason },
150
+ };
151
+ }
152
+ default: {
153
+ // reject or quarantine
154
+ return {
155
+ captured: false,
156
+ id: params.id as string,
157
+ autoTags: [],
158
+ governance: { action: decision.action, reason: decision.reason },
159
+ };
160
+ }
161
+ }
110
162
  },
111
163
  },
112
164
  {
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Plan grading operations — 5 ops for iterative plan quality scoring.
3
+ * Uses 6-pass gap analysis with severity-weighted scoring (ported from Salvador).
4
+ *
5
+ * Ops: plan_grade, plan_check_history, plan_latest_check,
6
+ * plan_meets_grade, plan_auto_improve.
7
+ */
8
+
9
+ import { z } from 'zod';
10
+ import type { OpDefinition } from '../facades/types.js';
11
+ import type { AgentRuntime } from './types.js';
12
+
13
+ const planGradeSchema = z.enum(['A+', 'A', 'B', 'C', 'D', 'F']);
14
+
15
+ /**
16
+ * Create the 5 plan grading operations for an agent runtime.
17
+ */
18
+ export function createGradingOps(runtime: AgentRuntime): OpDefinition[] {
19
+ const { planner } = runtime;
20
+
21
+ return [
22
+ {
23
+ name: 'plan_grade',
24
+ description:
25
+ 'Grade a plan using 6-pass gap analysis — severity-weighted scoring (critical=30, major=15, minor=2). Returns grade, score, gaps with recommendations, and iteration number.',
26
+ auth: 'read',
27
+ schema: z.object({
28
+ planId: z.string().describe('The plan ID to grade.'),
29
+ }),
30
+ handler: async (params) => {
31
+ const planId = params.planId as string;
32
+ return planner.grade(planId);
33
+ },
34
+ },
35
+
36
+ {
37
+ name: 'plan_check_history',
38
+ description: 'Get all grading checks for a plan (history). Shows score progression across iterations.',
39
+ auth: 'read',
40
+ schema: z.object({
41
+ planId: z.string().describe('The plan ID.'),
42
+ }),
43
+ handler: async (params) => {
44
+ const planId = params.planId as string;
45
+ const checks = planner.getCheckHistory(planId);
46
+ return { planId, count: checks.length, checks };
47
+ },
48
+ },
49
+
50
+ {
51
+ name: 'plan_latest_check',
52
+ description: 'Get the latest grading check for a plan.',
53
+ auth: 'read',
54
+ schema: z.object({
55
+ planId: z.string().describe('The plan ID.'),
56
+ }),
57
+ handler: async (params) => {
58
+ const planId = params.planId as string;
59
+ const check = planner.getLatestCheck(planId);
60
+ return check ?? { planId, check: null, message: 'No checks found for this plan.' };
61
+ },
62
+ },
63
+
64
+ {
65
+ name: 'plan_meets_grade',
66
+ description:
67
+ 'Check if a plan meets a target grade. Thresholds: A+=95, A=90, B=80, C=70, D=60.',
68
+ auth: 'read',
69
+ schema: z.object({
70
+ planId: z.string().describe('The plan ID.'),
71
+ targetGrade: planGradeSchema.describe('Target grade: A+, A, B, C, D, or F.'),
72
+ }),
73
+ handler: async (params) => {
74
+ const planId = params.planId as string;
75
+ const targetGrade = params.targetGrade as 'A+' | 'A' | 'B' | 'C' | 'D' | 'F';
76
+ return planner.meetsGrade(planId, targetGrade);
77
+ },
78
+ },
79
+
80
+ {
81
+ name: 'plan_auto_improve',
82
+ description:
83
+ 'Grade a plan and return gaps grouped by severity with actionable recommendations. Critical gaps first.',
84
+ auth: 'read',
85
+ schema: z.object({
86
+ planId: z.string().describe('The plan ID.'),
87
+ }),
88
+ handler: async (params) => {
89
+ const planId = params.planId as string;
90
+ const check = planner.grade(planId);
91
+
92
+ // Sort gaps by severity: critical > major > minor > info
93
+ const severityOrder: Record<string, number> = { critical: 0, major: 1, minor: 2, info: 3 };
94
+ const sortedGaps = [...check.gaps].sort(
95
+ (a, b) => (severityOrder[a.severity] ?? 4) - (severityOrder[b.severity] ?? 4),
96
+ );
97
+
98
+ // Group by severity for structured output
99
+ const grouped: Record<string, Array<{ category: string; description: string; recommendation: string; location?: string }>> = {};
100
+ for (const g of sortedGaps) {
101
+ if (!grouped[g.severity]) grouped[g.severity] = [];
102
+ grouped[g.severity].push({
103
+ category: g.category,
104
+ description: g.description,
105
+ recommendation: g.recommendation,
106
+ ...(g.location ? { location: g.location } : {}),
107
+ });
108
+ }
109
+
110
+ return {
111
+ grade: check.grade,
112
+ score: check.score,
113
+ iteration: check.iteration,
114
+ totalGaps: check.gaps.length,
115
+ gapsBySeverity: grouped,
116
+ nextAction: check.score >= 90 ? 'approve' : 'iterate',
117
+ };
118
+ },
119
+ },
120
+ ];
121
+ }
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Loop operations — 7 ops for iterative validation loops.
3
+ *
4
+ * Ops: loop_start, loop_iterate, loop_status, loop_cancel,
5
+ * loop_history, loop_is_active, loop_complete.
6
+ */
7
+
8
+ import { z } from 'zod';
9
+ import type { OpDefinition } from '../facades/types.js';
10
+ import type { AgentRuntime } from './types.js';
11
+ import type { LoopMode } from '../loop/types.js';
12
+
13
+ const loopModeSchema = z.enum([
14
+ 'token-migration',
15
+ 'contrast-fix',
16
+ 'component-build',
17
+ 'plan-iteration',
18
+ 'custom',
19
+ ]);
20
+
21
+ /**
22
+ * Default max iterations per mode.
23
+ */
24
+ const DEFAULT_MAX_ITERATIONS: Record<LoopMode, number> = {
25
+ 'token-migration': 20,
26
+ 'contrast-fix': 15,
27
+ 'component-build': 20,
28
+ 'plan-iteration': 10,
29
+ custom: 20,
30
+ };
31
+
32
+ /**
33
+ * Default target scores per mode.
34
+ */
35
+ const DEFAULT_TARGET_SCORES: Partial<Record<LoopMode, number>> = {
36
+ 'token-migration': 95,
37
+ 'component-build': 90,
38
+ 'plan-iteration': 90,
39
+ };
40
+
41
+ /**
42
+ * Create the 7 loop operations for an agent runtime.
43
+ */
44
+ export function createLoopOps(runtime: AgentRuntime): OpDefinition[] {
45
+ const { loop } = runtime;
46
+
47
+ return [
48
+ {
49
+ name: 'loop_start',
50
+ description:
51
+ 'Start an iterative validation loop. Modes: token-migration, contrast-fix, component-build, plan-iteration, custom.',
52
+ auth: 'write',
53
+ schema: z.object({
54
+ mode: loopModeSchema,
55
+ prompt: z.string().describe('Task description for the loop.'),
56
+ maxIterations: z
57
+ .number()
58
+ .optional()
59
+ .describe('Max iterations. Defaults vary by mode (10-20).'),
60
+ targetScore: z
61
+ .number()
62
+ .optional()
63
+ .describe('Target validation score (0-100). Defaults vary by mode.'),
64
+ }),
65
+ handler: async (params) => {
66
+ const mode = params.mode as LoopMode;
67
+ const prompt = params.prompt as string;
68
+ const maxIterations =
69
+ (params.maxIterations as number | undefined) ?? DEFAULT_MAX_ITERATIONS[mode];
70
+ const targetScore =
71
+ (params.targetScore as number | undefined) ?? DEFAULT_TARGET_SCORES[mode];
72
+
73
+ const state = loop.startLoop({
74
+ mode,
75
+ prompt,
76
+ maxIterations,
77
+ targetScore,
78
+ });
79
+
80
+ return {
81
+ started: true,
82
+ loopId: state.id,
83
+ mode,
84
+ maxIterations,
85
+ targetScore: targetScore ?? null,
86
+ };
87
+ },
88
+ },
89
+ {
90
+ name: 'loop_iterate',
91
+ description:
92
+ 'Record a validation iteration result. If max iterations reached on a failing result, the loop auto-closes.',
93
+ auth: 'write',
94
+ schema: z.object({
95
+ passed: z.boolean().describe('Whether this iteration passed validation.'),
96
+ validationScore: z
97
+ .number()
98
+ .optional()
99
+ .describe('Numeric validation score (0-100).'),
100
+ validationResult: z
101
+ .string()
102
+ .optional()
103
+ .describe('Free-text validation result summary.'),
104
+ }),
105
+ handler: async (params) => {
106
+ const iteration = loop.iterate({
107
+ passed: params.passed as boolean,
108
+ validationScore: params.validationScore as number | undefined,
109
+ validationResult: params.validationResult as string | undefined,
110
+ });
111
+
112
+ const status = loop.getStatus();
113
+ return {
114
+ iteration: iteration.iteration,
115
+ passed: iteration.passed,
116
+ validationScore: iteration.validationScore ?? null,
117
+ loopActive: loop.isActive(),
118
+ loopStatus: status?.status ?? 'max-iterations',
119
+ };
120
+ },
121
+ },
122
+ {
123
+ name: 'loop_status',
124
+ description: 'Get current loop status, config, and iteration history.',
125
+ auth: 'read',
126
+ handler: async () => {
127
+ const status = loop.getStatus();
128
+ if (!status) {
129
+ return { active: false, loop: null };
130
+ }
131
+ return {
132
+ active: true,
133
+ loop: status,
134
+ };
135
+ },
136
+ },
137
+ {
138
+ name: 'loop_cancel',
139
+ description: 'Cancel the active loop.',
140
+ auth: 'write',
141
+ handler: async () => {
142
+ const cancelled = loop.cancelLoop();
143
+ return {
144
+ cancelled: true,
145
+ loopId: cancelled.id,
146
+ iterations: cancelled.iterations.length,
147
+ status: cancelled.status,
148
+ };
149
+ },
150
+ },
151
+ {
152
+ name: 'loop_history',
153
+ description: 'Get history of all completed, cancelled, and max-iterations loops.',
154
+ auth: 'read',
155
+ handler: async () => {
156
+ const history = loop.getHistory();
157
+ return {
158
+ count: history.length,
159
+ loops: history.map((l) => ({
160
+ id: l.id,
161
+ mode: l.config.mode,
162
+ prompt: l.config.prompt,
163
+ status: l.status,
164
+ iterations: l.iterations.length,
165
+ startedAt: l.startedAt,
166
+ completedAt: l.completedAt,
167
+ })),
168
+ };
169
+ },
170
+ },
171
+ {
172
+ name: 'loop_is_active',
173
+ description: 'Check if a validation loop is currently running.',
174
+ auth: 'read',
175
+ handler: async () => {
176
+ return { active: loop.isActive() };
177
+ },
178
+ },
179
+ {
180
+ name: 'loop_complete',
181
+ description: 'Mark the active loop as completed (validation passed).',
182
+ auth: 'write',
183
+ handler: async () => {
184
+ const completed = loop.completeLoop();
185
+ return {
186
+ completed: true,
187
+ loopId: completed.id,
188
+ iterations: completed.iterations.length,
189
+ status: completed.status,
190
+ };
191
+ },
192
+ },
193
+ ];
194
+ }
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Cross-project memory operations — 3 ops for sharing knowledge across linked projects.
3
+ *
4
+ * Ops: memory_promote_to_global, memory_configure, memory_cross_project_search.
5
+ */
6
+
7
+ import { z } from 'zod';
8
+ import type { OpDefinition } from '../facades/types.js';
9
+ import type { AgentRuntime } from './types.js';
10
+
11
+ /**
12
+ * Create the 3 cross-project memory operations for an agent runtime.
13
+ */
14
+ export function createMemoryCrossProjectOps(runtime: AgentRuntime): OpDefinition[] {
15
+ const { vault, projectRegistry } = runtime;
16
+
17
+ return [
18
+ {
19
+ name: 'memory_promote_to_global',
20
+ description:
21
+ 'Promote a vault entry to the global pool by adding a _global tag. Promoted entries surface in cross-project searches across all linked projects.',
22
+ auth: 'write',
23
+ schema: z.object({
24
+ entryId: z.string().describe('The vault entry ID to promote.'),
25
+ }),
26
+ handler: async (params) => {
27
+ const entryId = params.entryId as string;
28
+ const entry = vault.get(entryId);
29
+ if (!entry) return { promoted: false, error: `Entry not found: ${entryId}` };
30
+
31
+ const tags = entry.tags ?? [];
32
+ if (tags.includes('_global')) {
33
+ return { promoted: false, entryId, message: 'Entry is already promoted to global.' };
34
+ }
35
+
36
+ vault.update(entryId, { tags: [...tags, '_global'] });
37
+ return { promoted: true, entryId, tags: [...tags, '_global'] };
38
+ },
39
+ },
40
+
41
+ {
42
+ name: 'memory_configure',
43
+ description:
44
+ 'Configure cross-project memory settings for a project. Stores config in the project registry metadata.',
45
+ auth: 'admin',
46
+ schema: z.object({
47
+ projectPath: z.string().describe('Project path to configure.'),
48
+ crossProjectEnabled: z
49
+ .boolean()
50
+ .optional()
51
+ .describe('Enable/disable cross-project memory search.'),
52
+ extraPaths: z
53
+ .array(z.string())
54
+ .optional()
55
+ .describe('Additional project paths to include in cross-project searches.'),
56
+ }),
57
+ handler: async (params) => {
58
+ const projectPath = params.projectPath as string;
59
+ const project = projectRegistry.getByPath(projectPath);
60
+ if (!project) {
61
+ return { configured: false, error: `Project not registered: ${projectPath}` };
62
+ }
63
+
64
+ const currentMeta = project.metadata ?? {};
65
+ const memoryConfig = (currentMeta.memoryConfig as Record<string, unknown>) ?? {};
66
+
67
+ if (params.crossProjectEnabled !== undefined) {
68
+ memoryConfig.crossProjectEnabled = params.crossProjectEnabled;
69
+ }
70
+ if (params.extraPaths !== undefined) {
71
+ memoryConfig.extraPaths = params.extraPaths;
72
+ }
73
+
74
+ projectRegistry.register(projectPath, project.name, {
75
+ ...currentMeta,
76
+ memoryConfig,
77
+ });
78
+
79
+ return { configured: true, projectPath, memoryConfig };
80
+ },
81
+ },
82
+
83
+ {
84
+ name: 'memory_cross_project_search',
85
+ description:
86
+ 'Search memories across the current project and all linked projects. Results weighted: current=1.0, global=0.9, linked=0.8. Requires cross-project search to be enabled.',
87
+ auth: 'read',
88
+ schema: z.object({
89
+ query: z.string().describe('Search query.'),
90
+ projectPath: z.string().describe('Current project path.'),
91
+ type: z
92
+ .enum(['session', 'lesson', 'preference'])
93
+ .optional()
94
+ .describe('Filter by memory type.'),
95
+ limit: z.number().optional().describe('Max results per project (default 10).'),
96
+ }),
97
+ handler: async (params) => {
98
+ const query = params.query as string;
99
+ const projectPath = params.projectPath as string;
100
+ const type = params.type as string | undefined;
101
+ const limit = (params.limit as number) ?? 10;
102
+
103
+ // Search current project (weight 1.0)
104
+ const currentResults = vault.searchMemories(query, { projectPath, type, limit });
105
+ const weightedResults = currentResults.map((m) => ({
106
+ memory: m,
107
+ weight: 1.0,
108
+ source: 'current' as const,
109
+ }));
110
+
111
+ // Search for globally promoted entries (search broadly, then filter for _global tag)
112
+ const allResults = vault.search(query, { limit: limit * 3 });
113
+ const globalEntries = allResults
114
+ .filter((r) => r.entry.tags?.includes('_global'))
115
+ .slice(0, limit)
116
+ .map((r) => ({
117
+ entry: r.entry,
118
+ score: r.score,
119
+ weight: 0.9,
120
+ source: 'global' as const,
121
+ }));
122
+
123
+ // Get linked projects and search their memories
124
+ const project = projectRegistry.getByPath(projectPath);
125
+ const linkedMemories: Array<{
126
+ memory: (typeof currentResults)[0];
127
+ weight: number;
128
+ source: 'linked';
129
+ linkedProject: string;
130
+ }> = [];
131
+
132
+ if (project) {
133
+ const config = (project.metadata?.memoryConfig as Record<string, unknown>) ?? {};
134
+ const enabled = config.crossProjectEnabled !== false; // default enabled
135
+
136
+ if (enabled) {
137
+ const linked = projectRegistry.getLinkedProjects(project.id);
138
+ for (const { project: linkedProj } of linked) {
139
+ const results = vault.searchMemories(query, {
140
+ projectPath: linkedProj.path,
141
+ type,
142
+ limit: Math.ceil(limit / 2), // fewer results from linked projects
143
+ });
144
+ for (const m of results) {
145
+ linkedMemories.push({
146
+ memory: m,
147
+ weight: 0.8,
148
+ source: 'linked',
149
+ linkedProject: linkedProj.path,
150
+ });
151
+ }
152
+ }
153
+
154
+ // Also search extra paths
155
+ const extraPaths = (config.extraPaths as string[]) ?? [];
156
+ for (const extraPath of extraPaths) {
157
+ const results = vault.searchMemories(query, {
158
+ projectPath: extraPath,
159
+ type,
160
+ limit: Math.ceil(limit / 2),
161
+ });
162
+ for (const m of results) {
163
+ linkedMemories.push({
164
+ memory: m,
165
+ weight: 0.7,
166
+ source: 'linked',
167
+ linkedProject: extraPath,
168
+ });
169
+ }
170
+ }
171
+ }
172
+ }
173
+
174
+ // Deduplicate by memory ID
175
+ const seen = new Set(weightedResults.map((r) => r.memory.id));
176
+ const dedupedLinked = linkedMemories.filter((r) => {
177
+ if (seen.has(r.memory.id)) return false;
178
+ seen.add(r.memory.id);
179
+ return true;
180
+ });
181
+
182
+ return {
183
+ memories: weightedResults,
184
+ globalEntries,
185
+ linkedMemories: dedupedLinked,
186
+ totalResults:
187
+ weightedResults.length + globalEntries.length + dedupedLinked.length,
188
+ };
189
+ },
190
+ },
191
+ ];
192
+ }