@a5c-ai/krate 5.0.1-staging.04a3db697

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 (246) hide show
  1. package/Dockerfile +31 -0
  2. package/README.md +183 -0
  3. package/bin/krate-demo.mjs +23 -0
  4. package/bin/krate-server.mjs +14 -0
  5. package/dist/krate-controller-ui.json +3067 -0
  6. package/dist/krate-lifecycle.json +201 -0
  7. package/dist/krate-runtime-snapshot.json +2955 -0
  8. package/dist/krate-summary.json +722 -0
  9. package/docs/README.md +61 -0
  10. package/docs/agents/README.md +83 -0
  11. package/docs/agents/acceptance-test-matrix.md +193 -0
  12. package/docs/agents/agent-mux-adapter-contract.md +167 -0
  13. package/docs/agents/agent-mux-source-map.md +310 -0
  14. package/docs/agents/agent-run-memory-import-spec.md +256 -0
  15. package/docs/agents/agent-stack-management-spec.md +421 -0
  16. package/docs/agents/api-contract-spec.md +309 -0
  17. package/docs/agents/artifacts-writeback-spec.md +145 -0
  18. package/docs/agents/chart-packaging-spec.md +128 -0
  19. package/docs/agents/ci-orchestration-spec.md +140 -0
  20. package/docs/agents/context-assembly-spec.md +219 -0
  21. package/docs/agents/controller-reconciliation-spec.md +255 -0
  22. package/docs/agents/crd-schema-spec.md +315 -0
  23. package/docs/agents/decision-log-open-questions.md +169 -0
  24. package/docs/agents/developer-implementation-checklist.md +329 -0
  25. package/docs/agents/dispatching-design.md +262 -0
  26. package/docs/agents/gaps-agent-mux-to-krate-crds.md +298 -0
  27. package/docs/agents/glossary.md +66 -0
  28. package/docs/agents/implementation-blueprint.md +324 -0
  29. package/docs/agents/implementation-rollout-slices.md +251 -0
  30. package/docs/agents/memory-context-integration-spec.md +194 -0
  31. package/docs/agents/memory-ontology-schema-spec.md +253 -0
  32. package/docs/agents/memory-operations-runbook.md +121 -0
  33. package/docs/agents/mvp-vertical-slice-spec.md +146 -0
  34. package/docs/agents/observability-audit-spec.md +265 -0
  35. package/docs/agents/operator-runbook.md +174 -0
  36. package/docs/agents/org-memory-api-payload-examples.md +333 -0
  37. package/docs/agents/org-memory-controller-sequence-spec.md +181 -0
  38. package/docs/agents/org-memory-e2e-fixture-plan.md +161 -0
  39. package/docs/agents/org-memory-ui-implementation-map.md +114 -0
  40. package/docs/agents/org-memory-vertical-slice-spec.md +168 -0
  41. package/docs/agents/org-resource-model-delta-spec.md +111 -0
  42. package/docs/agents/org-route-resource-model-spec.md +183 -0
  43. package/docs/agents/org-scoping-namespace-spec.md +114 -0
  44. package/docs/agents/rbac-secrets-management-spec.md +406 -0
  45. package/docs/agents/repository-page-integration-spec.md +255 -0
  46. package/docs/agents/resource-contract-examples.md +808 -0
  47. package/docs/agents/resource-relationship-map.md +190 -0
  48. package/docs/agents/security-threat-model.md +188 -0
  49. package/docs/agents/shared-memory-company-brain-spec.md +358 -0
  50. package/docs/agents/storage-migration-spec.md +168 -0
  51. package/docs/agents/subagent-orchestration-spec.md +152 -0
  52. package/docs/agents/system-overview.md +88 -0
  53. package/docs/agents/tools-mcp-skills-spec.md +189 -0
  54. package/docs/agents/traceability-matrix.md +79 -0
  55. package/docs/agents/ui-flow-spec.md +211 -0
  56. package/docs/agents/ui-ux-system-spec.md +426 -0
  57. package/docs/agents/workspace-lifecycle-spec.md +166 -0
  58. package/docs/architecture-spec.md +78 -0
  59. package/docs/components/control-plane.md +78 -0
  60. package/docs/components/data-plane.md +69 -0
  61. package/docs/components/hooks-events.md +67 -0
  62. package/docs/components/identity-rbac-policy.md +73 -0
  63. package/docs/components/kubevela-oam.md +70 -0
  64. package/docs/components/operations-publishing.md +81 -0
  65. package/docs/components/runners-ci.md +66 -0
  66. package/docs/components/web-ui.md +94 -0
  67. package/docs/external/README.md +47 -0
  68. package/docs/external/bidirectional-sync-design.md +134 -0
  69. package/docs/external/cicd-interface.md +64 -0
  70. package/docs/external/external-backend-controllers.md +170 -0
  71. package/docs/external/external-backend-crds.md +234 -0
  72. package/docs/external/external-backend-ui-spec.md +151 -0
  73. package/docs/external/external-backend-ux-flows.md +115 -0
  74. package/docs/external/external-object-mapping.md +125 -0
  75. package/docs/external/git-forge-interface.md +68 -0
  76. package/docs/external/github-integration-design.md +151 -0
  77. package/docs/external/issue-tracking-interface.md +66 -0
  78. package/docs/external/provider-capability-manifests.md +204 -0
  79. package/docs/external/provider-catalog.md +139 -0
  80. package/docs/external/provider-rollout-testing.md +78 -0
  81. package/docs/external/research-results.md +48 -0
  82. package/docs/external/security-auth-permissions.md +81 -0
  83. package/docs/external/sync-state-machines.md +108 -0
  84. package/docs/external/unified-external-backend-model.md +107 -0
  85. package/docs/external/user-facing-changes.md +67 -0
  86. package/docs/gaps.md +161 -0
  87. package/docs/install.md +94 -0
  88. package/docs/krate-design.md +334 -0
  89. package/docs/local-minikube.md +55 -0
  90. package/docs/ontology/README.md +32 -0
  91. package/docs/ontology/bounded-contexts.md +29 -0
  92. package/docs/ontology/events-and-hooks.md +32 -0
  93. package/docs/ontology/oam-kubevela.md +32 -0
  94. package/docs/ontology/operations-and-release.md +25 -0
  95. package/docs/ontology/personas-and-actors.md +32 -0
  96. package/docs/ontology/policies-and-invariants.md +33 -0
  97. package/docs/ontology/problem-space.md +30 -0
  98. package/docs/ontology/resource-contracts.md +40 -0
  99. package/docs/ontology/resource-taxonomy.md +42 -0
  100. package/docs/ontology/runners-and-ci.md +29 -0
  101. package/docs/ontology/solution-space.md +24 -0
  102. package/docs/ontology/storage-and-data-boundaries.md +29 -0
  103. package/docs/ontology/validation-matrix.md +24 -0
  104. package/docs/ontology/web-ui-excellent-flows.md +32 -0
  105. package/docs/ontology/workflows.md +39 -0
  106. package/docs/ontology/world.md +35 -0
  107. package/docs/product-requirements.md +62 -0
  108. package/docs/roadmap-mvp.md +87 -0
  109. package/docs/system-requirements.md +90 -0
  110. package/docs/tests/README.md +53 -0
  111. package/docs/tests/agent-qa-plan.md +63 -0
  112. package/docs/tests/browser-ui-tests.md +62 -0
  113. package/docs/tests/ci-quality-gates.md +48 -0
  114. package/docs/tests/coverage-model.md +64 -0
  115. package/docs/tests/e2e-scenario-tests.md +53 -0
  116. package/docs/tests/fixtures-test-data.md +63 -0
  117. package/docs/tests/observability-reliability-tests.md +54 -0
  118. package/docs/tests/product-test-matrix.md +145 -0
  119. package/docs/tests/qa-adoption-roadmap.md +130 -0
  120. package/docs/tests/qa-automation-plan.md +101 -0
  121. package/docs/tests/security-compliance-tests.md +57 -0
  122. package/docs/tests/test-framework-tools.md +88 -0
  123. package/docs/tests/test-suite-layout.md +121 -0
  124. package/docs/tests/unit-integration-tests.md +48 -0
  125. package/docs/todo-kyverno +714 -0
  126. package/docs/todos.md +4 -0
  127. package/docs/user-stories.md +78 -0
  128. package/examples/minikube-demo.yaml +190 -0
  129. package/examples/oam-application.yaml +23 -0
  130. package/examples/policy-kyverno-pr-title.yaml +18 -0
  131. package/package.json +63 -0
  132. package/scripts/build.mjs +29 -0
  133. package/scripts/setup-minikube.mjs +65 -0
  134. package/scripts/smoke.mjs +37 -0
  135. package/scripts/validate-doc-coverage.mjs +152 -0
  136. package/scripts/validate-package.mjs +93 -0
  137. package/scripts/validate-ui.mjs +236 -0
  138. package/src/agent-adapter-controller.js +169 -0
  139. package/src/agent-approval-controller.js +170 -0
  140. package/src/agent-context-bundles.js +242 -0
  141. package/src/agent-dispatch-controller.js +209 -0
  142. package/src/agent-gateway-config-controller.js +147 -0
  143. package/src/agent-memory-controller.js +357 -0
  144. package/src/agent-memory-import.js +327 -0
  145. package/src/agent-memory-query.js +292 -0
  146. package/src/agent-memory-repository-source-controller.js +255 -0
  147. package/src/agent-mux-client.js +280 -0
  148. package/src/agent-permission-review.js +250 -0
  149. package/src/agent-project-controller.js +117 -0
  150. package/src/agent-provider-config-controller.js +150 -0
  151. package/src/agent-secret-config-grant-controller.js +282 -0
  152. package/src/agent-session-transcript-controller.js +189 -0
  153. package/src/agent-stack-controller.js +347 -0
  154. package/src/agent-subagent-controller.js +160 -0
  155. package/src/agent-transport-binding-controller.js +121 -0
  156. package/src/agent-trigger-controller.js +321 -0
  157. package/src/agent-workspace-controller.js +447 -0
  158. package/src/agent-writeback-controller.js +302 -0
  159. package/src/api-controller.js +541 -0
  160. package/src/argocd-gitops.js +43 -0
  161. package/src/async-controller.js +207 -0
  162. package/src/audit-controller.js +191 -0
  163. package/src/auth.js +307 -0
  164. package/src/component-catalog.js +41 -0
  165. package/src/control-plane.js +136 -0
  166. package/src/controller-client.js +50 -0
  167. package/src/controller-ui.js +551 -0
  168. package/src/data-plane.js +178 -0
  169. package/src/event-bus.js +61 -0
  170. package/src/external/conflict-controller.js +225 -0
  171. package/src/external/github/auth.js +96 -0
  172. package/src/external/github/cicd.js +180 -0
  173. package/src/external/github/git-forge.js +240 -0
  174. package/src/external/github/index.js +144 -0
  175. package/src/external/github/issue-tracking.js +163 -0
  176. package/src/external/provider-adapter.js +161 -0
  177. package/src/external/provider-resource-factory.js +161 -0
  178. package/src/external/sync-controller.js +235 -0
  179. package/src/external/webhook-controller.js +144 -0
  180. package/src/external/write-controller.js +283 -0
  181. package/src/gitea-backend.js +95 -0
  182. package/src/gitea-service.js +173 -0
  183. package/src/handoff.js +98 -0
  184. package/src/hooks-events.js +63 -0
  185. package/src/http-server.js +377 -0
  186. package/src/identity-policy.js +86 -0
  187. package/src/index.js +55 -0
  188. package/src/kubernetes-controller-async.js +511 -0
  189. package/src/kubernetes-controller.js +878 -0
  190. package/src/kubernetes-resource-gateway.js +48 -0
  191. package/src/operations.js +112 -0
  192. package/src/org-scoping.js +5 -0
  193. package/src/resource-model.js +221 -0
  194. package/src/runners-ci.js +48 -0
  195. package/src/runtime.js +196 -0
  196. package/src/snapshot-cache.js +157 -0
  197. package/src/web-ui.js +40 -0
  198. package/tests/agent-adapter-controller.test.js +361 -0
  199. package/tests/agent-approval-controller.test.js +173 -0
  200. package/tests/agent-context-bundles.test.js +278 -0
  201. package/tests/agent-dispatch-controller.test.js +315 -0
  202. package/tests/agent-gateway-config-controller.test.js +386 -0
  203. package/tests/agent-memory-controller.test.js +308 -0
  204. package/tests/agent-memory-import-snapshot.test.js +477 -0
  205. package/tests/agent-memory-query.test.js +404 -0
  206. package/tests/agent-memory-repository-source.test.js +514 -0
  207. package/tests/agent-mux-client.test.js +204 -0
  208. package/tests/agent-permission-review-v2.test.js +317 -0
  209. package/tests/agent-permission-review.test.js +209 -0
  210. package/tests/agent-project-controller.test.js +302 -0
  211. package/tests/agent-provider-config-controller.test.js +376 -0
  212. package/tests/agent-resources.test.js +228 -0
  213. package/tests/agent-secret-config-grant.test.js +231 -0
  214. package/tests/agent-session-transcript-controller.test.js +499 -0
  215. package/tests/agent-stack-controller.test.js +221 -0
  216. package/tests/agent-subagent-controller.test.js +201 -0
  217. package/tests/agent-transport-binding-controller.test.js +294 -0
  218. package/tests/agent-trigger-controller.test.js +211 -0
  219. package/tests/agent-trigger-routes.test.js +190 -0
  220. package/tests/agent-trigger-sources.test.js +245 -0
  221. package/tests/agent-workspace-controller.test.js +181 -0
  222. package/tests/agent-writeback.test.js +292 -0
  223. package/tests/approval-persistence.test.js +171 -0
  224. package/tests/async-controller.test.js +252 -0
  225. package/tests/audit-controller.test.js +227 -0
  226. package/tests/deployment.test.js +396 -0
  227. package/tests/e2e/lifecycle.test.js +117 -0
  228. package/tests/external-github-forge.test.js +560 -0
  229. package/tests/external-github-issues-cicd.test.js +520 -0
  230. package/tests/external-integration.test.js +470 -0
  231. package/tests/external-persistence.test.js +340 -0
  232. package/tests/external-provider-adapter.test.js +365 -0
  233. package/tests/external-resource-model.test.js +215 -0
  234. package/tests/external-webhook-sync.test.js +287 -0
  235. package/tests/external-write-conflict.test.js +353 -0
  236. package/tests/gitea-service.test.js +253 -0
  237. package/tests/health-check-real.test.js +165 -0
  238. package/tests/integration/full-flow.test.js +266 -0
  239. package/tests/krate.test.js +727 -0
  240. package/tests/memory-search-wiring.test.js +270 -0
  241. package/tests/org-scoping.test.js +687 -0
  242. package/tests/session-cookie-hmac.test.js +151 -0
  243. package/tests/snapshot-performance.test.js +247 -0
  244. package/tests/sse-events.test.js +107 -0
  245. package/tests/workspace-volumes.test.js +312 -0
  246. package/tests/writeback-persistence.test.js +207 -0
@@ -0,0 +1,347 @@
1
+ import { createPermissionReviewer } from './agent-permission-review.js';
2
+ import { clone } from './resource-model.js';
3
+
4
+ export const AGENT_STACK_CONTROLLER_BOUNDARY = {
5
+ role: 'agent-stack-controller',
6
+ scope: 'Stack readiness reconciliation with capability resolution and condition management',
7
+ owns: ['capability resolution', 'stack conditions', 'readiness computation', 'mcp health checks'],
8
+ delegatesTo: ['agent-permission-review', 'resource-model'],
9
+ mustNotOwn: ['secret values', 'dispatch execution', 'Agent Mux sessions']
10
+ };
11
+
12
+ const MCP_HEALTH_TIMEOUT_MS = 3000;
13
+
14
+ /**
15
+ * Perform an HTTP health check for an MCP server endpoint.
16
+ * @param {string} url
17
+ * @param {Function|null} fetchFn
18
+ * @returns {Promise<{ status: string, latencyMs: number, error?: string }>}
19
+ */
20
+ async function performMcpHealthCheck(url, fetchFn) {
21
+ const fn = fetchFn || globalThis.fetch;
22
+ const start = Date.now();
23
+ try {
24
+ const controller = new AbortController();
25
+ const timer = setTimeout(() => controller.abort(), MCP_HEALTH_TIMEOUT_MS);
26
+ let response;
27
+ try {
28
+ response = await fn(url, { signal: controller.signal });
29
+ } finally {
30
+ clearTimeout(timer);
31
+ }
32
+ const latencyMs = Date.now() - start;
33
+ if (response.ok) {
34
+ return { status: 'healthy', latencyMs };
35
+ }
36
+ return { status: 'unhealthy', latencyMs, error: `HTTP ${response.status}` };
37
+ } catch (err) {
38
+ const latencyMs = Date.now() - start;
39
+ return { status: 'unhealthy', latencyMs, error: err.message || String(err) };
40
+ }
41
+ }
42
+
43
+ export function createAgentStackController(options = {}) {
44
+ const permissionReviewer = options.permissionReviewer || createPermissionReviewer();
45
+ const fetchFn = options.fetch || null;
46
+
47
+ return {
48
+ role: 'agent-stack-controller',
49
+
50
+ reconcileStack(stack, resources = {}) {
51
+ const spec = stack?.spec || {};
52
+ const conditions = [];
53
+ const missing = [];
54
+
55
+ // --- Resolve capability refs from stack spec ---
56
+ const resolvedTools = [];
57
+ const resolvedMcpServers = [];
58
+ const resolvedSkills = [];
59
+ const resolvedSubagents = [];
60
+ const resolvedContextLabels = [];
61
+
62
+ // toolPolicyRef
63
+ const toolPolicyRef = spec.toolPolicy || spec.toolPolicyRef || null;
64
+ let toolPolicyFound = true;
65
+ if (toolPolicyRef) {
66
+ const profiles = resources.AgentToolProfile || [];
67
+ const profile = profiles.find((p) => p.metadata?.name === toolPolicyRef);
68
+ if (profile) {
69
+ resolvedTools.push(profile.metadata.name);
70
+ } else {
71
+ toolPolicyFound = false;
72
+ missing.push(`AgentToolProfile/${toolPolicyRef}`);
73
+ }
74
+ }
75
+
76
+ // mcpServerRefs
77
+ const mcpServerRefs = spec.mcpServerRefs || [];
78
+ let allMcpFound = true;
79
+ for (const ref of mcpServerRefs) {
80
+ const servers = resources.AgentMcpServer || [];
81
+ const server = servers.find((s) => s.metadata?.name === ref);
82
+ if (server) {
83
+ resolvedMcpServers.push(server.metadata.name);
84
+ } else {
85
+ allMcpFound = false;
86
+ missing.push(`AgentMcpServer/${ref}`);
87
+ }
88
+ }
89
+
90
+ // skillRefs
91
+ const skillRefs = spec.skillRefs || [];
92
+ let allSkillsFound = true;
93
+ let allSkillsValid = true;
94
+ for (const ref of skillRefs) {
95
+ const skills = resources.AgentSkill || [];
96
+ const skill = skills.find((s) => s.metadata?.name === ref);
97
+ if (skill) {
98
+ resolvedSkills.push(skill.metadata.name);
99
+ if (!skill.spec?.format || !skill.spec?.sourceRef) {
100
+ allSkillsValid = false;
101
+ }
102
+ } else {
103
+ allSkillsFound = false;
104
+ allSkillsValid = false;
105
+ missing.push(`AgentSkill/${ref}`);
106
+ }
107
+ }
108
+
109
+ // subagentRefs
110
+ const subagentRefs = spec.subagentRefs || [];
111
+ let allSubagentsFound = true;
112
+ let allSubagentsValid = true;
113
+ for (const ref of subagentRefs) {
114
+ const subagents = resources.AgentSubagent || [];
115
+ const subagent = subagents.find((s) => s.metadata?.name === ref);
116
+ if (subagent) {
117
+ resolvedSubagents.push(subagent.metadata.name);
118
+ if (!subagent.spec?.taskKinds || subagent.spec.taskKinds.length === 0) {
119
+ allSubagentsValid = false;
120
+ }
121
+ } else {
122
+ allSubagentsFound = false;
123
+ allSubagentsValid = false;
124
+ missing.push(`AgentSubagent/${ref}`);
125
+ }
126
+ }
127
+
128
+ // contextLabelRefs
129
+ const contextLabelRefs = spec.contextLabelRefs || [];
130
+ let allContextLabelsFound = true;
131
+ for (const ref of contextLabelRefs) {
132
+ const labels = resources.AgentContextLabel || [];
133
+ const label = labels.find((l) => l.metadata?.name === ref);
134
+ if (label) {
135
+ resolvedContextLabels.push(label.metadata.name);
136
+ } else {
137
+ allContextLabelsFound = false;
138
+ missing.push(`AgentContextLabel/${ref}`);
139
+ }
140
+ }
141
+
142
+ // --- Build conditions ---
143
+ const allRefsFound = missing.length === 0;
144
+ conditions.push({
145
+ type: 'CapabilitiesResolved',
146
+ status: allRefsFound ? 'True' : 'False',
147
+ reason: allRefsFound ? 'AllRefsResolved' : 'MissingRefs',
148
+ message: allRefsFound ? 'All capability references resolved' : `Missing: ${missing.join(', ')}`
149
+ });
150
+
151
+ conditions.push({
152
+ type: 'ToolsAdmitted',
153
+ status: (toolPolicyFound || !toolPolicyRef) ? 'True' : 'False',
154
+ reason: !toolPolicyRef ? 'NoToolPolicyRef' : toolPolicyFound ? 'ToolPolicyResolved' : 'ToolPolicyMissing',
155
+ message: !toolPolicyRef ? 'No tool policy reference set' : toolPolicyFound ? 'Tool policy resolved' : `AgentToolProfile/${toolPolicyRef} not found`
156
+ });
157
+
158
+ conditions.push({
159
+ type: 'McpHealthy',
160
+ status: allMcpFound ? 'True' : 'False',
161
+ reason: allMcpFound ? 'AllMcpServersExist' : 'MissingMcpServers',
162
+ message: allMcpFound ? 'All MCP servers exist (health check deferred)' : `Missing MCP servers: ${mcpServerRefs.filter((ref) => !resolvedMcpServers.includes(ref)).join(', ')}`
163
+ });
164
+
165
+ conditions.push({
166
+ type: 'SkillsValidated',
167
+ status: (allSkillsFound && allSkillsValid) ? 'True' : 'False',
168
+ reason: !allSkillsFound ? 'MissingSkills' : !allSkillsValid ? 'InvalidSkillFormat' : 'AllSkillsValid',
169
+ message: !allSkillsFound ? `Missing skills: ${skillRefs.filter((ref) => !resolvedSkills.includes(ref)).join(', ')}` : !allSkillsValid ? 'Some skills have invalid format or missing sourceRef' : 'All skills validated'
170
+ });
171
+
172
+ conditions.push({
173
+ type: 'SubagentsValid',
174
+ status: (allSubagentsFound && allSubagentsValid) ? 'True' : 'False',
175
+ reason: !allSubagentsFound ? 'MissingSubagents' : !allSubagentsValid ? 'InvalidSubagentTaskKinds' : 'AllSubagentsValid',
176
+ message: !allSubagentsFound ? `Missing subagents: ${subagentRefs.filter((ref) => !resolvedSubagents.includes(ref)).join(', ')}` : !allSubagentsValid ? 'Some subagents have invalid or empty taskKinds' : 'All subagents validated'
177
+ });
178
+
179
+ conditions.push({
180
+ type: 'ContextLabelsValid',
181
+ status: allContextLabelsFound ? 'True' : 'False',
182
+ reason: allContextLabelsFound ? 'AllContextLabelsExist' : 'MissingContextLabels',
183
+ message: allContextLabelsFound ? 'All context labels exist' : `Missing context labels: ${contextLabelRefs.filter((ref) => !resolvedContextLabels.includes(ref)).join(', ')}`
184
+ });
185
+
186
+ // --- Permission review conditions via permissionReviewer ---
187
+ const serviceAccountRef = spec.runtimeIdentity?.serviceAccountRef || spec.runtimeIdentity;
188
+ const serviceAccounts = resources.AgentServiceAccount || [];
189
+ const serviceAccount = serviceAccounts.find((sa) => sa.metadata?.name === serviceAccountRef);
190
+ const runtimeIdentityReady = Boolean(serviceAccount);
191
+
192
+ conditions.push({
193
+ type: 'RuntimeIdentityReady',
194
+ status: runtimeIdentityReady ? 'True' : 'False',
195
+ reason: runtimeIdentityReady ? 'ServiceAccountBound' : 'MissingServiceAccount',
196
+ message: runtimeIdentityReady ? `AgentServiceAccount ${serviceAccountRef} bound` : `AgentServiceAccount ${serviceAccountRef || 'undefined'} not found`
197
+ });
198
+
199
+ // Run permission review for roles, secrets, config
200
+ const permissionReview = permissionReviewer.reviewPermissions({
201
+ repository: stack?.metadata?.labels?.repository || 'unknown',
202
+ ref: stack?.metadata?.labels?.ref || 'main',
203
+ actor: stack?.metadata?.labels?.actor || 'system',
204
+ agentStack: stack?.metadata?.name,
205
+ triggerSource: 'reconciliation',
206
+ taskKind: spec.taskKind || 'general',
207
+ resources
208
+ });
209
+
210
+ const rolesAdmitted = !permissionReview.reasons.some((r) => r.severity === 'error' && r.message.includes('AgentRoleBinding'));
211
+ const secretsAdmitted = !permissionReview.reasons.some((r) => r.severity === 'error' && r.message.includes('AgentSecretGrant'));
212
+ const configAdmitted = !permissionReview.reasons.some((r) => r.severity === 'error' && r.message.includes('AgentConfigGrant'));
213
+
214
+ conditions.push({
215
+ type: 'RolesAdmitted',
216
+ status: rolesAdmitted ? 'True' : 'False',
217
+ reason: rolesAdmitted ? 'RoleBindingsResolved' : 'MissingRoleBindings',
218
+ message: rolesAdmitted ? 'Role bindings satisfied' : 'Missing required AgentRoleBinding resources'
219
+ });
220
+
221
+ conditions.push({
222
+ type: 'SecretsAdmitted',
223
+ status: secretsAdmitted ? 'True' : 'False',
224
+ reason: secretsAdmitted ? 'SecretGrantsResolved' : 'MissingSecretGrants',
225
+ message: secretsAdmitted ? 'Secret grants satisfied' : 'Missing required AgentSecretGrant resources'
226
+ });
227
+
228
+ conditions.push({
229
+ type: 'ConfigAdmitted',
230
+ status: configAdmitted ? 'True' : 'False',
231
+ reason: configAdmitted ? 'ConfigGrantsResolved' : 'MissingConfigGrants',
232
+ message: configAdmitted ? 'Config grants satisfied' : 'Missing required AgentConfigGrant resources'
233
+ });
234
+
235
+ // --- Ready condition: true only if ALL other conditions are true ---
236
+ const allTrue = conditions.every((c) => c.status === 'True');
237
+ const hasErrors = conditions.some((c) => c.status === 'False');
238
+
239
+ conditions.push({
240
+ type: 'Ready',
241
+ status: allTrue ? 'True' : 'False',
242
+ reason: allTrue ? 'StackReady' : 'StackNotReady',
243
+ message: allTrue ? 'All conditions met' : `Failing conditions: ${conditions.filter((c) => c.status === 'False').map((c) => c.type).join(', ')}`
244
+ });
245
+
246
+ return {
247
+ conditions: clone(conditions),
248
+ capabilities: {
249
+ tools: clone(resolvedTools),
250
+ mcpServers: clone(resolvedMcpServers),
251
+ skills: clone(resolvedSkills),
252
+ subagents: clone(resolvedSubagents),
253
+ contextLabels: clone(resolvedContextLabels)
254
+ },
255
+ validation: allTrue ? 'valid' : hasErrors ? 'invalid' : 'warning',
256
+ permissionDecision: permissionReview.decision
257
+ };
258
+ },
259
+
260
+ listStackCapabilities(stack, resources = {}) {
261
+ const spec = stack?.spec || {};
262
+ const capabilities = [];
263
+
264
+ // Tools
265
+ const toolPolicyRef = spec.toolPolicy || spec.toolPolicyRef || null;
266
+ if (toolPolicyRef) {
267
+ const profiles = resources.AgentToolProfile || [];
268
+ const profile = profiles.find((p) => p.metadata?.name === toolPolicyRef);
269
+ capabilities.push({
270
+ kind: 'tool',
271
+ name: toolPolicyRef,
272
+ status: profile ? 'resolved' : 'missing',
273
+ ref: toolPolicyRef
274
+ });
275
+ }
276
+
277
+ // MCP Servers
278
+ for (const ref of spec.mcpServerRefs || []) {
279
+ const servers = resources.AgentMcpServer || [];
280
+ const server = servers.find((s) => s.metadata?.name === ref);
281
+ capabilities.push({
282
+ kind: 'mcp',
283
+ name: ref,
284
+ status: server ? 'resolved' : 'missing',
285
+ ref
286
+ });
287
+ }
288
+
289
+ // Skills
290
+ for (const ref of spec.skillRefs || []) {
291
+ const skills = resources.AgentSkill || [];
292
+ const skill = skills.find((s) => s.metadata?.name === ref);
293
+ capabilities.push({
294
+ kind: 'skill',
295
+ name: ref,
296
+ status: skill ? 'resolved' : 'missing',
297
+ ref
298
+ });
299
+ }
300
+
301
+ // Subagents
302
+ for (const ref of spec.subagentRefs || []) {
303
+ const subagents = resources.AgentSubagent || [];
304
+ const subagent = subagents.find((s) => s.metadata?.name === ref);
305
+ capabilities.push({
306
+ kind: 'subagent',
307
+ name: ref,
308
+ status: subagent ? 'resolved' : 'missing',
309
+ ref
310
+ });
311
+ }
312
+
313
+ // Context Labels
314
+ for (const ref of spec.contextLabelRefs || []) {
315
+ const labels = resources.AgentContextLabel || [];
316
+ const label = labels.find((l) => l.metadata?.name === ref);
317
+ capabilities.push({
318
+ kind: 'contextLabel',
319
+ name: ref,
320
+ status: label ? 'resolved' : 'missing',
321
+ ref
322
+ });
323
+ }
324
+
325
+ return capabilities;
326
+ },
327
+
328
+ /**
329
+ * Perform a health check for an AgentMcpServer resource.
330
+ * If no endpoint is configured in spec, returns { status: 'unknown', reason: 'no-endpoint' }.
331
+ * Otherwise performs a real HTTP GET with a 3s timeout.
332
+ * @param {object} mcpServer
333
+ * @returns {Promise<{ serverName: string, status: string, latencyMs?: number, reason?: string, error?: string }>}
334
+ */
335
+ async checkMcpHealth(mcpServer) {
336
+ const serverName = mcpServer?.metadata?.name;
337
+ const endpoint = mcpServer?.spec?.endpoint;
338
+
339
+ if (!endpoint) {
340
+ return { serverName, status: 'unknown', reason: 'no-endpoint' };
341
+ }
342
+
343
+ const checkResult = await performMcpHealthCheck(endpoint, fetchFn);
344
+ return { serverName, ...checkResult };
345
+ }
346
+ };
347
+ }
@@ -0,0 +1,160 @@
1
+ import { createResource, clone } from './resource-model.js';
2
+
3
+ export const AGENT_SUBAGENT_CONTROLLER_BOUNDARY = {
4
+ role: 'agent-subagent-controller',
5
+ scope: 'Subagent dispatch orchestration with tool scoping, role-based routing, and supervision protocol',
6
+ owns: ['subagent validation', 'dispatch record creation', 'tool scope resolution', 'task routing', 'supervision config'],
7
+ delegatesTo: ['resource-model'],
8
+ mustNotOwn: ['secret values', 'Agent Mux sessions', 'parent session lifecycle']
9
+ };
10
+
11
+ const DEFAULT_SUPERVISION = {
12
+ monitorInterval: 60,
13
+ maxDuration: 7200,
14
+ autoTerminate: false
15
+ };
16
+
17
+ export function createAgentSubagentController() {
18
+ return {
19
+ role: 'agent-subagent-controller',
20
+
21
+ /**
22
+ * Validate an AgentSubagent resource.
23
+ * Checks for required fields beyond what the CRD spec.requiredSpec covers:
24
+ * - metadata.name
25
+ * - spec.parentStackRef
26
+ * - spec.role
27
+ */
28
+ validate(subagent) {
29
+ const errors = [];
30
+
31
+ if (!subagent?.metadata?.name) {
32
+ errors.push('metadata.name is required');
33
+ }
34
+
35
+ const spec = subagent?.spec || {};
36
+
37
+ if (!spec.parentStackRef) {
38
+ errors.push('spec.parentStackRef is required');
39
+ }
40
+
41
+ if (!spec.role) {
42
+ errors.push('spec.role is required');
43
+ }
44
+
45
+ return {
46
+ valid: errors.length === 0,
47
+ errors
48
+ };
49
+ },
50
+
51
+ /**
52
+ * Get the tool scope for a subagent.
53
+ * Returns { unrestricted: true, allowed: [], denied: [] } when no toolScope is set.
54
+ * Returns { unrestricted: false, allowed: [...], denied: [...] } when toolScope is configured.
55
+ */
56
+ getToolScope(subagent) {
57
+ const toolScope = subagent?.spec?.toolScope;
58
+ if (!toolScope) {
59
+ return {
60
+ unrestricted: true,
61
+ allowed: [],
62
+ denied: []
63
+ };
64
+ }
65
+ return {
66
+ unrestricted: false,
67
+ allowed: clone(toolScope.allowed || []),
68
+ denied: clone(toolScope.denied || [])
69
+ };
70
+ },
71
+
72
+ /**
73
+ * Get the list of explicitly denied tools for a subagent.
74
+ */
75
+ getDeniedTools(subagent) {
76
+ const toolScope = subagent?.spec?.toolScope;
77
+ return clone(toolScope?.denied || []);
78
+ },
79
+
80
+ /**
81
+ * Dispatch a subagent — creates a dispatch record linking the subagent to
82
+ * a parent session. Requires parentSessionRef to be provided.
83
+ */
84
+ dispatchSubagent({ subagent, parentSessionRef, taskKind, namespace = 'default', organizationRef = 'default', resources = {} }) {
85
+ if (!parentSessionRef) {
86
+ return {
87
+ error: true,
88
+ reason: 'parentSessionRef-required',
89
+ message: 'parentSessionRef is required to dispatch a subagent'
90
+ };
91
+ }
92
+
93
+ const subagentName = subagent?.metadata?.name;
94
+ const parentStackRef = subagent?.spec?.parentStackRef;
95
+ const role = subagent?.spec?.role;
96
+ const recordName = `subagent-dispatch-${subagentName}-${Date.now()}`;
97
+
98
+ const dispatchRecord = createResource(
99
+ 'AgentDispatchRun',
100
+ { name: recordName, namespace },
101
+ {
102
+ organizationRef,
103
+ repository: resources.AgentStack?.[0]?.spec?.repositoryRef || 'unknown',
104
+ sourceRefs: [],
105
+ agentStack: parentStackRef || 'unknown',
106
+ taskKind: taskKind || 'general',
107
+ parentSessionRef,
108
+ subagentRef: subagentName,
109
+ subagentRole: role
110
+ }
111
+ );
112
+
113
+ dispatchRecord.status = { phase: 'Queued', queuedAt: new Date().toISOString() };
114
+
115
+ return {
116
+ dispatchRecord: clone(dispatchRecord)
117
+ };
118
+ },
119
+
120
+ /**
121
+ * Get supervision configuration for the subagent.
122
+ * Returns configured values or defaults when supervision is not set.
123
+ */
124
+ getSupervisionConfig(subagent) {
125
+ const supervision = subagent?.spec?.supervision;
126
+ if (!supervision) {
127
+ return { ...DEFAULT_SUPERVISION };
128
+ }
129
+ return {
130
+ monitorInterval: supervision.monitorInterval !== undefined ? supervision.monitorInterval : DEFAULT_SUPERVISION.monitorInterval,
131
+ maxDuration: supervision.maxDuration !== undefined ? supervision.maxDuration : DEFAULT_SUPERVISION.maxDuration,
132
+ autoTerminate: supervision.autoTerminate !== undefined ? supervision.autoTerminate : DEFAULT_SUPERVISION.autoTerminate
133
+ };
134
+ },
135
+
136
+ /**
137
+ * Validate task routing: checks that the requested role maps to an available subagent.
138
+ */
139
+ validateTaskRouting({ role, taskKind, subagents = [] }) {
140
+ const match = subagents.find(s => s?.spec?.role === role);
141
+ if (!match) {
142
+ return {
143
+ valid: false,
144
+ error: `No subagent found for role '${role}'`
145
+ };
146
+ }
147
+ return {
148
+ valid: true,
149
+ matchedSubagent: clone(match)
150
+ };
151
+ },
152
+
153
+ /**
154
+ * Get current status of a subagent from its status field.
155
+ */
156
+ getSubagentStatus(subagent) {
157
+ return clone(subagent?.status || { phase: 'idle' });
158
+ }
159
+ };
160
+ }
@@ -0,0 +1,121 @@
1
+ // Agent Transport Binding Controller — Slice 1.2b
2
+ // Manages AgentTransportBinding resources: connection config validation,
3
+ // health status tracking, and reconnect policy enforcement.
4
+
5
+ export const AGENT_TRANSPORT_BINDING_CONTROLLER_BOUNDARY = {
6
+ role: 'agent-transport-binding-controller',
7
+ scope: 'AgentTransportBinding lifecycle: validation, connection status tracking, reconnect policy enforcement',
8
+ owns: ['binding validation', 'connection status tracking', 'reconnect policy enforcement'],
9
+ delegatesTo: ['resource-model', 'agent-adapter-controller'],
10
+ mustNotOwn: ['secret values', 'dispatch execution', 'Agent Mux sessions', 'adapter implementation']
11
+ };
12
+
13
+ const VALID_PROTOCOLS = ['stdio', 'http', 'websocket', 'unix'];
14
+
15
+ const DEFAULT_RECONNECT_POLICY = Object.freeze({
16
+ maxRetries: 3,
17
+ backoffMs: 1000,
18
+ maxBackoffMs: 30000
19
+ });
20
+
21
+ /**
22
+ * Validate an AgentTransportBinding resource. Returns { valid, errors }.
23
+ * @param {object} resource
24
+ * @returns {{ valid: boolean, errors: string[] }}
25
+ */
26
+ export function validateAgentTransportBinding(resource) {
27
+ const errors = [];
28
+
29
+ // Guard against null/undefined resource
30
+ if (resource == null) {
31
+ errors.push('resource must not be null or undefined');
32
+ return { valid: false, errors };
33
+ }
34
+
35
+ // Validate metadata.name
36
+ if (!resource?.metadata?.name) {
37
+ errors.push('metadata.name is required');
38
+ }
39
+
40
+ const spec = resource?.spec || {};
41
+
42
+ // Validate adapterRef
43
+ if (!spec.adapterRef) {
44
+ errors.push('spec.adapterRef is required');
45
+ }
46
+
47
+ // Validate endpoint
48
+ if (!spec.endpoint) {
49
+ errors.push('spec.endpoint is required');
50
+ }
51
+
52
+ // Validate protocol
53
+ const protocol = spec.protocol;
54
+ if (!protocol) {
55
+ errors.push(`spec.protocol is required; valid protocols are: ${VALID_PROTOCOLS.join(', ')}`);
56
+ } else if (!VALID_PROTOCOLS.includes(protocol)) {
57
+ errors.push(`spec.protocol "${protocol}" is not supported; valid protocols are: ${VALID_PROTOCOLS.join(', ')}`);
58
+ }
59
+
60
+ return { valid: errors.length === 0, errors };
61
+ }
62
+
63
+ /**
64
+ * Factory that returns an AgentTransportBinding controller instance.
65
+ */
66
+ export function createAgentTransportBindingController() {
67
+ return {
68
+ role: 'agent-transport-binding-controller',
69
+
70
+ /**
71
+ * Validate an AgentTransportBinding resource.
72
+ * @param {object} resource
73
+ * @returns {{ valid: boolean, errors: string[] }}
74
+ */
75
+ validate(resource) {
76
+ return validateAgentTransportBinding(resource);
77
+ },
78
+
79
+ /**
80
+ * Return the current connection status for a transport binding.
81
+ * Reads from resource.status.connectionStatus when available,
82
+ * otherwise returns 'unknown'.
83
+ * @param {object} resource
84
+ * @returns {{ bindingName: string, connectionStatus: string }}
85
+ */
86
+ getConnectionStatus(resource) {
87
+ if (resource == null) {
88
+ throw new Error('resource must not be null or undefined');
89
+ }
90
+ const bindingName = resource?.metadata?.name;
91
+ const connectionStatus = resource?.status?.connectionStatus ?? 'unknown';
92
+ return { bindingName, connectionStatus };
93
+ },
94
+
95
+ /**
96
+ * Return the reconnect policy for a transport binding.
97
+ * Merges spec.reconnectPolicy values with defaults.
98
+ * @param {object} resource
99
+ * @returns {{ maxRetries: number, backoffMs: number, maxBackoffMs: number }}
100
+ */
101
+ getReconnectPolicy(resource) {
102
+ if (resource == null) {
103
+ throw new Error('resource must not be null or undefined');
104
+ }
105
+ const specPolicy = resource?.spec?.reconnectPolicy ?? {};
106
+ return {
107
+ maxRetries: specPolicy.maxRetries ?? DEFAULT_RECONNECT_POLICY.maxRetries,
108
+ backoffMs: specPolicy.backoffMs ?? DEFAULT_RECONNECT_POLICY.backoffMs,
109
+ maxBackoffMs: specPolicy.maxBackoffMs ?? DEFAULT_RECONNECT_POLICY.maxBackoffMs
110
+ };
111
+ },
112
+
113
+ /**
114
+ * Return the list of supported protocol types.
115
+ * @returns {string[]}
116
+ */
117
+ getSupportedProtocols() {
118
+ return [...VALID_PROTOCOLS];
119
+ }
120
+ };
121
+ }