open-managed-agents 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +93 -0
- package/README.md +248 -0
- package/bin/oma.mjs +499 -0
- package/dist/scripts/alpha-openai-compatible-fixture.mjs +134 -0
- package/dist/scripts/alpha-smoke-models.mjs +92 -0
- package/dist/scripts/alpha-smoke.mjs +581 -0
- package/dist/scripts/oma-doctor.js +230 -0
- package/dist/scripts/oma-models.js +299 -0
- package/dist/scripts/oma-workspaces.js +133 -0
- package/dist/src/control-plane/admin/auth.js +43 -0
- package/dist/src/control-plane/admin/routes.js +76 -0
- package/dist/src/control-plane/admin/service.js +102 -0
- package/dist/src/control-plane/admission.js +90 -0
- package/dist/src/control-plane/agents/routes.js +63 -0
- package/dist/src/control-plane/agents/service.js +687 -0
- package/dist/src/control-plane/agents/store.js +391 -0
- package/dist/src/control-plane/agents/types.js +1 -0
- package/dist/src/control-plane/api-constants.js +3 -0
- package/dist/src/control-plane/app.js +907 -0
- package/dist/src/control-plane/console/auth.js +68 -0
- package/dist/src/control-plane/console/static.js +112 -0
- package/dist/src/control-plane/deployment-runtime-config.js +328 -0
- package/dist/src/control-plane/deployment-runtime-event-coordinator.js +25 -0
- package/dist/src/control-plane/deployment-runtime-turn-guard.js +14 -0
- package/dist/src/control-plane/deployment-session-coordinator.js +25 -0
- package/dist/src/control-plane/deployment-session-output-coordinator.js +27 -0
- package/dist/src/control-plane/deployment-storage.js +354 -0
- package/dist/src/control-plane/egress/image.js +48 -0
- package/dist/src/control-plane/egress/policy.js +571 -0
- package/dist/src/control-plane/egress/presets.js +77 -0
- package/dist/src/control-plane/egress/proxy.js +72 -0
- package/dist/src/control-plane/egress/ssrf.js +106 -0
- package/dist/src/control-plane/egress/utils/debug.js +14 -0
- package/dist/src/control-plane/egress/vendor/http-proxy.js +335 -0
- package/dist/src/control-plane/egress/vendor/mitm-ca.js +226 -0
- package/dist/src/control-plane/egress/vendor/mitm-leaf.js +124 -0
- package/dist/src/control-plane/egress/vendor/parent-proxy.js +438 -0
- package/dist/src/control-plane/egress/vendor/request-filter.js +105 -0
- package/dist/src/control-plane/egress/vendor/sandbox-config.js +1 -0
- package/dist/src/control-plane/egress/vendor/tls-terminate-proxy.js +269 -0
- package/dist/src/control-plane/environments/routes.js +54 -0
- package/dist/src/control-plane/environments/service.js +112 -0
- package/dist/src/control-plane/environments/store.js +132 -0
- package/dist/src/control-plane/environments/types.js +1 -0
- package/dist/src/control-plane/errors.js +81 -0
- package/dist/src/control-plane/events/broadcaster.js +217 -0
- package/dist/src/control-plane/events/constants.js +2 -0
- package/dist/src/control-plane/events/persist.js +39 -0
- package/dist/src/control-plane/events/request.js +190 -0
- package/dist/src/control-plane/events/routes.js +159 -0
- package/dist/src/control-plane/events/runtime-helpers.js +134 -0
- package/dist/src/control-plane/events/service.js +1985 -0
- package/dist/src/control-plane/events/session-guards.js +29 -0
- package/dist/src/control-plane/events/sse.js +6 -0
- package/dist/src/control-plane/events/store.js +772 -0
- package/dist/src/control-plane/events/tool-persistence.js +102 -0
- package/dist/src/control-plane/events/types.js +21 -0
- package/dist/src/control-plane/files/routes.js +121 -0
- package/dist/src/control-plane/files/service.js +65 -0
- package/dist/src/control-plane/files/store-common.js +87 -0
- package/dist/src/control-plane/files/store-local.js +488 -0
- package/dist/src/control-plane/files/store-memory.js +270 -0
- package/dist/src/control-plane/files/store.js +2 -0
- package/dist/src/control-plane/files/types.js +7 -0
- package/dist/src/control-plane/http.js +25 -0
- package/dist/src/control-plane/ids.js +43 -0
- package/dist/src/control-plane/logging.js +263 -0
- package/dist/src/control-plane/models/auth-storage-backend.js +238 -0
- package/dist/src/control-plane/models/catalog.js +78 -0
- package/dist/src/control-plane/models/config-security.js +376 -0
- package/dist/src/control-plane/models/deployment-config.js +93 -0
- package/dist/src/control-plane/models/routes.js +32 -0
- package/dist/src/control-plane/models/service.js +138 -0
- package/dist/src/control-plane/observability/instruments.js +65 -0
- package/dist/src/control-plane/observability/metrics.js +125 -0
- package/dist/src/control-plane/observability/routes.js +95 -0
- package/dist/src/control-plane/openapi/document.js +282 -0
- package/dist/src/control-plane/openapi/routes.js +78 -0
- package/dist/src/control-plane/request-idempotency.js +85 -0
- package/dist/src/control-plane/secrets/envelope.js +132 -0
- package/dist/src/control-plane/secrets/master-key.js +44 -0
- package/dist/src/control-plane/secrets/routes.js +20 -0
- package/dist/src/control-plane/secrets/service.js +71 -0
- package/dist/src/control-plane/secrets/store.js +212 -0
- package/dist/src/control-plane/secrets/types.js +3 -0
- package/dist/src/control-plane/sessions/pi/custom-tools.js +146 -0
- package/dist/src/control-plane/sessions/pi/mcp/bridge.js +527 -0
- package/dist/src/control-plane/sessions/pi/mcp/client.js +157 -0
- package/dist/src/control-plane/sessions/pi/mcp/credential.js +28 -0
- package/dist/src/control-plane/sessions/pi/mcp/fetch.js +69 -0
- package/dist/src/control-plane/sessions/pi/mcp/probe.js +202 -0
- package/dist/src/control-plane/sessions/pi/mcp/runtime.js +13 -0
- package/dist/src/control-plane/sessions/pi/runner.js +1104 -0
- package/dist/src/control-plane/sessions/pi/sandbox/cma-glob.js +388 -0
- package/dist/src/control-plane/sessions/pi/sandbox/cma-grep.js +124 -0
- package/dist/src/control-plane/sessions/pi/sandbox/docker-egress.js +347 -0
- package/dist/src/control-plane/sessions/pi/sandbox/docker.js +1589 -0
- package/dist/src/control-plane/sessions/pi/sandbox/glob.js +48 -0
- package/dist/src/control-plane/sessions/pi/sandbox/image.js +7 -0
- package/dist/src/control-plane/sessions/pi/sandbox/microsandbox.js +1677 -0
- package/dist/src/control-plane/sessions/pi/sandbox/provider.js +561 -0
- package/dist/src/control-plane/sessions/pi/sandbox/selection.js +168 -0
- package/dist/src/control-plane/sessions/pi/span-normalizer.js +82 -0
- package/dist/src/control-plane/sessions/pi/tool-permissions.js +392 -0
- package/dist/src/control-plane/sessions/pi/translator.js +100 -0
- package/dist/src/control-plane/sessions/request.js +164 -0
- package/dist/src/control-plane/sessions/resources.js +63 -0
- package/dist/src/control-plane/sessions/routes.js +88 -0
- package/dist/src/control-plane/sessions/serialize.js +24 -0
- package/dist/src/control-plane/sessions/service.js +733 -0
- package/dist/src/control-plane/sessions/store.js +538 -0
- package/dist/src/control-plane/sessions/types.js +1 -0
- package/dist/src/control-plane/skills/archive.js +169 -0
- package/dist/src/control-plane/skills/routes.js +41 -0
- package/dist/src/control-plane/skills/service.js +29 -0
- package/dist/src/control-plane/skills/store.js +233 -0
- package/dist/src/control-plane/skills/types.js +5 -0
- package/dist/src/control-plane/sqlite-transaction.js +15 -0
- package/dist/src/control-plane/vaults/mcp-oauth-validate.js +165 -0
- package/dist/src/control-plane/vaults/oauth-refresh-ticker.js +43 -0
- package/dist/src/control-plane/vaults/oauth-refresh.js +448 -0
- package/dist/src/control-plane/vaults/routes.js +66 -0
- package/dist/src/control-plane/vaults/service.js +553 -0
- package/dist/src/control-plane/vaults/store.js +691 -0
- package/dist/src/control-plane/vaults/types.js +1 -0
- package/dist/src/control-plane/wake-loop.js +65 -0
- package/dist/src/control-plane/wiring.js +92 -0
- package/dist/src/control-plane/workspace.js +4 -0
- package/dist/src/control-plane/workspaces/store.js +158 -0
- package/dist/src/egress-proxy-main.js +103 -0
- package/dist/src/main.js +124 -0
- package/dist/src/types/agents.js +1 -0
- package/dist/src/types/common.js +1 -0
- package/dist/src/types/environments.js +1 -0
- package/dist/src/types/events.js +121 -0
- package/dist/src/types/files.js +1 -0
- package/dist/src/types/json.js +19 -0
- package/dist/src/types/sessions.js +1 -0
- package/dist/ui/managed-agents-console/README.md +52 -0
- package/dist/ui/managed-agents-console/docs/agents.md +23 -0
- package/dist/ui/managed-agents-console/docs/console.md +23 -0
- package/dist/ui/managed-agents-console/docs/dreams.md +5 -0
- package/dist/ui/managed-agents-console/docs/environments.md +27 -0
- package/dist/ui/managed-agents-console/docs/events.md +19 -0
- package/dist/ui/managed-agents-console/docs/files.md +17 -0
- package/dist/ui/managed-agents-console/docs/github.md +11 -0
- package/dist/ui/managed-agents-console/docs/integrations.md +19 -0
- package/dist/ui/managed-agents-console/docs/memory.md +11 -0
- package/dist/ui/managed-agents-console/docs/migration.md +17 -0
- package/dist/ui/managed-agents-console/docs/multiagent.md +11 -0
- package/dist/ui/managed-agents-console/docs/outcomes.md +11 -0
- package/dist/ui/managed-agents-console/docs/overview.md +41 -0
- package/dist/ui/managed-agents-console/docs/permissions.md +24 -0
- package/dist/ui/managed-agents-console/docs/quickstart.md +51 -0
- package/dist/ui/managed-agents-console/docs/reference.md +27 -0
- package/dist/ui/managed-agents-console/docs/sandbox-reference.md +19 -0
- package/dist/ui/managed-agents-console/docs/sandbox-security.md +15 -0
- package/dist/ui/managed-agents-console/docs/scheduled-deployments.md +5 -0
- package/dist/ui/managed-agents-console/docs/self-hosted-sandboxes.md +15 -0
- package/dist/ui/managed-agents-console/docs/session-operations.md +21 -0
- package/dist/ui/managed-agents-console/docs/sessions.md +21 -0
- package/dist/ui/managed-agents-console/docs/skills.md +15 -0
- package/dist/ui/managed-agents-console/docs/tools.md +23 -0
- package/dist/ui/managed-agents-console/docs/vaults.md +19 -0
- package/dist/ui/managed-agents-console/docs/webhooks.md +11 -0
- package/dist/ui/managed-agents-console/index.html +47 -0
- package/dist/ui/managed-agents-console/serve.mjs +116 -0
- package/dist/ui/managed-agents-console/src/agents-files.jsx +273 -0
- package/dist/ui/managed-agents-console/src/api.js +1043 -0
- package/dist/ui/managed-agents-console/src/app.jsx +633 -0
- package/dist/ui/managed-agents-console/src/auth.jsx +215 -0
- package/dist/ui/managed-agents-console/src/console.css +733 -0
- package/dist/ui/managed-agents-console/src/data.js +188 -0
- package/dist/ui/managed-agents-console/src/detail.jsx +719 -0
- package/dist/ui/managed-agents-console/src/docs.jsx +200 -0
- package/dist/ui/managed-agents-console/src/environments.jsx +365 -0
- package/dist/ui/managed-agents-console/src/forms.jsx +494 -0
- package/dist/ui/managed-agents-console/src/icons.jsx +48 -0
- package/dist/ui/managed-agents-console/src/skills.jsx +21 -0
- package/dist/ui/managed-agents-console/src/sse.js +185 -0
- package/dist/ui/managed-agents-console/src/states.jsx +90 -0
- package/dist/ui/managed-agents-console/src/tweaks-panel.jsx +541 -0
- package/dist/ui/managed-agents-console/src/ui.jsx +190 -0
- package/dist/ui/managed-agents-console/src/vaults-data.js +89 -0
- package/dist/ui/managed-agents-console/src/vaults.jsx +171 -0
- package/dist/ui/managed-agents-console/vendor/babel.min.js +4 -0
- package/dist/ui/managed-agents-console/vendor/react-dom.production.min.js +267 -0
- package/dist/ui/managed-agents-console/vendor/react.production.min.js +31 -0
- package/dist/ui/openapi-docs/VENDOR.md +27 -0
- package/dist/ui/openapi-docs/index.html +16 -0
- package/dist/ui/openapi-docs/swagger-initializer.js +14 -0
- package/dist/ui/openapi-docs/vendor/LICENSE +202 -0
- package/dist/ui/openapi-docs/vendor/NOTICE +2 -0
- package/dist/ui/openapi-docs/vendor/swagger-ui-bundle.js +2 -0
- package/dist/ui/openapi-docs/vendor/swagger-ui-standalone-preset.js +2 -0
- package/dist/ui/openapi-docs/vendor/swagger-ui.css +3 -0
- package/package.json +53 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>OMA · Managed Agents Console</title>
|
|
7
|
+
<link rel="stylesheet" href="src/console.css" />
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="root"></div>
|
|
11
|
+
|
|
12
|
+
<!-- Vendored production builds (plan 0120 §3.1): the console must paint
|
|
13
|
+
without any network egress — air-gapped installs included. The CSS font
|
|
14
|
+
stacks fall back to system fonts for the same reason. -->
|
|
15
|
+
<script src="vendor/react.production.min.js"></script>
|
|
16
|
+
<script src="vendor/react-dom.production.min.js"></script>
|
|
17
|
+
<script src="vendor/babel.min.js"></script>
|
|
18
|
+
|
|
19
|
+
<script src="src/data.js"></script>
|
|
20
|
+
<script type="module" src="src/api.js"></script>
|
|
21
|
+
<script type="module">
|
|
22
|
+
import * as VaultsData from './src/vaults-data.js';
|
|
23
|
+
window.VaultsData = VaultsData;
|
|
24
|
+
</script>
|
|
25
|
+
<script type="text/babel" src="src/icons.jsx"></script>
|
|
26
|
+
<script type="text/babel" src="src/tweaks-panel.jsx"></script>
|
|
27
|
+
<script type="text/babel" src="src/ui.jsx"></script>
|
|
28
|
+
<script type="text/babel" src="src/states.jsx"></script>
|
|
29
|
+
<script type="text/babel" src="src/detail.jsx"></script>
|
|
30
|
+
<script type="text/babel" src="src/agents-files.jsx"></script>
|
|
31
|
+
<script type="text/babel" src="src/forms.jsx"></script>
|
|
32
|
+
<script type="text/babel" src="src/environments.jsx"></script>
|
|
33
|
+
<script type="text/babel" src="src/auth.jsx"></script>
|
|
34
|
+
<script type="text/babel" src="src/vaults.jsx"></script>
|
|
35
|
+
<script type="text/babel" src="src/skills.jsx"></script>
|
|
36
|
+
<script type="text/babel" src="src/docs.jsx"></script>
|
|
37
|
+
<script type="text/babel" src="src/app.jsx"></script>
|
|
38
|
+
|
|
39
|
+
<template id="__bundler_thumbnail">
|
|
40
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
|
41
|
+
<rect width="100" height="100" fill="#191c22"/>
|
|
42
|
+
<rect x="30" y="30" width="40" height="40" rx="9" fill="#eab44c"/>
|
|
43
|
+
<text x="50" y="58" font-family="monospace" font-size="22" font-weight="700" fill="#1a1d23" text-anchor="middle">O</text>
|
|
44
|
+
</svg>
|
|
45
|
+
</template>
|
|
46
|
+
</body>
|
|
47
|
+
</html>
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { createReadStream, existsSync, statSync } from "node:fs";
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
import { extname, join, normalize, resolve, sep } from "node:path";
|
|
4
|
+
|
|
5
|
+
const root = resolve(new URL(".", import.meta.url).pathname);
|
|
6
|
+
const port = Number(process.env.OMA_CONSOLE_PORT ?? 4177);
|
|
7
|
+
const apiBase = process.env.OMA_CONSOLE_API_BASE ?? "http://127.0.0.1:40178";
|
|
8
|
+
const betaHeader = [
|
|
9
|
+
"managed-agents-2026-04-01",
|
|
10
|
+
"files-api-2025-04-14",
|
|
11
|
+
].join(", ");
|
|
12
|
+
const strippedProxyResponseHeaders = new Set([
|
|
13
|
+
"content-encoding",
|
|
14
|
+
"content-length",
|
|
15
|
+
"transfer-encoding",
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
const contentTypes = new Map([
|
|
19
|
+
[".css", "text/css; charset=utf-8"],
|
|
20
|
+
[".html", "text/html; charset=utf-8"],
|
|
21
|
+
[".js", "text/javascript; charset=utf-8"],
|
|
22
|
+
[".jsx", "text/babel; charset=utf-8"],
|
|
23
|
+
[".json", "application/json; charset=utf-8"],
|
|
24
|
+
[".md", "text/markdown; charset=utf-8"],
|
|
25
|
+
[".png", "image/png"],
|
|
26
|
+
[".svg", "image/svg+xml; charset=utf-8"],
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
function resolvePath(urlPath) {
|
|
30
|
+
const clean = normalize(decodeURIComponent(urlPath.split("?")[0] ?? "/"));
|
|
31
|
+
const relative = clean === "/" ? "index.html" : clean.replace(/^\/+/, "");
|
|
32
|
+
const candidate = resolve(join(root, relative));
|
|
33
|
+
// Boundary-correct containment (#154): a bare startsWith(root) also
|
|
34
|
+
// accepts prefix-sharing siblings like <root>-private.
|
|
35
|
+
if (candidate !== root && !candidate.startsWith(root + sep)) return null;
|
|
36
|
+
if (!existsSync(candidate)) return null;
|
|
37
|
+
const stat = statSync(candidate);
|
|
38
|
+
if (stat.isDirectory()) return join(candidate, "index.html");
|
|
39
|
+
return candidate;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const server = createServer((req, res) => {
|
|
43
|
+
if ((req.url ?? "").startsWith("/v1/")) {
|
|
44
|
+
proxyApiRequest(req, res);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const filePath = resolvePath(req.url ?? "/");
|
|
49
|
+
if (!filePath) {
|
|
50
|
+
res.writeHead(404, { "content-type": "text/plain; charset=utf-8" });
|
|
51
|
+
res.end("Not found\n");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const contentType =
|
|
56
|
+
contentTypes.get(extname(filePath)) ?? "application/octet-stream";
|
|
57
|
+
res.writeHead(200, {
|
|
58
|
+
"cache-control": "no-store",
|
|
59
|
+
"content-type": contentType,
|
|
60
|
+
});
|
|
61
|
+
createReadStream(filePath).pipe(res);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
server.listen(port, "127.0.0.1", () => {
|
|
65
|
+
console.log(`Managed Agents Console: http://127.0.0.1:${port}/`);
|
|
66
|
+
console.log(`OMA API proxy: ${apiBase}`);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
async function proxyApiRequest(req, res) {
|
|
70
|
+
const target = new URL(req.url ?? "/", apiBase);
|
|
71
|
+
const headers = new Headers();
|
|
72
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
73
|
+
if (Array.isArray(value)) {
|
|
74
|
+
headers.set(key, value.join(", "));
|
|
75
|
+
} else if (value !== undefined) {
|
|
76
|
+
headers.set(key, value);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
headers.set("anthropic-beta", headers.get("anthropic-beta") ?? betaHeader);
|
|
80
|
+
headers.delete("host");
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const upstream = await fetch(target, {
|
|
84
|
+
method: req.method,
|
|
85
|
+
headers,
|
|
86
|
+
body: req.method === "GET" || req.method === "HEAD" ? undefined : req,
|
|
87
|
+
duplex: "half",
|
|
88
|
+
});
|
|
89
|
+
const responseHeaders = {};
|
|
90
|
+
upstream.headers.forEach((value, key) => {
|
|
91
|
+
if (strippedProxyResponseHeaders.has(key.toLowerCase())) return;
|
|
92
|
+
responseHeaders[key] = value;
|
|
93
|
+
});
|
|
94
|
+
res.writeHead(upstream.status, responseHeaders);
|
|
95
|
+
if (upstream.body) {
|
|
96
|
+
const reader = upstream.body.getReader();
|
|
97
|
+
while (true) {
|
|
98
|
+
const next = await reader.read();
|
|
99
|
+
if (next.done) break;
|
|
100
|
+
res.write(Buffer.from(next.value));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
res.end();
|
|
104
|
+
} catch (error) {
|
|
105
|
+
res.writeHead(502, { "content-type": "application/json; charset=utf-8" });
|
|
106
|
+
res.end(JSON.stringify({
|
|
107
|
+
type: "error",
|
|
108
|
+
error: {
|
|
109
|
+
type: "api_error",
|
|
110
|
+
message: `Could not reach OMA API at ${apiBase}`,
|
|
111
|
+
},
|
|
112
|
+
request_id: "req_console_proxy",
|
|
113
|
+
detail: String(error),
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
// agents-files.jsx — Agents list/detail + workspace Files → window
|
|
2
|
+
const { useState: useStateA } = React;
|
|
3
|
+
|
|
4
|
+
function AgentsList({ agents, openAgent, onCreate, dataState = 'loaded', readOnly = false }) {
|
|
5
|
+
const loading = dataState === 'loading';
|
|
6
|
+
const error = dataState === 'error';
|
|
7
|
+
const partial = dataState === 'partial';
|
|
8
|
+
const empty = dataState === 'empty' || agents.length === 0;
|
|
9
|
+
return (
|
|
10
|
+
<div className="main-scroll scroll fade-in">
|
|
11
|
+
<PageHead title="Agents" sub="Templates that define model + prompt. Create and manage Managed Agents."
|
|
12
|
+
action="Create agent" onAction={onCreate} readOnly={readOnly}
|
|
13
|
+
endpoint="POST /v1/agents" />
|
|
14
|
+
<div className="toolbar">
|
|
15
|
+
<Field wide placeholder="Search by agent ID" />
|
|
16
|
+
<Select label="Created" value="All time" />
|
|
17
|
+
<Select label="Status" value="Active" w={130} />
|
|
18
|
+
</div>
|
|
19
|
+
{partial && <PartialNotice resource="agents" />}
|
|
20
|
+
{loading ? <SkeletonTable rows={5} cols={[150, 'grow', 170, 'pill', 70, 70]} />
|
|
21
|
+
: error ? <ErrorState resource="agents" onRetry={() => {}} />
|
|
22
|
+
: empty ? <EmptyState icon="bot" title="No agents yet"
|
|
23
|
+
message="Agents are reusable templates that define a model, system prompt, and tools. Create one to get started."
|
|
24
|
+
actionLabel={!readOnly ? "Create agent" : null} onAction={onCreate} />
|
|
25
|
+
: <>
|
|
26
|
+
<div className="panel">
|
|
27
|
+
<div className="thead">
|
|
28
|
+
<span className="th" style={{ width:150 }}>ID</span>
|
|
29
|
+
<span className="th grow">Name</span>
|
|
30
|
+
<span className="th" style={{ width:170 }}>Model</span>
|
|
31
|
+
<span className="th" style={{ width:90 }}>Status</span>
|
|
32
|
+
<span className="th" style={{ width:70 }}>Created</span>
|
|
33
|
+
<span className="th" style={{ width:70 }}>Updated</span>
|
|
34
|
+
<span className="th" style={{ width:20 }} />
|
|
35
|
+
</div>
|
|
36
|
+
{agents.map((a) => (
|
|
37
|
+
<div className="trow" key={a.id} onClick={() => openAgent(a)}>
|
|
38
|
+
<span className="td mono" style={{ width:150, fontSize:12, color:'var(--soft)' }}>{a.short}</span>
|
|
39
|
+
<span className="td grow cell-strong">{a.name}</span>
|
|
40
|
+
<span className="td mono" style={{ width:170, fontSize:12.5, color:'var(--soft)' }}>{a.model}</span>
|
|
41
|
+
<span className="td" style={{ width:90 }}><St k={a.status} /></span>
|
|
42
|
+
<span className="td mono" style={{ width:70, color:'var(--faint)' }}>{a.created}</span>
|
|
43
|
+
<span className="td mono" style={{ width:70, color:'var(--faint)' }}>{a.updated}</span>
|
|
44
|
+
<span className="td" style={{ width:20 }}><Kebab /></span>
|
|
45
|
+
</div>
|
|
46
|
+
))}
|
|
47
|
+
</div>
|
|
48
|
+
<Pager />
|
|
49
|
+
</>}
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function AgentDetail({ agent, go, onCreateSession, onArchive, onUpdateToolPermission, createSessionReadOnly = false, archiveReadOnly = false }) {
|
|
55
|
+
const a = agent;
|
|
56
|
+
const mcpServers = Array.isArray(a.mcpServers) ? a.mcpServers : [];
|
|
57
|
+
const configuredSkills = Array.isArray(a.skills) ? a.skills : [];
|
|
58
|
+
const [tab, setTab] = useStateA('agent');
|
|
59
|
+
const [dialog, setDialog] = useStateA(false);
|
|
60
|
+
const [lifecycleBusy, setLifecycleBusy] = useStateA(false);
|
|
61
|
+
const [lifecycleError, setLifecycleError] = useStateA(null);
|
|
62
|
+
const currentToolPolicy = a.toolPermission === 'Ask before use'
|
|
63
|
+
? 'always_ask'
|
|
64
|
+
: a.toolPermission === 'Always allow'
|
|
65
|
+
? 'always_allow'
|
|
66
|
+
: a.toolPermission === 'Mixed permissions'
|
|
67
|
+
? 'mixed'
|
|
68
|
+
: 'none';
|
|
69
|
+
const hasEnabledTools = a.toolPermission !== 'No enabled tools';
|
|
70
|
+
const [toolPolicy, setToolPolicy] = useStateA(currentToolPolicy);
|
|
71
|
+
const [toolPolicyBusy, setToolPolicyBusy] = useStateA(false);
|
|
72
|
+
const [toolPolicyError, setToolPolicyError] = useStateA(null);
|
|
73
|
+
const archived = a.status === 'archived';
|
|
74
|
+
const confirmArchive = async () => {
|
|
75
|
+
if (!onArchive || lifecycleBusy) return;
|
|
76
|
+
setLifecycleBusy(true);
|
|
77
|
+
setLifecycleError(null);
|
|
78
|
+
try {
|
|
79
|
+
await onArchive();
|
|
80
|
+
setDialog(false);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
setLifecycleError(error);
|
|
83
|
+
} finally {
|
|
84
|
+
setLifecycleBusy(false);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const saveToolPolicy = async () => {
|
|
88
|
+
if (!onUpdateToolPermission || toolPolicyBusy || toolPolicy === currentToolPolicy) return;
|
|
89
|
+
setToolPolicyBusy(true);
|
|
90
|
+
setToolPolicyError(null);
|
|
91
|
+
try {
|
|
92
|
+
await onUpdateToolPermission(toolPolicy);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
setToolPolicyError(error);
|
|
95
|
+
} finally {
|
|
96
|
+
setToolPolicyBusy(false);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
return (
|
|
100
|
+
<div className="main-scroll scroll fade-in">
|
|
101
|
+
<Crumbs items={[{ label:'Agents', onClick:() => go('agents') }, { label:a.name }]} />
|
|
102
|
+
<div className="sess-head">
|
|
103
|
+
<div>
|
|
104
|
+
<div className="sess-title"><h1>{a.name}</h1><St k={a.status} /></div>
|
|
105
|
+
<div className="meta-row mono" style={{ fontSize:12.5, color:'var(--faint)' }}>{a.id} · Last updated {a.updated}</div>
|
|
106
|
+
</div>
|
|
107
|
+
<div style={{ display:'flex', gap:9 }}>
|
|
108
|
+
<button className="btn" disabled={archived || archiveReadOnly || lifecycleBusy} title={archiveReadOnly ? 'Connect a live workspace to archive this agent.' : undefined}
|
|
109
|
+
style={{ opacity: archived || archiveReadOnly ? .5 : 1 }} onClick={() => { if (!archiveReadOnly) { setLifecycleError(null); setDialog(true); } }}>
|
|
110
|
+
<Icon name="archive" size={14} />{archived ? 'Archived' : 'Archive'}
|
|
111
|
+
</button>
|
|
112
|
+
<button className="btn btn-primary" disabled={archived || createSessionReadOnly} title={archived ? 'Archived agents cannot create new sessions.' : createSessionReadOnly ? 'Connect a live workspace to create a session.' : undefined}
|
|
113
|
+
onClick={() => !archived && !createSessionReadOnly && onCreateSession()}>
|
|
114
|
+
<Icon name="plus" size={15} />Create session
|
|
115
|
+
</button>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
{dialog &&
|
|
120
|
+
<ConfirmDialog icon="archive" title="Archive this agent?"
|
|
121
|
+
message={<>Archiving <b>{a.name}</b> hides it from the default list and stops it appearing in new-session pickers. Existing sessions are unaffected.</>}
|
|
122
|
+
confirmLabel="Archive agent" endpoint="POST /v1/agents/:id/archive"
|
|
123
|
+
onClose={() => setDialog(false)}
|
|
124
|
+
onConfirm={confirmArchive} busy={lifecycleBusy} error={lifecycleError} />}
|
|
125
|
+
|
|
126
|
+
<div style={{ display:'flex', gap:20, borderBottom:'1px solid var(--border)', marginBottom:22 }}>
|
|
127
|
+
{['agent','sessions'].map((t) => (
|
|
128
|
+
<div key={t} onClick={() => setTab(t)} style={{ padding:'0 0 11px', cursor:'pointer', textTransform:'capitalize',
|
|
129
|
+
color: tab === t ? 'var(--ink)' : 'var(--soft)', fontWeight: tab === t ? 600 : 400,
|
|
130
|
+
borderBottom: tab === t ? '2px solid var(--accent)' : '2px solid transparent', marginBottom:-1 }}>
|
|
131
|
+
{t === 'agent' ? 'Agent' : 'Sessions'}
|
|
132
|
+
</div>
|
|
133
|
+
))}
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<div style={{ display:'grid', gridTemplateColumns:'1fr 250px', gap:24, alignItems:'start' }}>
|
|
137
|
+
<div>
|
|
138
|
+
<div className="field select" style={{ width:120, marginBottom:22 }}>
|
|
139
|
+
<span style={{ color:'var(--faint)' }}>Version</span><b>{a.version}</b><Icon name="chevDown" size={14} className="chev" />
|
|
140
|
+
</div>
|
|
141
|
+
<div className="section">
|
|
142
|
+
<div className="sec-label" style={{ marginBottom:8 }}>Model</div>
|
|
143
|
+
<div className="mono" style={{ fontSize:14 }}>{a.model}</div>
|
|
144
|
+
</div>
|
|
145
|
+
{a.description && <div className="section"><div className="sec-label" style={{ marginBottom:8 }}>Description</div><div style={{ color:'var(--soft)', fontSize:13.5 }}>{a.description}</div></div>}
|
|
146
|
+
<div className="section">
|
|
147
|
+
<div className="sec-label" style={{ marginBottom:8 }}>System prompt</div>
|
|
148
|
+
<div className="prompt-box">{a.system}</div>
|
|
149
|
+
</div>
|
|
150
|
+
<div className="section">
|
|
151
|
+
<div className="sec-label" style={{ marginBottom:8 }}>MCPs and tools</div>
|
|
152
|
+
<div className="panel">
|
|
153
|
+
<div className="tool-card">
|
|
154
|
+
<div className="tool-ico"><Icon name="cpu" size={18} /></div>
|
|
155
|
+
<div><div className="cell-strong" style={{ fontSize:13.5 }}>Built-in tools</div>
|
|
156
|
+
<div className="mono" style={{ fontSize:11.5, color:'var(--faint)', marginTop:2 }}>{a.toolset}</div></div>
|
|
157
|
+
</div>
|
|
158
|
+
<div className="disclosure">
|
|
159
|
+
<div style={{ display:'flex', alignItems:'center', gap:9 }}>
|
|
160
|
+
<Icon name="chevRight" size={14} style={{ color:'var(--faint)' }} />
|
|
161
|
+
<span style={{ fontSize:13.5 }}>Tool permissions</span>
|
|
162
|
+
<span className="pill" style={{ height:20 }}>{a.tools}</span>
|
|
163
|
+
</div>
|
|
164
|
+
<span style={{ display:'flex', alignItems:'center', gap:6, color:'var(--soft)', fontSize:12.5 }}>
|
|
165
|
+
<Icon name="checkCircle" size={14} style={{ color:'var(--green)' }} />{a.toolPermission || 'Always allow'}</span>
|
|
166
|
+
</div>
|
|
167
|
+
{mcpServers.map((server) => <div className="tool-card" key={server.name}>
|
|
168
|
+
<div className="tool-ico"><Icon name="database" size={18} /></div>
|
|
169
|
+
<div><div className="cell-strong" style={{ fontSize:13.5 }}>{server.name}</div>
|
|
170
|
+
<div className="mono" style={{ fontSize:11.5, color:'var(--faint)', marginTop:2 }}>{server.url}</div></div>
|
|
171
|
+
</div>)}
|
|
172
|
+
<div style={{ padding:'12px 14px', borderTop:'1px solid var(--border)' }}>
|
|
173
|
+
<div className="form-row two" style={{ alignItems:'end' }}>
|
|
174
|
+
<Labeled label="Approval policy" htmlFor="agent-tool-approval" hint="Saving creates a new immutable agent version. Existing sessions keep their current policy.">
|
|
175
|
+
<select id="agent-tool-approval" className="selectbox" value={toolPolicy}
|
|
176
|
+
disabled={!hasEnabledTools || archived || archiveReadOnly || toolPolicyBusy}
|
|
177
|
+
onChange={(event) => { setToolPolicy(event.target.value); setToolPolicyError(null); }}>
|
|
178
|
+
{currentToolPolicy === 'mixed' && <option value="mixed" disabled>Mixed permissions</option>}
|
|
179
|
+
{currentToolPolicy === 'none' && <option value="none" disabled>No enabled tools</option>}
|
|
180
|
+
<option value="always_ask">Ask before use</option>
|
|
181
|
+
<option value="always_allow">Allow automatically</option>
|
|
182
|
+
</select>
|
|
183
|
+
</Labeled>
|
|
184
|
+
<button className="btn btn-primary" disabled={!hasEnabledTools || archived || archiveReadOnly || toolPolicyBusy || toolPolicy === currentToolPolicy}
|
|
185
|
+
onClick={saveToolPolicy}>{toolPolicyBusy ? 'Saving…' : 'Save new version'}</button>
|
|
186
|
+
</div>
|
|
187
|
+
{toolPolicy === 'always_allow' && <div className="inline-warn" role="status"><Icon name="alert" size={14} /><span>Enabled tools can run without confirmation; sandbox and network restrictions still apply.</span></div>}
|
|
188
|
+
{toolPolicyError && <div className="inline-warn" role="alert"><Icon name="alert" size={14} /><span>{toolPolicyError.message}</span></div>}
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
<div className="section">
|
|
193
|
+
<div className="sec-label" style={{ marginBottom:8 }}>Skills</div>
|
|
194
|
+
{configuredSkills.length === 0 ? <div style={{ color:'var(--faint)', fontSize:13.5 }}>No skills configured.</div>
|
|
195
|
+
: <div className="panel">{configuredSkills.map((skill) => <div className="tool-card" key={`${skill.type}:${skill.skill_id}:${skill.version || 'latest'}`}>
|
|
196
|
+
<div className="tool-ico"><Icon name="fileText" size={18} /></div><div><div className="cell-strong" style={{ fontSize:13.5 }}>{skill.skill_id}</div><div className="mono" style={{ fontSize:11.5, color:'var(--faint)', marginTop:2 }}>{skill.type} · {skill.version || 'latest'}</div></div>
|
|
197
|
+
</div>)}</div>}
|
|
198
|
+
</div>
|
|
199
|
+
{Object.keys(a.metadata || {}).length > 0 && <div className="section"><div className="sec-label" style={{ marginBottom:8 }}>Metadata</div><pre className="code" style={{ whiteSpace:'pre-wrap' }}>{JSON.stringify(a.metadata, null, 2)}</pre></div>}
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<div className="panel" style={{ padding:'14px 16px' }}>
|
|
203
|
+
<div className="sec-label" style={{ marginBottom:6 }}>Linked sessions</div>
|
|
204
|
+
{a.sessions.length === 0 && <div style={{ color:'var(--faint)', fontSize:13, padding:'8px 0' }}>None yet.</div>}
|
|
205
|
+
{a.sessions.map((t, i) => (
|
|
206
|
+
<div className="linked" key={i}>
|
|
207
|
+
<span style={{ overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{t}</span>
|
|
208
|
+
<St k="idle" />
|
|
209
|
+
</div>
|
|
210
|
+
))}
|
|
211
|
+
{a.sessions.length > 0 && <div className="dl" style={{ marginTop:10 }}>View all {a.sessions.length}<Icon name="arrowRight" size={13} /></div>}
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function FilesView({ files = FILES, dataState = 'loaded', readOnly = false }) {
|
|
219
|
+
const [scoped, setScoped] = useStateA(true);
|
|
220
|
+
const loading = dataState === 'loading';
|
|
221
|
+
const error = dataState === 'error';
|
|
222
|
+
const partial = dataState === 'partial';
|
|
223
|
+
const empty = dataState === 'empty';
|
|
224
|
+
return (
|
|
225
|
+
<div className="main-scroll scroll fade-in">
|
|
226
|
+
<PageHead title="Files" sub="Workspace uploads and session-scoped output files."
|
|
227
|
+
action="Upload file" readOnly={true} endpoint="POST /v1/files" />
|
|
228
|
+
<div className="toolbar">
|
|
229
|
+
<Field wide placeholder="Search files" />
|
|
230
|
+
<div className="field select" onClick={() => setScoped(!scoped)} style={{ width:210 }}>
|
|
231
|
+
<span style={{ color:'var(--faint)' }}>Scope</span>
|
|
232
|
+
<b>{scoped ? 'Session · …VcyvLi' : 'All workspace'}</b>
|
|
233
|
+
<Icon name="chevDown" size={14} className="chev" />
|
|
234
|
+
</div>
|
|
235
|
+
<Select label="Type" value="All" w={110} />
|
|
236
|
+
</div>
|
|
237
|
+
{partial && <PartialNotice resource="files" />}
|
|
238
|
+
{loading ? <SkeletonTable rows={5} cols={['grow', 90, 80, 70, 110]} />
|
|
239
|
+
: error ? <ErrorState resource="files" onRetry={() => {}} />
|
|
240
|
+
: empty ? <EmptyState icon="folder" title="No files here"
|
|
241
|
+
message="No output files for this session yet. Files generated by the agent appear here and can be downloaded."
|
|
242
|
+
actionLabel={null} onAction={() => {}} />
|
|
243
|
+
: <>
|
|
244
|
+
<div className="panel">
|
|
245
|
+
<div className="thead">
|
|
246
|
+
<span className="th" style={{ width:15 }}><span className="checkbox" /></span>
|
|
247
|
+
<span className="th grow">Filename</span>
|
|
248
|
+
<span className="th" style={{ width:90 }}>Type</span>
|
|
249
|
+
<span className="th" style={{ width:80 }}>Size</span>
|
|
250
|
+
<span className="th" style={{ width:70 }}>Created</span>
|
|
251
|
+
<span className="th" style={{ width:110 }}>Download</span>
|
|
252
|
+
</div>
|
|
253
|
+
{files.map((f, i) => (
|
|
254
|
+
<div className={'trow' + (f.dl ? '' : ' inert-row')} key={i}>
|
|
255
|
+
<span className="td" style={{ width:15 }}><span className="checkbox" /></span>
|
|
256
|
+
<span className="td grow" style={{ display:'flex', alignItems:'center', gap:11 }}>
|
|
257
|
+
<span className="file-ico">{f.ext}</span><span className="cell-strong">{f.name}</span></span>
|
|
258
|
+
<span className="td mono" style={{ width:90, fontSize:11.5, color:'var(--faint)' }}>{f.type}</span>
|
|
259
|
+
<span className="td mono" style={{ width:80, color:'var(--soft)' }}>{f.size}</span>
|
|
260
|
+
<span className="td mono" style={{ width:70, color:'var(--faint)' }}>{f.created}</span>
|
|
261
|
+
<span className="td" style={{ width:110 }}>
|
|
262
|
+
{f.dl && f.href ? <a className="dl" href="#" onClick={(e) => { e.preventDefault(); OmaConsoleApi.downloadFile(f.href, f.name); }}><Icon name="download" size={14} />Download</a> : <span className="inert">{f.dl ? '— demo file' : '— mounted input'}</span>}
|
|
263
|
+
</span>
|
|
264
|
+
</div>
|
|
265
|
+
))}
|
|
266
|
+
</div>
|
|
267
|
+
<Pager />
|
|
268
|
+
</>}
|
|
269
|
+
</div>
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
Object.assign(window, { AgentsList, AgentDetail, FilesView });
|