@a5c-ai/kradle 5.0.1-staging.3abdf9534c25

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 (295) hide show
  1. package/Dockerfile +31 -0
  2. package/README.md +187 -0
  3. package/bin/kradle-demo.mjs +23 -0
  4. package/bin/kradle-server.mjs +14 -0
  5. package/dist/kradle-controller-ui.json +3482 -0
  6. package/dist/kradle-lifecycle.json +201 -0
  7. package/dist/kradle-runtime-snapshot.json +3125 -0
  8. package/dist/kradle-summary.json +724 -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-kradle-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/architecture-v2.md +2759 -0
  60. package/docs/components/control-plane.md +78 -0
  61. package/docs/components/data-plane.md +69 -0
  62. package/docs/components/hooks-events.md +67 -0
  63. package/docs/components/identity-rbac-policy.md +73 -0
  64. package/docs/components/kubevela-oam.md +70 -0
  65. package/docs/components/operations-publishing.md +81 -0
  66. package/docs/components/runners-ci.md +66 -0
  67. package/docs/components/web-ui.md +94 -0
  68. package/docs/crd-behaviors-and-relationships.md +3926 -0
  69. package/docs/external/README.md +47 -0
  70. package/docs/external/bidirectional-sync-design.md +134 -0
  71. package/docs/external/cicd-interface.md +64 -0
  72. package/docs/external/external-backend-controllers.md +170 -0
  73. package/docs/external/external-backend-crds.md +234 -0
  74. package/docs/external/external-backend-ui-spec.md +151 -0
  75. package/docs/external/external-backend-ux-flows.md +115 -0
  76. package/docs/external/external-object-mapping.md +125 -0
  77. package/docs/external/git-forge-interface.md +68 -0
  78. package/docs/external/github-integration-design.md +151 -0
  79. package/docs/external/issue-tracking-interface.md +66 -0
  80. package/docs/external/provider-capability-manifests.md +204 -0
  81. package/docs/external/provider-catalog.md +139 -0
  82. package/docs/external/provider-rollout-testing.md +78 -0
  83. package/docs/external/research-results.md +48 -0
  84. package/docs/external/security-auth-permissions.md +81 -0
  85. package/docs/external/sync-state-machines.md +108 -0
  86. package/docs/external/unified-external-backend-model.md +107 -0
  87. package/docs/external/user-facing-changes.md +67 -0
  88. package/docs/gaps.md +161 -0
  89. package/docs/install.md +94 -0
  90. package/docs/integration-and-design-decisions.md +1530 -0
  91. package/docs/kradle-design.md +334 -0
  92. package/docs/local-minikube.md +55 -0
  93. package/docs/ontology/README.md +32 -0
  94. package/docs/ontology/bounded-contexts.md +29 -0
  95. package/docs/ontology/events-and-hooks.md +32 -0
  96. package/docs/ontology/oam-kubevela.md +32 -0
  97. package/docs/ontology/operations-and-release.md +25 -0
  98. package/docs/ontology/personas-and-actors.md +32 -0
  99. package/docs/ontology/policies-and-invariants.md +33 -0
  100. package/docs/ontology/problem-space.md +30 -0
  101. package/docs/ontology/resource-contracts.md +40 -0
  102. package/docs/ontology/resource-taxonomy.md +42 -0
  103. package/docs/ontology/runners-and-ci.md +29 -0
  104. package/docs/ontology/solution-space.md +24 -0
  105. package/docs/ontology/storage-and-data-boundaries.md +29 -0
  106. package/docs/ontology/validation-matrix.md +24 -0
  107. package/docs/ontology/web-ui-excellent-flows.md +32 -0
  108. package/docs/ontology/workflows.md +39 -0
  109. package/docs/ontology/world.md +35 -0
  110. package/docs/openapi.yaml +1291 -0
  111. package/docs/product-requirements.md +62 -0
  112. package/docs/requirements-v2.md +235 -0
  113. package/docs/roadmap-mvp.md +87 -0
  114. package/docs/sdk-api-reference.md +1108 -0
  115. package/docs/system-requirements.md +90 -0
  116. package/docs/system-spec-v2.md +1230 -0
  117. package/docs/tests/README.md +53 -0
  118. package/docs/tests/agent-qa-plan.md +63 -0
  119. package/docs/tests/browser-ui-tests.md +62 -0
  120. package/docs/tests/ci-quality-gates.md +48 -0
  121. package/docs/tests/coverage-model.md +64 -0
  122. package/docs/tests/e2e-scenario-tests.md +53 -0
  123. package/docs/tests/fixtures-test-data.md +63 -0
  124. package/docs/tests/observability-reliability-tests.md +54 -0
  125. package/docs/tests/product-test-matrix.md +145 -0
  126. package/docs/tests/qa-adoption-roadmap.md +130 -0
  127. package/docs/tests/qa-automation-plan.md +101 -0
  128. package/docs/tests/security-compliance-tests.md +57 -0
  129. package/docs/tests/test-framework-tools.md +88 -0
  130. package/docs/tests/test-suite-layout.md +121 -0
  131. package/docs/tests/unit-integration-tests.md +48 -0
  132. package/docs/todo-kyverno +714 -0
  133. package/docs/todos.md +4 -0
  134. package/docs/user-stories.md +78 -0
  135. package/docs/web-console-spec.md +533 -0
  136. package/examples/minikube-demo.yaml +190 -0
  137. package/examples/oam-application.yaml +23 -0
  138. package/examples/policy-kyverno-pr-title.yaml +18 -0
  139. package/package.json +66 -0
  140. package/scripts/build.mjs +29 -0
  141. package/scripts/setup-minikube.mjs +65 -0
  142. package/scripts/smoke.mjs +37 -0
  143. package/scripts/validate-doc-coverage.mjs +152 -0
  144. package/scripts/validate-package.mjs +95 -0
  145. package/scripts/validate-ui.mjs +305 -0
  146. package/src/agent-adapter-controller.js +169 -0
  147. package/src/agent-approval-controller.js +170 -0
  148. package/src/agent-context-bundles.js +242 -0
  149. package/src/agent-dispatch-controller.js +549 -0
  150. package/src/agent-gateway-config-controller.js +147 -0
  151. package/src/agent-identity-migration.js +115 -0
  152. package/src/agent-memory-controller.js +357 -0
  153. package/src/agent-memory-import.js +327 -0
  154. package/src/agent-memory-query.js +292 -0
  155. package/src/agent-memory-repository-source-controller.js +255 -0
  156. package/src/agent-mux-client.js +589 -0
  157. package/src/agent-permission-review.js +250 -0
  158. package/src/agent-persona-controller.js +135 -0
  159. package/src/agent-project-controller.js +117 -0
  160. package/src/agent-prompt-composition.js +55 -0
  161. package/src/agent-provider-config-controller.js +151 -0
  162. package/src/agent-secret-config-grant-controller.js +282 -0
  163. package/src/agent-session-transcript-controller.js +189 -0
  164. package/src/agent-stack-controller.js +421 -0
  165. package/src/agent-subagent-controller.js +160 -0
  166. package/src/agent-transport-binding-controller.js +121 -0
  167. package/src/agent-trigger-controller.js +387 -0
  168. package/src/agent-workspace-controller.js +702 -0
  169. package/src/agent-writeback-controller.js +302 -0
  170. package/src/api-controller.js +621 -0
  171. package/src/argocd-gitops.js +43 -0
  172. package/src/artifact-registry-controller.js +542 -0
  173. package/src/assistant-runtime.js +284 -0
  174. package/src/async-controller.js +207 -0
  175. package/src/audit-controller.js +191 -0
  176. package/src/auth.js +310 -0
  177. package/src/component-catalog.js +41 -0
  178. package/src/control-plane.js +136 -0
  179. package/src/controller-client.js +112 -0
  180. package/src/controller-ui.js +620 -0
  181. package/src/data-plane.js +179 -0
  182. package/src/event-bus.js +397 -0
  183. package/src/external/conflict-controller.js +225 -0
  184. package/src/external/github/auth.js +96 -0
  185. package/src/external/github/cicd.js +180 -0
  186. package/src/external/github/git-forge.js +240 -0
  187. package/src/external/github/index.js +144 -0
  188. package/src/external/github/issue-tracking.js +163 -0
  189. package/src/external/provider-adapter.js +161 -0
  190. package/src/external/provider-resource-factory.js +221 -0
  191. package/src/external/sync-controller.js +235 -0
  192. package/src/external/webhook-controller.js +144 -0
  193. package/src/external/write-controller.js +283 -0
  194. package/src/gitea-backend.js +131 -0
  195. package/src/gitea-service.js +173 -0
  196. package/src/handoff.js +98 -0
  197. package/src/health-probes.js +134 -0
  198. package/src/hooks-events.js +63 -0
  199. package/src/hooks-lifecycle.js +117 -0
  200. package/src/http-server.js +409 -0
  201. package/src/identity-policy.js +86 -0
  202. package/src/index.js +71 -0
  203. package/src/jitsi-agent-bridge.js +141 -0
  204. package/src/jitsi-meeting-controller.js +291 -0
  205. package/src/jitsi-sync-controller.js +198 -0
  206. package/src/kradle-inference-service-controller.js +246 -0
  207. package/src/kubernetes-controller-async.js +531 -0
  208. package/src/kubernetes-controller.js +904 -0
  209. package/src/kubernetes-resource-gateway.js +48 -0
  210. package/src/model-route-controller.js +364 -0
  211. package/src/notification-controller.js +178 -0
  212. package/src/operations.js +112 -0
  213. package/src/org-scoping.js +5 -0
  214. package/src/resource-model.js +282 -0
  215. package/src/runner-controller.js +272 -0
  216. package/src/runners-ci.js +48 -0
  217. package/src/runtime.js +196 -0
  218. package/src/snapshot-cache.js +157 -0
  219. package/src/virtual-model-controller.js +538 -0
  220. package/src/virtual-model-hook-bridge.js +200 -0
  221. package/src/web-ui.js +40 -0
  222. package/tests/agent-adapter-controller.test.js +361 -0
  223. package/tests/agent-approval-controller.test.js +173 -0
  224. package/tests/agent-context-bundles.test.js +278 -0
  225. package/tests/agent-dispatch-controller.test.js +679 -0
  226. package/tests/agent-gateway-config-controller.test.js +386 -0
  227. package/tests/agent-identity-migration.test.js +87 -0
  228. package/tests/agent-memory-controller.test.js +461 -0
  229. package/tests/agent-memory-import-snapshot.test.js +477 -0
  230. package/tests/agent-memory-query.test.js +404 -0
  231. package/tests/agent-memory-repository-source.test.js +514 -0
  232. package/tests/agent-mux-client.test.js +389 -0
  233. package/tests/agent-mux-integration.test.js +971 -0
  234. package/tests/agent-permission-review-v2.test.js +317 -0
  235. package/tests/agent-permission-review.test.js +209 -0
  236. package/tests/agent-persona-controller.test.js +127 -0
  237. package/tests/agent-project-controller.test.js +302 -0
  238. package/tests/agent-prompt-composition.test.js +76 -0
  239. package/tests/agent-provider-config-controller.test.js +376 -0
  240. package/tests/agent-resources.test.js +303 -0
  241. package/tests/agent-secret-config-grant.test.js +231 -0
  242. package/tests/agent-session-transcript-controller.test.js +499 -0
  243. package/tests/agent-stack-controller.test.js +283 -0
  244. package/tests/agent-subagent-controller.test.js +201 -0
  245. package/tests/agent-transport-binding-controller.test.js +294 -0
  246. package/tests/agent-trigger-controller.test.js +271 -0
  247. package/tests/agent-trigger-routes.test.js +190 -0
  248. package/tests/agent-trigger-sources.test.js +245 -0
  249. package/tests/agent-workspace-controller.test.js +181 -0
  250. package/tests/agent-writeback.test.js +292 -0
  251. package/tests/approval-persistence.test.js +171 -0
  252. package/tests/artifact-registry.test.js +511 -0
  253. package/tests/assistant-runtime.test.js +506 -0
  254. package/tests/async-controller.test.js +252 -0
  255. package/tests/audit-controller.test.js +227 -0
  256. package/tests/codespace-controller.test.js +318 -0
  257. package/tests/controller-client.test.js +133 -0
  258. package/tests/deployment.test.js +527 -0
  259. package/tests/e2e/lifecycle.test.js +120 -0
  260. package/tests/event-bus-integration.test.js +355 -0
  261. package/tests/external-github-forge.test.js +560 -0
  262. package/tests/external-github-issues-cicd.test.js +520 -0
  263. package/tests/external-integration.test.js +470 -0
  264. package/tests/external-persistence.test.js +415 -0
  265. package/tests/external-provider-adapter.test.js +365 -0
  266. package/tests/external-resource-model.test.js +223 -0
  267. package/tests/external-webhook-sync.test.js +287 -0
  268. package/tests/external-write-conflict.test.js +353 -0
  269. package/tests/gitea-service.test.js +253 -0
  270. package/tests/health-check-real.test.js +165 -0
  271. package/tests/health-probes.test.js +90 -0
  272. package/tests/hooks-lifecycle.test.js +364 -0
  273. package/tests/integration/full-flow.test.js +266 -0
  274. package/tests/jitsi-agent-bridge.test.js +119 -0
  275. package/tests/jitsi-helm-integration.test.js +77 -0
  276. package/tests/jitsi-meeting-controller.test.js +170 -0
  277. package/tests/jitsi-resource-model.test.js +73 -0
  278. package/tests/jitsi-sync-controller.test.js +112 -0
  279. package/tests/kradle-inference-service.test.js +689 -0
  280. package/tests/kradle.test.js +779 -0
  281. package/tests/memory-search-wiring.test.js +270 -0
  282. package/tests/model-route-controller.test.js +733 -0
  283. package/tests/notification-controller.test.js +196 -0
  284. package/tests/notification-integration.test.js +179 -0
  285. package/tests/org-scoping.test.js +687 -0
  286. package/tests/runner-controller.test.js +327 -0
  287. package/tests/runner-integration.test.js +231 -0
  288. package/tests/session-cookie-hmac.test.js +151 -0
  289. package/tests/snapshot-performance.test.js +315 -0
  290. package/tests/sse-events.test.js +107 -0
  291. package/tests/virtual-model-controller.test.js +877 -0
  292. package/tests/virtual-model-hook-bridge.test.js +384 -0
  293. package/tests/webhook-trigger.test.js +198 -0
  294. package/tests/workspace-volumes.test.js +312 -0
  295. package/tests/writeback-persistence.test.js +207 -0
@@ -0,0 +1,151 @@
1
+ // Agent Provider Config Controller — Slice 1.2c
2
+ // Manages AgentProviderConfig resources: model provider config validation,
3
+ // endpoint resolution, credential ref validation, and feature flag management.
4
+
5
+ export const AGENT_PROVIDER_CONFIG_CONTROLLER_BOUNDARY = {
6
+ role: 'agent-provider-config-controller',
7
+ scope: 'AgentProviderConfig lifecycle: validation, endpoint resolution, credential ref validation, feature flags',
8
+ owns: ['provider config validation', 'endpoint resolution', 'credential ref validation', 'feature flag defaults', 'rate limit defaults'],
9
+ delegatesTo: ['resource-model'],
10
+ mustNotOwn: ['secret values', 'dispatch execution', 'Agent Mux sessions', 'adapter implementation']
11
+ };
12
+
13
+ const VALID_PROVIDER_TYPES = ['anthropic', 'openai', 'azure-openai', 'google-vertex', 'foundry', 'custom', 'kserve'];
14
+
15
+ const DEFAULT_ENDPOINTS = Object.freeze({
16
+ anthropic: 'https://api.anthropic.com/v1',
17
+ openai: 'https://api.openai.com/v1',
18
+ 'azure-openai': null, // requires explicit endpoint (tenant-specific)
19
+ 'google-vertex': 'https://us-central1-aiplatform.googleapis.com/v1',
20
+ foundry: null, // requires explicit endpoint
21
+ custom: null, // requires explicit endpoint
22
+ kserve: null // requires explicit endpoint from InferenceService discovery
23
+ });
24
+
25
+ const DEFAULT_FEATURE_FLAGS = Object.freeze({
26
+ streaming: true,
27
+ tool_use: true,
28
+ vision: false
29
+ });
30
+
31
+ const DEFAULT_RATE_LIMITS = Object.freeze({
32
+ requestsPerMinute: 60,
33
+ tokensPerMinute: 100000
34
+ });
35
+
36
+ /**
37
+ * Validate an AgentProviderConfig resource. Returns { valid, errors }.
38
+ * @param {object} resource
39
+ * @returns {{ valid: boolean, errors: string[] }}
40
+ */
41
+ export function validateAgentProviderConfig(resource) {
42
+ const errors = [];
43
+
44
+ // Guard against null/undefined resource
45
+ if (resource == null) {
46
+ errors.push('resource must not be null or undefined');
47
+ return { valid: false, errors };
48
+ }
49
+
50
+ // Validate metadata.name
51
+ if (!resource?.metadata?.name) {
52
+ errors.push('metadata.name is required');
53
+ }
54
+
55
+ const spec = resource?.spec || {};
56
+
57
+ // Validate providerType
58
+ const providerType = spec.providerType;
59
+ if (!providerType) {
60
+ errors.push(`spec.providerType is required; valid types are: ${VALID_PROVIDER_TYPES.join(', ')}`);
61
+ } else if (!VALID_PROVIDER_TYPES.includes(providerType)) {
62
+ errors.push(`spec.providerType "${providerType}" is not supported; valid types are: ${VALID_PROVIDER_TYPES.join(', ')}`);
63
+ }
64
+
65
+ // Validate credentialRef — always required for security
66
+ if (!spec.credentialRef) {
67
+ errors.push('spec.credentialRef is required; provide a Kubernetes Secret name for the API key');
68
+ }
69
+
70
+ return { valid: errors.length === 0, errors };
71
+ }
72
+
73
+ /**
74
+ * Factory that returns an AgentProviderConfig controller instance.
75
+ */
76
+ export function createAgentProviderConfigController() {
77
+ return {
78
+ role: 'agent-provider-config-controller',
79
+
80
+ /**
81
+ * Validate an AgentProviderConfig resource.
82
+ * @param {object} resource
83
+ * @returns {{ valid: boolean, errors: string[] }}
84
+ */
85
+ validate(resource) {
86
+ return validateAgentProviderConfig(resource);
87
+ },
88
+
89
+ /**
90
+ * Resolve the effective API endpoint for a provider config.
91
+ * Returns the explicit spec.endpoint if set, otherwise falls back to
92
+ * the well-known default for the provider type.
93
+ * @param {object} resource
94
+ * @returns {string|null}
95
+ */
96
+ resolveEndpoint(resource) {
97
+ if (resource == null) {
98
+ throw new Error('resource must not be null or undefined');
99
+ }
100
+ const spec = resource?.spec || {};
101
+
102
+ // If an explicit endpoint is set in spec, prefer it
103
+ if (spec.endpoint) {
104
+ return spec.endpoint;
105
+ }
106
+
107
+ // Fall back to the known default for the provider type
108
+ const providerType = spec.providerType;
109
+ return DEFAULT_ENDPOINTS[providerType] ?? null;
110
+ },
111
+
112
+ /**
113
+ * Return the effective feature flags for a provider config.
114
+ * Merges spec.featureFlags with defaults; spec values take precedence.
115
+ * @param {object} resource
116
+ * @returns {{ streaming: boolean, tool_use: boolean, vision: boolean, [key: string]: boolean }}
117
+ */
118
+ getFeatureFlags(resource) {
119
+ if (resource == null) {
120
+ throw new Error('resource must not be null or undefined');
121
+ }
122
+ const specFlags = resource?.spec?.featureFlags ?? {};
123
+ return { ...DEFAULT_FEATURE_FLAGS, ...specFlags };
124
+ },
125
+
126
+ /**
127
+ * Return the effective rate limit configuration for a provider config.
128
+ * Merges spec.rateLimits with defaults; spec values take precedence.
129
+ * @param {object} resource
130
+ * @returns {{ requestsPerMinute: number, tokensPerMinute: number }}
131
+ */
132
+ getRateLimits(resource) {
133
+ if (resource == null) {
134
+ throw new Error('resource must not be null or undefined');
135
+ }
136
+ const specLimits = resource?.spec?.rateLimits ?? {};
137
+ return {
138
+ requestsPerMinute: specLimits.requestsPerMinute ?? DEFAULT_RATE_LIMITS.requestsPerMinute,
139
+ tokensPerMinute: specLimits.tokensPerMinute ?? DEFAULT_RATE_LIMITS.tokensPerMinute
140
+ };
141
+ },
142
+
143
+ /**
144
+ * Return the list of supported provider types.
145
+ * @returns {string[]}
146
+ */
147
+ getSupportedProviderTypes() {
148
+ return [...VALID_PROVIDER_TYPES];
149
+ }
150
+ };
151
+ }
@@ -0,0 +1,282 @@
1
+ // Agent Secret/Config Grant Controller — Secret & ConfigMap grant lifecycle
2
+ //
3
+ // Provides:
4
+ // - createAgentSecretGrantController() — validates AgentSecretGrant resources
5
+ // - createAgentConfigGrantController() — validates AgentConfigGrant resources
6
+ // - listGrantsForAgent(agentRef) — filter grants by target agent
7
+ // - revokeGrant(grantName) — mark a grant as revoked
8
+
9
+ import { createResource, clone } from './resource-model.js';
10
+
11
+ export const AGENT_SECRET_GRANT_CONTROLLER_BOUNDARY = {
12
+ role: 'agent-secret-grant-controller',
13
+ scope: 'AgentSecretGrant lifecycle — creation, listing, revocation for agent-to-secret access grants',
14
+ owns: ['grant creation', 'grant listing', 'grant revocation', 'input validation'],
15
+ delegatesTo: ['resource-model'],
16
+ mustNotOwn: ['Kubernetes Secret storage', 'agent execution', 'secret value decryption', 'UI rendering']
17
+ };
18
+
19
+ export const AGENT_CONFIG_GRANT_CONTROLLER_BOUNDARY = {
20
+ role: 'agent-config-grant-controller',
21
+ scope: 'AgentConfigGrant lifecycle — creation, listing, revocation for agent-to-configmap access grants',
22
+ owns: ['grant creation', 'grant listing', 'grant revocation', 'input validation'],
23
+ delegatesTo: ['resource-model'],
24
+ mustNotOwn: ['Kubernetes ConfigMap storage', 'agent execution', 'UI rendering']
25
+ };
26
+
27
+ const VALID_PERMISSIONS = new Set(['read', 'use', 'mount']);
28
+
29
+ // ---------------------------------------------------------------------------
30
+ // Validation helpers
31
+ // ---------------------------------------------------------------------------
32
+
33
+ /**
34
+ * Validate an AgentSecretGrant input.
35
+ *
36
+ * @param {object} input
37
+ * @returns {{ valid: boolean, errors: string[] }}
38
+ */
39
+ export function validateAgentSecretGrant(input) {
40
+ const errors = [];
41
+ if (!input) return { valid: false, errors: ['input must not be null or undefined'] };
42
+ if (!input.name || typeof input.name !== 'string') errors.push('name is required and must be a non-empty string');
43
+ if (!input.orgRef || typeof input.orgRef !== 'string') errors.push('orgRef is required and must be a non-empty string');
44
+ if (!input.secretName || typeof input.secretName !== 'string') errors.push('secretName is required and must be a non-empty string');
45
+ if (!input.grantedTo || typeof input.grantedTo !== 'string') errors.push('grantedTo is required and must be a non-empty string');
46
+ if (!Array.isArray(input.permissions) || input.permissions.length === 0) {
47
+ errors.push('permissions is required and must be a non-empty array');
48
+ } else {
49
+ const invalid = input.permissions.filter((p) => !VALID_PERMISSIONS.has(p));
50
+ if (invalid.length > 0) errors.push(`invalid permissions: ${invalid.join(', ')}. Valid values: ${[...VALID_PERMISSIONS].join(', ')}`);
51
+ }
52
+ return { valid: errors.length === 0, errors };
53
+ }
54
+
55
+ /**
56
+ * Validate an AgentConfigGrant input.
57
+ *
58
+ * @param {object} input
59
+ * @returns {{ valid: boolean, errors: string[] }}
60
+ */
61
+ export function validateAgentConfigGrant(input) {
62
+ const errors = [];
63
+ if (!input) return { valid: false, errors: ['input must not be null or undefined'] };
64
+ if (!input.name || typeof input.name !== 'string') errors.push('name is required and must be a non-empty string');
65
+ if (!input.orgRef || typeof input.orgRef !== 'string') errors.push('orgRef is required and must be a non-empty string');
66
+ if (!input.configMapName || typeof input.configMapName !== 'string') errors.push('configMapName is required and must be a non-empty string');
67
+ if (!input.grantedTo || typeof input.grantedTo !== 'string') errors.push('grantedTo is required and must be a non-empty string');
68
+ return { valid: errors.length === 0, errors };
69
+ }
70
+
71
+ // ---------------------------------------------------------------------------
72
+ // listGrantsForAgent — shared utility
73
+ // ---------------------------------------------------------------------------
74
+
75
+ /**
76
+ * Filter grants by the grantedTo (agent ref) field from a collection of grant resources.
77
+ *
78
+ * @param {object[]} grants Array of AgentSecretGrant or AgentConfigGrant resources
79
+ * @param {string} agentRef The agent identifier to filter by
80
+ * @returns {object[]}
81
+ */
82
+ export function listGrantsForAgent(grants, agentRef) {
83
+ if (!agentRef) return [];
84
+ return grants.filter((g) => g.spec?.grantedTo === agentRef || g.spec?.subject === agentRef);
85
+ }
86
+
87
+ // ---------------------------------------------------------------------------
88
+ // revokeGrant — shared utility
89
+ // ---------------------------------------------------------------------------
90
+
91
+ /**
92
+ * Mark a grant as revoked by returning an updated copy of the resource.
93
+ *
94
+ * @param {object[]} grants Array of grant resources
95
+ * @param {string} grantName Name of the grant to revoke
96
+ * @returns {{ grant: object } | { error: true, reason: string, message: string }}
97
+ */
98
+ export function revokeGrant(grants, grantName) {
99
+ if (!grantName) return { error: true, reason: 'missing-name', message: 'grantName is required' };
100
+
101
+ const found = grants.find((g) => g.metadata?.name === grantName);
102
+ if (!found) return { error: true, reason: 'not-found', message: `Grant not found: ${grantName}` };
103
+
104
+ if (found.status?.phase === 'Revoked') {
105
+ return { error: true, reason: 'already-revoked', message: `Grant "${grantName}" is already revoked` };
106
+ }
107
+
108
+ const updated = clone(found);
109
+ updated.status = {
110
+ ...updated.status,
111
+ phase: 'Revoked',
112
+ revokedAt: new Date().toISOString()
113
+ };
114
+
115
+ return { grant: updated };
116
+ }
117
+
118
+ // ---------------------------------------------------------------------------
119
+ // createAgentSecretGrantController
120
+ // ---------------------------------------------------------------------------
121
+
122
+ /**
123
+ * Create a controller for AgentSecretGrant resources.
124
+ *
125
+ * @param {{ persistFn?: (resource: object) => Promise<any> }} [opts]
126
+ * @returns {object}
127
+ */
128
+ export function createAgentSecretGrantController({ persistFn } = {}) {
129
+ function persist(resource) {
130
+ if (typeof persistFn === 'function') {
131
+ Promise.resolve(persistFn(resource)).catch(() => {});
132
+ }
133
+ }
134
+
135
+ return {
136
+ role: 'agent-secret-grant-controller',
137
+
138
+ /**
139
+ * Create an AgentSecretGrant resource.
140
+ *
141
+ * @param {{ name, orgRef, secretName, grantedTo, permissions, namespace?, keys? }} input
142
+ * @returns {{ grant: object } | { error: true, message: string }}
143
+ */
144
+ createSecretGrant({
145
+ name,
146
+ orgRef,
147
+ secretName,
148
+ grantedTo,
149
+ permissions = ['read'],
150
+ namespace = 'default',
151
+ keys = []
152
+ }) {
153
+ const validation = validateAgentSecretGrant({ name, orgRef, secretName, grantedTo, permissions });
154
+ if (!validation.valid) {
155
+ return { error: true, message: validation.errors.join('; ') };
156
+ }
157
+
158
+ const now = new Date().toISOString();
159
+ const grant = createResource('AgentSecretGrant', { name, namespace }, {
160
+ organizationRef: orgRef,
161
+ orgRef,
162
+ secretName,
163
+ secretRef: secretName,
164
+ grantedTo,
165
+ subject: grantedTo,
166
+ permissions,
167
+ keys,
168
+ purpose: permissions.join(',')
169
+ });
170
+ grant.status = { phase: 'Active', createdAt: now };
171
+
172
+ persist(grant);
173
+ return { grant };
174
+ },
175
+
176
+ /**
177
+ * List all grants for a specific agent.
178
+ *
179
+ * @param {string} agentRef
180
+ * @param {object[]} grants
181
+ * @returns {object[]}
182
+ */
183
+ listGrantsForAgent(agentRef, grants = []) {
184
+ return listGrantsForAgent(grants, agentRef);
185
+ },
186
+
187
+ /**
188
+ * Revoke a grant by name.
189
+ *
190
+ * @param {string} grantName
191
+ * @param {object[]} grants
192
+ * @returns {{ grant: object } | { error: true, reason: string, message: string }}
193
+ */
194
+ revokeGrant(grantName, grants = []) {
195
+ const result = revokeGrant(grants, grantName);
196
+ if (!result.error && result.grant) persist(result.grant);
197
+ return result;
198
+ }
199
+ };
200
+ }
201
+
202
+ // ---------------------------------------------------------------------------
203
+ // createAgentConfigGrantController
204
+ // ---------------------------------------------------------------------------
205
+
206
+ /**
207
+ * Create a controller for AgentConfigGrant resources.
208
+ *
209
+ * @param {{ persistFn?: (resource: object) => Promise<any> }} [opts]
210
+ * @returns {object}
211
+ */
212
+ export function createAgentConfigGrantController({ persistFn } = {}) {
213
+ function persist(resource) {
214
+ if (typeof persistFn === 'function') {
215
+ Promise.resolve(persistFn(resource)).catch(() => {});
216
+ }
217
+ }
218
+
219
+ return {
220
+ role: 'agent-config-grant-controller',
221
+
222
+ /**
223
+ * Create an AgentConfigGrant resource.
224
+ *
225
+ * @param {{ name, orgRef, configMapName, grantedTo, namespace?, keys? }} input
226
+ * @returns {{ grant: object } | { error: true, message: string }}
227
+ */
228
+ createConfigGrant({
229
+ name,
230
+ orgRef,
231
+ configMapName,
232
+ grantedTo,
233
+ namespace = 'default',
234
+ keys = []
235
+ }) {
236
+ const validation = validateAgentConfigGrant({ name, orgRef, configMapName, grantedTo });
237
+ if (!validation.valid) {
238
+ return { error: true, message: validation.errors.join('; ') };
239
+ }
240
+
241
+ const now = new Date().toISOString();
242
+ const grant = createResource('AgentConfigGrant', { name, namespace }, {
243
+ organizationRef: orgRef,
244
+ orgRef,
245
+ configMapName,
246
+ configMapRef: configMapName,
247
+ grantedTo,
248
+ subject: grantedTo,
249
+ keys,
250
+ purpose: 'read'
251
+ });
252
+ grant.status = { phase: 'Active', createdAt: now };
253
+
254
+ persist(grant);
255
+ return { grant };
256
+ },
257
+
258
+ /**
259
+ * List all grants for a specific agent.
260
+ *
261
+ * @param {string} agentRef
262
+ * @param {object[]} grants
263
+ * @returns {object[]}
264
+ */
265
+ listGrantsForAgent(agentRef, grants = []) {
266
+ return listGrantsForAgent(grants, agentRef);
267
+ },
268
+
269
+ /**
270
+ * Revoke a grant by name.
271
+ *
272
+ * @param {string} grantName
273
+ * @param {object[]} grants
274
+ * @returns {{ grant: object } | { error: true, reason: string, message: string }}
275
+ */
276
+ revokeGrant(grantName, grants = []) {
277
+ const result = revokeGrant(grants, grantName);
278
+ if (!result.error && result.grant) persist(result.grant);
279
+ return result;
280
+ }
281
+ };
282
+ }
@@ -0,0 +1,189 @@
1
+ // Agent Session Transcript Controller — Slice 1.2e
2
+ // Manages AgentSessionTranscript resources: durable transcript storage,
3
+ // message indexing, and pagination support.
4
+
5
+ export const AGENT_SESSION_TRANSCRIPT_CONTROLLER_BOUNDARY = {
6
+ role: 'agent-session-transcript-controller',
7
+ scope: 'AgentSessionTranscript lifecycle: validation, message indexing, pagination, role/tool filtering',
8
+ owns: ['transcript validation', 'message indexing', 'pagination', 'role filter', 'tool name filter'],
9
+ delegatesTo: ['resource-model'],
10
+ mustNotOwn: ['session lifecycle', 'dispatch execution', 'Agent Mux sessions', 'secret values']
11
+ };
12
+
13
+ const VALID_ROLES = ['user', 'assistant', 'tool', 'system'];
14
+
15
+ /**
16
+ * Validate an AgentSessionTranscript resource. Returns { valid, errors }.
17
+ * @param {object} resource
18
+ * @returns {{ valid: boolean, errors: string[] }}
19
+ */
20
+ export function validateAgentSessionTranscript(resource) {
21
+ const errors = [];
22
+
23
+ // Guard against null/undefined resource
24
+ if (resource == null) {
25
+ errors.push('resource must not be null or undefined');
26
+ return { valid: false, errors };
27
+ }
28
+
29
+ // Validate metadata.name
30
+ if (!resource?.metadata?.name) {
31
+ errors.push('metadata.name is required');
32
+ }
33
+
34
+ const spec = resource?.spec || {};
35
+
36
+ // Validate organizationRef
37
+ if (!spec.organizationRef) {
38
+ errors.push('spec.organizationRef is required');
39
+ }
40
+
41
+ // Validate sessionRef
42
+ if (!spec.sessionRef) {
43
+ errors.push('spec.sessionRef is required; provide the AgentSession ID this transcript is linked to');
44
+ }
45
+
46
+ // Validate messages — must be an array (can be empty for a new transcript)
47
+ const messages = spec.messages;
48
+ if (!Array.isArray(messages)) {
49
+ errors.push('spec.messages must be an array');
50
+ } else {
51
+ // Validate each message shape
52
+ for (let i = 0; i < messages.length; i++) {
53
+ const msg = messages[i];
54
+ if (msg == null || typeof msg !== 'object') {
55
+ errors.push(`spec.messages[${i}] must be an object`);
56
+ continue;
57
+ }
58
+ if (!msg.role) {
59
+ errors.push(`spec.messages[${i}].role is required`);
60
+ } else if (!VALID_ROLES.includes(msg.role)) {
61
+ errors.push(`spec.messages[${i}].role "${msg.role}" is not valid; valid roles are: ${VALID_ROLES.join(', ')}`);
62
+ }
63
+ if (msg.content == null) {
64
+ errors.push(`spec.messages[${i}].content is required`);
65
+ }
66
+ }
67
+ }
68
+
69
+ // Validate pageSize if explicitly set
70
+ const pageSize = spec.pageSize;
71
+ if (pageSize != null && (!Number.isInteger(pageSize) || pageSize < 1)) {
72
+ errors.push('spec.pageSize must be a positive integer when specified');
73
+ }
74
+
75
+ return { valid: errors.length === 0, errors };
76
+ }
77
+
78
+ /**
79
+ * Factory that returns an AgentSessionTranscript controller instance.
80
+ */
81
+ export function createAgentSessionTranscriptController() {
82
+ return {
83
+ role: 'agent-session-transcript-controller',
84
+
85
+ /**
86
+ * Validate an AgentSessionTranscript resource.
87
+ * @param {object} resource
88
+ * @returns {{ valid: boolean, errors: string[] }}
89
+ */
90
+ validate(resource) {
91
+ return validateAgentSessionTranscript(resource);
92
+ },
93
+
94
+ /**
95
+ * Return all messages in the transcript, in order.
96
+ * @param {object} resource
97
+ * @returns {Array<{ role: string, content: string, timestamp?: string, toolCalls?: object[] }>}
98
+ */
99
+ getMessages(resource) {
100
+ if (resource == null) {
101
+ throw new Error('resource must not be null or undefined');
102
+ }
103
+ const messages = resource?.spec?.messages;
104
+ if (!Array.isArray(messages)) {
105
+ return [];
106
+ }
107
+ return [...messages];
108
+ },
109
+
110
+ /**
111
+ * Return the total number of messages in the transcript.
112
+ * @param {object} resource
113
+ * @returns {number}
114
+ */
115
+ getTotalMessages(resource) {
116
+ if (resource == null) {
117
+ throw new Error('resource must not be null or undefined');
118
+ }
119
+ const messages = resource?.spec?.messages;
120
+ return Array.isArray(messages) ? messages.length : 0;
121
+ },
122
+
123
+ /**
124
+ * Return a page of messages from the transcript.
125
+ * @param {object} resource
126
+ * @param {number} pageIndex - zero-based page index
127
+ * @param {number} [pageSizeOverride] - override spec.pageSize (defaults to spec.pageSize or 20)
128
+ * @returns {{ messages: object[], pageIndex: number, pageSize: number, totalMessages: number, totalPages: number }}
129
+ */
130
+ getPage(resource, pageIndex, pageSizeOverride) {
131
+ if (resource == null) {
132
+ throw new Error('resource must not be null or undefined');
133
+ }
134
+ const messages = Array.isArray(resource?.spec?.messages) ? resource.spec.messages : [];
135
+ const pageSize = pageSizeOverride ?? resource?.spec?.pageSize ?? 20;
136
+ const totalMessages = messages.length;
137
+ const totalPages = Math.max(1, Math.ceil(totalMessages / pageSize));
138
+ const safePageIndex = Math.max(0, Math.min(pageIndex, totalPages - 1));
139
+ const start = safePageIndex * pageSize;
140
+ const end = start + pageSize;
141
+ return {
142
+ messages: messages.slice(start, end),
143
+ pageIndex: safePageIndex,
144
+ pageSize,
145
+ totalMessages,
146
+ totalPages
147
+ };
148
+ },
149
+
150
+ /**
151
+ * Return all messages filtered by role.
152
+ * @param {object} resource
153
+ * @param {string} role - one of: user, assistant, tool, system
154
+ * @returns {Array<object>}
155
+ */
156
+ getMessagesByRole(resource, role) {
157
+ if (resource == null) {
158
+ throw new Error('resource must not be null or undefined');
159
+ }
160
+ const messages = Array.isArray(resource?.spec?.messages) ? resource.spec.messages : [];
161
+ return messages.filter((m) => m.role === role);
162
+ },
163
+
164
+ /**
165
+ * Return all messages that contain a tool call with the given tool name.
166
+ * @param {object} resource
167
+ * @param {string} toolName
168
+ * @returns {Array<object>}
169
+ */
170
+ getMessagesByToolName(resource, toolName) {
171
+ if (resource == null) {
172
+ throw new Error('resource must not be null or undefined');
173
+ }
174
+ const messages = Array.isArray(resource?.spec?.messages) ? resource.spec.messages : [];
175
+ return messages.filter((m) => {
176
+ if (!Array.isArray(m.toolCalls)) return false;
177
+ return m.toolCalls.some((tc) => tc.name === toolName);
178
+ });
179
+ },
180
+
181
+ /**
182
+ * Return the list of valid message roles.
183
+ * @returns {string[]}
184
+ */
185
+ getValidRoles() {
186
+ return [...VALID_ROLES];
187
+ }
188
+ };
189
+ }