@a5c-ai/krate 5.0.1-staging.00fa5317c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +31 -0
- package/README.md +183 -0
- package/bin/krate-demo.mjs +23 -0
- package/bin/krate-server.mjs +14 -0
- package/dist/krate-controller-ui.json +3205 -0
- package/dist/krate-lifecycle.json +201 -0
- package/dist/krate-runtime-snapshot.json +3125 -0
- package/dist/krate-summary.json +724 -0
- package/docs/README.md +61 -0
- package/docs/agents/README.md +83 -0
- package/docs/agents/acceptance-test-matrix.md +193 -0
- package/docs/agents/agent-mux-adapter-contract.md +167 -0
- package/docs/agents/agent-mux-source-map.md +310 -0
- package/docs/agents/agent-run-memory-import-spec.md +256 -0
- package/docs/agents/agent-stack-management-spec.md +421 -0
- package/docs/agents/api-contract-spec.md +309 -0
- package/docs/agents/artifacts-writeback-spec.md +145 -0
- package/docs/agents/chart-packaging-spec.md +128 -0
- package/docs/agents/ci-orchestration-spec.md +140 -0
- package/docs/agents/context-assembly-spec.md +219 -0
- package/docs/agents/controller-reconciliation-spec.md +255 -0
- package/docs/agents/crd-schema-spec.md +315 -0
- package/docs/agents/decision-log-open-questions.md +169 -0
- package/docs/agents/developer-implementation-checklist.md +329 -0
- package/docs/agents/dispatching-design.md +262 -0
- package/docs/agents/gaps-agent-mux-to-krate-crds.md +298 -0
- package/docs/agents/glossary.md +66 -0
- package/docs/agents/implementation-blueprint.md +324 -0
- package/docs/agents/implementation-rollout-slices.md +251 -0
- package/docs/agents/memory-context-integration-spec.md +194 -0
- package/docs/agents/memory-ontology-schema-spec.md +253 -0
- package/docs/agents/memory-operations-runbook.md +121 -0
- package/docs/agents/mvp-vertical-slice-spec.md +146 -0
- package/docs/agents/observability-audit-spec.md +265 -0
- package/docs/agents/operator-runbook.md +174 -0
- package/docs/agents/org-memory-api-payload-examples.md +333 -0
- package/docs/agents/org-memory-controller-sequence-spec.md +181 -0
- package/docs/agents/org-memory-e2e-fixture-plan.md +161 -0
- package/docs/agents/org-memory-ui-implementation-map.md +114 -0
- package/docs/agents/org-memory-vertical-slice-spec.md +168 -0
- package/docs/agents/org-resource-model-delta-spec.md +111 -0
- package/docs/agents/org-route-resource-model-spec.md +183 -0
- package/docs/agents/org-scoping-namespace-spec.md +114 -0
- package/docs/agents/rbac-secrets-management-spec.md +406 -0
- package/docs/agents/repository-page-integration-spec.md +255 -0
- package/docs/agents/resource-contract-examples.md +808 -0
- package/docs/agents/resource-relationship-map.md +190 -0
- package/docs/agents/security-threat-model.md +188 -0
- package/docs/agents/shared-memory-company-brain-spec.md +358 -0
- package/docs/agents/storage-migration-spec.md +168 -0
- package/docs/agents/subagent-orchestration-spec.md +152 -0
- package/docs/agents/system-overview.md +88 -0
- package/docs/agents/tools-mcp-skills-spec.md +189 -0
- package/docs/agents/traceability-matrix.md +79 -0
- package/docs/agents/ui-flow-spec.md +211 -0
- package/docs/agents/ui-ux-system-spec.md +426 -0
- package/docs/agents/workspace-lifecycle-spec.md +166 -0
- package/docs/architecture-spec.md +78 -0
- package/docs/components/control-plane.md +78 -0
- package/docs/components/data-plane.md +69 -0
- package/docs/components/hooks-events.md +67 -0
- package/docs/components/identity-rbac-policy.md +73 -0
- package/docs/components/kubevela-oam.md +70 -0
- package/docs/components/operations-publishing.md +81 -0
- package/docs/components/runners-ci.md +66 -0
- package/docs/components/web-ui.md +94 -0
- package/docs/external/README.md +47 -0
- package/docs/external/bidirectional-sync-design.md +134 -0
- package/docs/external/cicd-interface.md +64 -0
- package/docs/external/external-backend-controllers.md +170 -0
- package/docs/external/external-backend-crds.md +234 -0
- package/docs/external/external-backend-ui-spec.md +151 -0
- package/docs/external/external-backend-ux-flows.md +115 -0
- package/docs/external/external-object-mapping.md +125 -0
- package/docs/external/git-forge-interface.md +68 -0
- package/docs/external/github-integration-design.md +151 -0
- package/docs/external/issue-tracking-interface.md +66 -0
- package/docs/external/provider-capability-manifests.md +204 -0
- package/docs/external/provider-catalog.md +139 -0
- package/docs/external/provider-rollout-testing.md +78 -0
- package/docs/external/research-results.md +48 -0
- package/docs/external/security-auth-permissions.md +81 -0
- package/docs/external/sync-state-machines.md +108 -0
- package/docs/external/unified-external-backend-model.md +107 -0
- package/docs/external/user-facing-changes.md +67 -0
- package/docs/gaps.md +161 -0
- package/docs/install.md +94 -0
- package/docs/krate-design.md +334 -0
- package/docs/local-minikube.md +55 -0
- package/docs/ontology/README.md +32 -0
- package/docs/ontology/bounded-contexts.md +29 -0
- package/docs/ontology/events-and-hooks.md +32 -0
- package/docs/ontology/oam-kubevela.md +32 -0
- package/docs/ontology/operations-and-release.md +25 -0
- package/docs/ontology/personas-and-actors.md +32 -0
- package/docs/ontology/policies-and-invariants.md +33 -0
- package/docs/ontology/problem-space.md +30 -0
- package/docs/ontology/resource-contracts.md +40 -0
- package/docs/ontology/resource-taxonomy.md +42 -0
- package/docs/ontology/runners-and-ci.md +29 -0
- package/docs/ontology/solution-space.md +24 -0
- package/docs/ontology/storage-and-data-boundaries.md +29 -0
- package/docs/ontology/validation-matrix.md +24 -0
- package/docs/ontology/web-ui-excellent-flows.md +32 -0
- package/docs/ontology/workflows.md +39 -0
- package/docs/ontology/world.md +35 -0
- package/docs/openapi.yaml +1275 -0
- package/docs/product-requirements.md +62 -0
- package/docs/roadmap-mvp.md +87 -0
- package/docs/system-requirements.md +90 -0
- package/docs/tests/README.md +53 -0
- package/docs/tests/agent-qa-plan.md +63 -0
- package/docs/tests/browser-ui-tests.md +62 -0
- package/docs/tests/ci-quality-gates.md +48 -0
- package/docs/tests/coverage-model.md +64 -0
- package/docs/tests/e2e-scenario-tests.md +53 -0
- package/docs/tests/fixtures-test-data.md +63 -0
- package/docs/tests/observability-reliability-tests.md +54 -0
- package/docs/tests/product-test-matrix.md +145 -0
- package/docs/tests/qa-adoption-roadmap.md +130 -0
- package/docs/tests/qa-automation-plan.md +101 -0
- package/docs/tests/security-compliance-tests.md +57 -0
- package/docs/tests/test-framework-tools.md +88 -0
- package/docs/tests/test-suite-layout.md +121 -0
- package/docs/tests/unit-integration-tests.md +48 -0
- package/docs/todo-kyverno +714 -0
- package/docs/todos.md +4 -0
- package/docs/user-stories.md +78 -0
- package/examples/minikube-demo.yaml +190 -0
- package/examples/oam-application.yaml +23 -0
- package/examples/policy-kyverno-pr-title.yaml +18 -0
- package/package.json +63 -0
- package/scripts/build.mjs +29 -0
- package/scripts/setup-minikube.mjs +65 -0
- package/scripts/smoke.mjs +37 -0
- package/scripts/validate-doc-coverage.mjs +152 -0
- package/scripts/validate-package.mjs +93 -0
- package/scripts/validate-ui.mjs +278 -0
- package/src/agent-adapter-controller.js +169 -0
- package/src/agent-approval-controller.js +170 -0
- package/src/agent-context-bundles.js +242 -0
- package/src/agent-dispatch-controller.js +209 -0
- package/src/agent-gateway-config-controller.js +147 -0
- package/src/agent-memory-controller.js +357 -0
- package/src/agent-memory-import.js +327 -0
- package/src/agent-memory-query.js +292 -0
- package/src/agent-memory-repository-source-controller.js +255 -0
- package/src/agent-mux-client.js +280 -0
- package/src/agent-permission-review.js +250 -0
- package/src/agent-project-controller.js +117 -0
- package/src/agent-provider-config-controller.js +150 -0
- package/src/agent-secret-config-grant-controller.js +282 -0
- package/src/agent-session-transcript-controller.js +189 -0
- package/src/agent-stack-controller.js +347 -0
- package/src/agent-subagent-controller.js +160 -0
- package/src/agent-transport-binding-controller.js +121 -0
- package/src/agent-trigger-controller.js +381 -0
- package/src/agent-workspace-controller.js +702 -0
- package/src/agent-writeback-controller.js +302 -0
- package/src/api-controller.js +541 -0
- package/src/argocd-gitops.js +43 -0
- package/src/async-controller.js +207 -0
- package/src/audit-controller.js +191 -0
- package/src/auth.js +307 -0
- package/src/component-catalog.js +41 -0
- package/src/control-plane.js +136 -0
- package/src/controller-client.js +72 -0
- package/src/controller-ui.js +617 -0
- package/src/data-plane.js +179 -0
- package/src/event-bus.js +61 -0
- package/src/external/conflict-controller.js +225 -0
- package/src/external/github/auth.js +96 -0
- package/src/external/github/cicd.js +180 -0
- package/src/external/github/git-forge.js +240 -0
- package/src/external/github/index.js +144 -0
- package/src/external/github/issue-tracking.js +163 -0
- package/src/external/provider-adapter.js +161 -0
- package/src/external/provider-resource-factory.js +161 -0
- package/src/external/sync-controller.js +235 -0
- package/src/external/webhook-controller.js +144 -0
- package/src/external/write-controller.js +283 -0
- package/src/gitea-backend.js +131 -0
- package/src/gitea-service.js +173 -0
- package/src/handoff.js +98 -0
- package/src/hooks-events.js +63 -0
- package/src/http-server.js +377 -0
- package/src/identity-policy.js +86 -0
- package/src/index.js +57 -0
- package/src/kubernetes-controller-async.js +511 -0
- package/src/kubernetes-controller.js +878 -0
- package/src/kubernetes-resource-gateway.js +48 -0
- package/src/notification-controller.js +178 -0
- package/src/operations.js +112 -0
- package/src/org-scoping.js +5 -0
- package/src/resource-model.js +221 -0
- package/src/runner-controller.js +272 -0
- package/src/runners-ci.js +48 -0
- package/src/runtime.js +196 -0
- package/src/snapshot-cache.js +157 -0
- package/src/web-ui.js +40 -0
- package/tests/agent-adapter-controller.test.js +361 -0
- package/tests/agent-approval-controller.test.js +173 -0
- package/tests/agent-context-bundles.test.js +278 -0
- package/tests/agent-dispatch-controller.test.js +315 -0
- package/tests/agent-gateway-config-controller.test.js +386 -0
- package/tests/agent-memory-controller.test.js +308 -0
- package/tests/agent-memory-import-snapshot.test.js +477 -0
- package/tests/agent-memory-query.test.js +404 -0
- package/tests/agent-memory-repository-source.test.js +514 -0
- package/tests/agent-mux-client.test.js +204 -0
- package/tests/agent-permission-review-v2.test.js +317 -0
- package/tests/agent-permission-review.test.js +209 -0
- package/tests/agent-project-controller.test.js +302 -0
- package/tests/agent-provider-config-controller.test.js +376 -0
- package/tests/agent-resources.test.js +228 -0
- package/tests/agent-secret-config-grant.test.js +231 -0
- package/tests/agent-session-transcript-controller.test.js +499 -0
- package/tests/agent-stack-controller.test.js +221 -0
- package/tests/agent-subagent-controller.test.js +201 -0
- package/tests/agent-transport-binding-controller.test.js +294 -0
- package/tests/agent-trigger-controller.test.js +211 -0
- package/tests/agent-trigger-routes.test.js +190 -0
- package/tests/agent-trigger-sources.test.js +245 -0
- package/tests/agent-workspace-controller.test.js +181 -0
- package/tests/agent-writeback.test.js +292 -0
- package/tests/approval-persistence.test.js +171 -0
- package/tests/async-controller.test.js +252 -0
- package/tests/audit-controller.test.js +227 -0
- package/tests/codespace-controller.test.js +318 -0
- package/tests/deployment.test.js +407 -0
- package/tests/e2e/lifecycle.test.js +117 -0
- package/tests/event-bus-integration.test.js +190 -0
- package/tests/external-github-forge.test.js +560 -0
- package/tests/external-github-issues-cicd.test.js +520 -0
- package/tests/external-integration.test.js +470 -0
- package/tests/external-persistence.test.js +340 -0
- package/tests/external-provider-adapter.test.js +365 -0
- package/tests/external-resource-model.test.js +215 -0
- package/tests/external-webhook-sync.test.js +287 -0
- package/tests/external-write-conflict.test.js +353 -0
- package/tests/gitea-service.test.js +253 -0
- package/tests/health-check-real.test.js +165 -0
- package/tests/integration/full-flow.test.js +266 -0
- package/tests/krate.test.js +756 -0
- package/tests/memory-search-wiring.test.js +270 -0
- package/tests/notification-controller.test.js +196 -0
- package/tests/notification-integration.test.js +179 -0
- package/tests/org-scoping.test.js +687 -0
- package/tests/runner-controller.test.js +327 -0
- package/tests/runner-integration.test.js +231 -0
- package/tests/session-cookie-hmac.test.js +151 -0
- package/tests/snapshot-performance.test.js +247 -0
- package/tests/sse-events.test.js +107 -0
- package/tests/webhook-trigger.test.js +198 -0
- package/tests/workspace-volumes.test.js +312 -0
- package/tests/writeback-persistence.test.js +207 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
// Agent Memory Repository & Source Controller — Slice 2.3a
|
|
2
|
+
//
|
|
3
|
+
// AgentMemoryRepository: org-level memory storage pointer, git repo ref validation.
|
|
4
|
+
// AgentMemorySource: read policies for memory paths, access control.
|
|
5
|
+
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Boundaries
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
export const AGENT_MEMORY_REPOSITORY_CONTROLLER_BOUNDARY = {
|
|
11
|
+
role: 'agent-memory-repository-controller',
|
|
12
|
+
scope: 'AgentMemoryRepository lifecycle: validation, repo URL resolution, retention policy defaults',
|
|
13
|
+
owns: ['memory repository validation', 'repo URL', 'retention policy defaults'],
|
|
14
|
+
delegatesTo: ['resource-model'],
|
|
15
|
+
mustNotOwn: ['git operations', 'secret values', 'Agent Mux sessions', 'memory search']
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const AGENT_MEMORY_SOURCE_CONTROLLER_BOUNDARY = {
|
|
19
|
+
role: 'agent-memory-source-controller',
|
|
20
|
+
scope: 'AgentMemorySource lifecycle: validation, read policy, included/excluded paths, access control',
|
|
21
|
+
owns: ['memory source validation', 'read policy defaults', 'included paths', 'excluded paths'],
|
|
22
|
+
delegatesTo: ['resource-model'],
|
|
23
|
+
mustNotOwn: ['git operations', 'secret values', 'Agent Mux sessions', 'memory search']
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Constants — defaults
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
const DEFAULT_RETENTION_POLICY = Object.freeze({
|
|
31
|
+
maxAgeDays: 90,
|
|
32
|
+
maxSizeMb: 500,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const DEFAULT_READ_POLICY = Object.freeze({
|
|
36
|
+
mode: 'allow-all',
|
|
37
|
+
maxDepth: 5,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Valid git/http/ssh URL schemes
|
|
41
|
+
// Accepted forms:
|
|
42
|
+
// https://... http://... ssh://... git://... git@host:path (SCP-style)
|
|
43
|
+
const VALID_REPO_URL_PATTERNS = [
|
|
44
|
+
/^https?:\/\//,
|
|
45
|
+
/^ssh:\/\//,
|
|
46
|
+
/^git:\/\//,
|
|
47
|
+
/^git@[^:]+:.+/,
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Standalone validateMemoryRepository
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Validate an AgentMemoryRepository resource. Returns { valid, errors }.
|
|
56
|
+
* @param {object} resource
|
|
57
|
+
* @returns {{ valid: boolean, errors: string[] }}
|
|
58
|
+
*/
|
|
59
|
+
export function validateMemoryRepository(resource) {
|
|
60
|
+
const errors = [];
|
|
61
|
+
|
|
62
|
+
// Guard against null/undefined
|
|
63
|
+
if (resource == null) {
|
|
64
|
+
errors.push('resource must not be null or undefined');
|
|
65
|
+
return { valid: false, errors };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Validate metadata.name
|
|
69
|
+
if (!resource.metadata?.name) {
|
|
70
|
+
errors.push('metadata.name is required');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const spec = resource.spec || {};
|
|
74
|
+
|
|
75
|
+
// Validate organizationRef
|
|
76
|
+
if (!spec.organizationRef) {
|
|
77
|
+
errors.push('spec.organizationRef is required');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Validate repoUrl — presence
|
|
81
|
+
if (!spec.repoUrl) {
|
|
82
|
+
errors.push('spec.repoUrl is required; provide a git/http/ssh repository URL');
|
|
83
|
+
} else {
|
|
84
|
+
// Validate repoUrl — format (git/http/ssh/scp-style)
|
|
85
|
+
const isValid = VALID_REPO_URL_PATTERNS.some((re) => re.test(spec.repoUrl));
|
|
86
|
+
if (!isValid) {
|
|
87
|
+
errors.push(
|
|
88
|
+
'spec.repoUrl must be a valid git repository URL (https://, http://, ssh://, git://, or git@host:path format)'
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { valid: errors.length === 0, errors };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// AgentMemoryRepository controller factory
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Factory that returns an AgentMemoryRepository controller instance.
|
|
102
|
+
*/
|
|
103
|
+
export function createAgentMemoryRepositoryController() {
|
|
104
|
+
return {
|
|
105
|
+
role: 'agent-memory-repository-controller',
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Validate an AgentMemoryRepository resource.
|
|
109
|
+
* @param {object} resource
|
|
110
|
+
* @returns {{ valid: boolean, errors: string[] }}
|
|
111
|
+
*/
|
|
112
|
+
validateMemoryRepository(resource) {
|
|
113
|
+
return validateMemoryRepository(resource);
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Return the repository URL configured in spec.repoUrl.
|
|
118
|
+
* @param {object} resource
|
|
119
|
+
* @returns {string}
|
|
120
|
+
*/
|
|
121
|
+
getRepositoryUrl(resource) {
|
|
122
|
+
if (resource == null) {
|
|
123
|
+
throw new Error('resource must not be null or undefined');
|
|
124
|
+
}
|
|
125
|
+
return resource.spec?.repoUrl ?? null;
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Return the effective retention policy for the memory repository.
|
|
130
|
+
* Merges spec.retentionPolicy with defaults; spec values take precedence.
|
|
131
|
+
* Defaults: maxAgeDays = 90, maxSizeMb = 500.
|
|
132
|
+
* @param {object} resource
|
|
133
|
+
* @returns {{ maxAgeDays: number, maxSizeMb: number }}
|
|
134
|
+
*/
|
|
135
|
+
getRetentionPolicy(resource) {
|
|
136
|
+
if (resource == null) {
|
|
137
|
+
throw new Error('resource must not be null or undefined');
|
|
138
|
+
}
|
|
139
|
+
const specPolicy = resource.spec?.retentionPolicy ?? {};
|
|
140
|
+
return {
|
|
141
|
+
maxAgeDays: specPolicy.maxAgeDays ?? DEFAULT_RETENTION_POLICY.maxAgeDays,
|
|
142
|
+
maxSizeMb: specPolicy.maxSizeMb ?? DEFAULT_RETENTION_POLICY.maxSizeMb,
|
|
143
|
+
};
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
// Standalone validateMemorySource
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Validate an AgentMemorySource resource. Returns { valid, errors }.
|
|
154
|
+
* @param {object} resource
|
|
155
|
+
* @returns {{ valid: boolean, errors: string[] }}
|
|
156
|
+
*/
|
|
157
|
+
export function validateMemorySource(resource) {
|
|
158
|
+
const errors = [];
|
|
159
|
+
|
|
160
|
+
// Guard against null/undefined
|
|
161
|
+
if (resource == null) {
|
|
162
|
+
errors.push('resource must not be null or undefined');
|
|
163
|
+
return { valid: false, errors };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Validate metadata.name
|
|
167
|
+
if (!resource.metadata?.name) {
|
|
168
|
+
errors.push('metadata.name is required');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const spec = resource.spec || {};
|
|
172
|
+
|
|
173
|
+
// Validate organizationRef
|
|
174
|
+
if (!spec.organizationRef) {
|
|
175
|
+
errors.push('spec.organizationRef is required');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Validate repositoryRef
|
|
179
|
+
if (!spec.repositoryRef) {
|
|
180
|
+
errors.push('spec.repositoryRef is required; provide a reference to the AgentMemoryRepository');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Validate paths — must be present and non-empty
|
|
184
|
+
const paths = spec.paths;
|
|
185
|
+
if (!Array.isArray(paths) || paths.length === 0) {
|
|
186
|
+
errors.push('spec.paths must be a non-empty array of memory path patterns');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return { valid: errors.length === 0, errors };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
// AgentMemorySource controller factory
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Factory that returns an AgentMemorySource controller instance.
|
|
198
|
+
*/
|
|
199
|
+
export function createAgentMemorySourceController() {
|
|
200
|
+
return {
|
|
201
|
+
role: 'agent-memory-source-controller',
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Validate an AgentMemorySource resource.
|
|
205
|
+
* @param {object} resource
|
|
206
|
+
* @returns {{ valid: boolean, errors: string[] }}
|
|
207
|
+
*/
|
|
208
|
+
validateMemorySource(resource) {
|
|
209
|
+
return validateMemorySource(resource);
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Return the effective read policy for the memory source.
|
|
214
|
+
* Merges spec.readPolicy with defaults; spec values take precedence.
|
|
215
|
+
* Defaults: mode = 'allow-all', maxDepth = 5.
|
|
216
|
+
* @param {object} resource
|
|
217
|
+
* @returns {{ mode: string, maxDepth: number }}
|
|
218
|
+
*/
|
|
219
|
+
getReadPolicy(resource) {
|
|
220
|
+
if (resource == null) {
|
|
221
|
+
throw new Error('resource must not be null or undefined');
|
|
222
|
+
}
|
|
223
|
+
const specPolicy = resource.spec?.readPolicy ?? {};
|
|
224
|
+
return {
|
|
225
|
+
mode: specPolicy.mode ?? DEFAULT_READ_POLICY.mode,
|
|
226
|
+
maxDepth: specPolicy.maxDepth ?? DEFAULT_READ_POLICY.maxDepth,
|
|
227
|
+
};
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Return the included memory path patterns from spec.paths.
|
|
232
|
+
* @param {object} resource
|
|
233
|
+
* @returns {string[]}
|
|
234
|
+
*/
|
|
235
|
+
getIncludedPaths(resource) {
|
|
236
|
+
if (resource == null) {
|
|
237
|
+
throw new Error('resource must not be null or undefined');
|
|
238
|
+
}
|
|
239
|
+
return Array.isArray(resource.spec?.paths) ? [...resource.spec.paths] : [];
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Return the excluded memory path patterns from spec.excludedPaths.
|
|
244
|
+
* Returns an empty array when not set.
|
|
245
|
+
* @param {object} resource
|
|
246
|
+
* @returns {string[]}
|
|
247
|
+
*/
|
|
248
|
+
getExcludedPaths(resource) {
|
|
249
|
+
if (resource == null) {
|
|
250
|
+
throw new Error('resource must not be null or undefined');
|
|
251
|
+
}
|
|
252
|
+
return Array.isArray(resource.spec?.excludedPaths) ? [...resource.spec.excludedPaths] : [];
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
import https from 'node:https';
|
|
3
|
+
import { URL } from 'node:url';
|
|
4
|
+
import { createResource } from './resource-model.js';
|
|
5
|
+
|
|
6
|
+
export const AGENT_MUX_CLIENT_BOUNDARY = {
|
|
7
|
+
role: 'agent-mux-client',
|
|
8
|
+
scope: 'HTTP/SSE adapter for Agent Mux gateway — capabilities, sessions, events, transcripts',
|
|
9
|
+
owns: ['gateway HTTP calls', 'SSE event streaming', 'transcript reconciliation'],
|
|
10
|
+
delegatesTo: ['resource-model'],
|
|
11
|
+
mustNotOwn: ['secret values', 'permission review', 'resource persistence']
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Internal HTTP request helper. Zero external deps — uses node:http / node:https.
|
|
16
|
+
* @param {string} url
|
|
17
|
+
* @param {{ method?: string, body?: object, headers?: Record<string,string>, timeout?: number }} options
|
|
18
|
+
* @returns {Promise<{ status: number, body: any }>}
|
|
19
|
+
*/
|
|
20
|
+
function httpRequest(url, { method = 'GET', body, headers = {}, timeout = 30000 } = {}) {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
const parsed = new URL(url);
|
|
23
|
+
const transport = parsed.protocol === 'https:' ? https : http;
|
|
24
|
+
const opts = {
|
|
25
|
+
hostname: parsed.hostname,
|
|
26
|
+
port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
|
|
27
|
+
path: parsed.pathname + parsed.search,
|
|
28
|
+
method,
|
|
29
|
+
headers: { 'Accept': 'application/json', ...headers },
|
|
30
|
+
timeout,
|
|
31
|
+
};
|
|
32
|
+
if (body) {
|
|
33
|
+
const payload = JSON.stringify(body);
|
|
34
|
+
opts.headers['Content-Type'] = 'application/json';
|
|
35
|
+
opts.headers['Content-Length'] = Buffer.byteLength(payload);
|
|
36
|
+
}
|
|
37
|
+
const req = transport.request(opts, (res) => {
|
|
38
|
+
const chunks = [];
|
|
39
|
+
res.on('data', chunk => chunks.push(chunk));
|
|
40
|
+
res.on('end', () => {
|
|
41
|
+
const raw = Buffer.concat(chunks).toString();
|
|
42
|
+
try {
|
|
43
|
+
resolve({ status: res.statusCode, body: JSON.parse(raw) });
|
|
44
|
+
} catch {
|
|
45
|
+
resolve({ status: res.statusCode, body: raw });
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
req.on('timeout', () => { req.destroy(); reject(new Error('Request timeout')); });
|
|
50
|
+
req.on('error', reject);
|
|
51
|
+
if (body) req.write(JSON.stringify(body));
|
|
52
|
+
req.end();
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Parse SSE text into an array of parsed JSON data payloads.
|
|
58
|
+
* Each `data: {...}` line is extracted; malformed JSON is silently skipped.
|
|
59
|
+
* @param {string} text
|
|
60
|
+
* @returns {object[]}
|
|
61
|
+
*/
|
|
62
|
+
export function parseSseLines(text) {
|
|
63
|
+
const events = [];
|
|
64
|
+
for (const block of text.split('\n\n')) {
|
|
65
|
+
for (const line of block.split('\n')) {
|
|
66
|
+
if (line.startsWith('data: ')) {
|
|
67
|
+
try { events.push(JSON.parse(line.slice(6))); } catch { /* skip malformed */ }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return events;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @param {{ gateway?: string, enabled?: boolean }} options
|
|
76
|
+
*/
|
|
77
|
+
export function createAgentMuxClient(options = {}) {
|
|
78
|
+
const { gateway = '', enabled = false } = options;
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
role: 'agent-mux-client',
|
|
82
|
+
|
|
83
|
+
isAvailable() {
|
|
84
|
+
return enabled && !!gateway;
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Query adapter capabilities from the gateway.
|
|
89
|
+
* GET {gateway}/api/v1/agents/{adapter}/capabilities
|
|
90
|
+
* @param {string} adapter
|
|
91
|
+
* @returns {Promise<object|null>}
|
|
92
|
+
*/
|
|
93
|
+
async queryCapabilities(adapter) {
|
|
94
|
+
if (!this.isAvailable()) return null;
|
|
95
|
+
try {
|
|
96
|
+
const { status, body } = await httpRequest(`${gateway}/api/v1/agents/${encodeURIComponent(adapter)}/capabilities`);
|
|
97
|
+
if (status >= 200 && status < 300) return body;
|
|
98
|
+
return null;
|
|
99
|
+
} catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Launch a new agent session through the gateway.
|
|
106
|
+
* POST {gateway}/api/v1/sessions
|
|
107
|
+
* @param {{ stack: object, contextBundle?: object, permissionSnapshot?: object, workspace?: object }} params
|
|
108
|
+
* @returns {Promise<{ runId: string, sessionId: string }|null>}
|
|
109
|
+
*/
|
|
110
|
+
async launchSession({ stack, contextBundle, permissionSnapshot, workspace }) {
|
|
111
|
+
if (!this.isAvailable()) return null;
|
|
112
|
+
try {
|
|
113
|
+
const payload = {
|
|
114
|
+
agent: stack?.baseAgent,
|
|
115
|
+
model: stack?.model,
|
|
116
|
+
prompt: contextBundle?.prompt,
|
|
117
|
+
systemPrompt: contextBundle?.systemPrompt,
|
|
118
|
+
attachments: contextBundle?.attachments,
|
|
119
|
+
workspace: workspace?.mountPath || '/workspace',
|
|
120
|
+
};
|
|
121
|
+
const { status, body } = await httpRequest(`${gateway}/api/v1/sessions`, { method: 'POST', body: payload });
|
|
122
|
+
if (status >= 200 && status < 300 && body?.runId && body?.sessionId) {
|
|
123
|
+
return { runId: body.runId, sessionId: body.sessionId };
|
|
124
|
+
}
|
|
125
|
+
return null;
|
|
126
|
+
} catch {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Get session status from the gateway.
|
|
133
|
+
* GET {gateway}/api/v1/sessions/{sessionId}
|
|
134
|
+
* @param {string} sessionId
|
|
135
|
+
* @returns {Promise<object|null>}
|
|
136
|
+
*/
|
|
137
|
+
async getSessionStatus(sessionId) {
|
|
138
|
+
if (!this.isAvailable()) return null;
|
|
139
|
+
try {
|
|
140
|
+
const { status, body } = await httpRequest(`${gateway}/api/v1/sessions/${encodeURIComponent(sessionId)}`);
|
|
141
|
+
if (status >= 200 && status < 300) return body;
|
|
142
|
+
return null;
|
|
143
|
+
} catch {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Subscribe to SSE events for a run. Reconnects with exponential backoff (1s, 2s, 4s... max 30s).
|
|
150
|
+
* GET {gateway}/api/v1/runs/{runId}/events (Accept: text/event-stream)
|
|
151
|
+
* @param {string} runId
|
|
152
|
+
* @param {(event: object) => void} callback
|
|
153
|
+
* @returns {{ abort: () => void }}
|
|
154
|
+
*/
|
|
155
|
+
subscribeToEvents(runId, callback) {
|
|
156
|
+
let aborted = false;
|
|
157
|
+
let currentReq = null;
|
|
158
|
+
let backoff = 1000;
|
|
159
|
+
|
|
160
|
+
const connect = () => {
|
|
161
|
+
if (aborted) return;
|
|
162
|
+
try {
|
|
163
|
+
const parsed = new URL(`${gateway}/api/v1/runs/${encodeURIComponent(runId)}/events`);
|
|
164
|
+
const transport = parsed.protocol === 'https:' ? https : http;
|
|
165
|
+
const opts = {
|
|
166
|
+
hostname: parsed.hostname,
|
|
167
|
+
port: parsed.port || (parsed.protocol === 'https:' ? 443 : 80),
|
|
168
|
+
path: parsed.pathname + parsed.search,
|
|
169
|
+
method: 'GET',
|
|
170
|
+
headers: { 'Accept': 'text/event-stream' },
|
|
171
|
+
};
|
|
172
|
+
currentReq = transport.request(opts, (res) => {
|
|
173
|
+
if (aborted) return;
|
|
174
|
+
// Reset backoff on successful connection
|
|
175
|
+
backoff = 1000;
|
|
176
|
+
let buffer = '';
|
|
177
|
+
res.on('data', (chunk) => {
|
|
178
|
+
if (aborted) return;
|
|
179
|
+
buffer += chunk.toString();
|
|
180
|
+
// Process complete SSE blocks (separated by double newlines)
|
|
181
|
+
const parts = buffer.split('\n\n');
|
|
182
|
+
// Keep the last part as it may be incomplete
|
|
183
|
+
buffer = parts.pop() || '';
|
|
184
|
+
for (const block of parts) {
|
|
185
|
+
for (const line of block.split('\n')) {
|
|
186
|
+
if (line.startsWith('data: ')) {
|
|
187
|
+
try {
|
|
188
|
+
callback(JSON.parse(line.slice(6)));
|
|
189
|
+
} catch { /* skip malformed */ }
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
res.on('end', () => {
|
|
195
|
+
if (!aborted) reconnect();
|
|
196
|
+
});
|
|
197
|
+
res.on('error', () => {
|
|
198
|
+
if (!aborted) reconnect();
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
currentReq.on('error', () => {
|
|
202
|
+
if (!aborted) reconnect();
|
|
203
|
+
});
|
|
204
|
+
currentReq.end();
|
|
205
|
+
} catch {
|
|
206
|
+
if (!aborted) reconnect();
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const reconnect = () => {
|
|
211
|
+
if (aborted) return;
|
|
212
|
+
const delay = backoff;
|
|
213
|
+
backoff = Math.min(backoff * 2, 30000);
|
|
214
|
+
setTimeout(connect, delay);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
connect();
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
abort() {
|
|
221
|
+
aborted = true;
|
|
222
|
+
if (currentReq) {
|
|
223
|
+
currentReq.destroy();
|
|
224
|
+
currentReq = null;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Reconcile SSE events into an AgentSessionTranscript resource.
|
|
232
|
+
* Parses events by role, computes cost, creates the resource via createResource().
|
|
233
|
+
* @param {string} sessionId
|
|
234
|
+
* @param {object[]} events
|
|
235
|
+
* @param {{ namespace?: string, organizationRef?: string }} options
|
|
236
|
+
* @returns {object} AgentSessionTranscript resource
|
|
237
|
+
*/
|
|
238
|
+
reconcileTranscript(sessionId, events, { namespace = 'default', organizationRef = 'default' } = {}) {
|
|
239
|
+
const messages = [];
|
|
240
|
+
let totalInputTokens = 0;
|
|
241
|
+
let totalOutputTokens = 0;
|
|
242
|
+
|
|
243
|
+
for (const event of events) {
|
|
244
|
+
if (!event || typeof event !== 'object') continue;
|
|
245
|
+
const role = event.role || 'unknown';
|
|
246
|
+
const content = event.content || event.text || event.message || '';
|
|
247
|
+
const node = {
|
|
248
|
+
role,
|
|
249
|
+
content: typeof content === 'string' ? content : JSON.stringify(content),
|
|
250
|
+
timestamp: event.timestamp || new Date().toISOString(),
|
|
251
|
+
};
|
|
252
|
+
if (event.toolUse) node.toolUse = event.toolUse;
|
|
253
|
+
if (event.toolResult) node.toolResult = event.toolResult;
|
|
254
|
+
messages.push(node);
|
|
255
|
+
|
|
256
|
+
// Accumulate token usage if present
|
|
257
|
+
if (event.usage) {
|
|
258
|
+
totalInputTokens += event.usage.inputTokens || 0;
|
|
259
|
+
totalOutputTokens += event.usage.outputTokens || 0;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return createResource(
|
|
264
|
+
'AgentSessionTranscript',
|
|
265
|
+
{ name: `transcript-${sessionId}`, namespace },
|
|
266
|
+
{
|
|
267
|
+
organizationRef,
|
|
268
|
+
sessionRef: sessionId,
|
|
269
|
+
messages,
|
|
270
|
+
cost: {
|
|
271
|
+
inputTokens: totalInputTokens,
|
|
272
|
+
outputTokens: totalOutputTokens,
|
|
273
|
+
totalTokens: totalInputTokens + totalOutputTokens,
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
{ phase: 'Reconciled', reconciledAt: new Date().toISOString() }
|
|
277
|
+
);
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
}
|