@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.
- 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 +3067 -0
- package/dist/krate-lifecycle.json +201 -0
- package/dist/krate-runtime-snapshot.json +2955 -0
- package/dist/krate-summary.json +722 -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/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 +236 -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 +321 -0
- package/src/agent-workspace-controller.js +447 -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 +50 -0
- package/src/controller-ui.js +551 -0
- package/src/data-plane.js +178 -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 +95 -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 +55 -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/operations.js +112 -0
- package/src/org-scoping.js +5 -0
- package/src/resource-model.js +221 -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/deployment.test.js +396 -0
- package/tests/e2e/lifecycle.test.js +117 -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 +727 -0
- package/tests/memory-search-wiring.test.js +270 -0
- package/tests/org-scoping.test.js +687 -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/workspace-volumes.test.js +312 -0
- 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
|
+
}
|