comisai 1.0.24 → 1.0.26
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/node_modules/@comis/agent/dist/bootstrap/sections/tool-descriptions.js +130 -10
- package/node_modules/@comis/agent/dist/bootstrap/sections/tooling-sections.d.ts +1 -1
- package/node_modules/@comis/agent/dist/bootstrap/sections/tooling-sections.js +9 -2
- package/node_modules/@comis/agent/dist/bridge/bridge-metrics.d.ts +8 -0
- package/node_modules/@comis/agent/dist/bridge/bridge-metrics.js +2 -0
- package/node_modules/@comis/agent/dist/bridge/pi-event-bridge.d.ts +29 -0
- package/node_modules/@comis/agent/dist/bridge/pi-event-bridge.js +242 -2
- package/node_modules/@comis/agent/dist/bridge/thinking-block-hash-invariant.d.ts +210 -0
- package/node_modules/@comis/agent/dist/bridge/thinking-block-hash-invariant.js +566 -0
- package/node_modules/@comis/agent/dist/context-engine/context-engine.js +8 -6
- package/node_modules/@comis/agent/dist/context-engine/signature-replay-scrubber.d.ts +51 -30
- package/node_modules/@comis/agent/dist/context-engine/signature-replay-scrubber.js +109 -36
- package/node_modules/@comis/agent/dist/executor/executor-context-engine-setup.js +5 -1
- package/node_modules/@comis/agent/dist/executor/executor-post-execution.js +22 -20
- package/node_modules/@comis/agent/dist/executor/executor-prompt-runner.d.ts +2 -0
- package/node_modules/@comis/agent/dist/executor/executor-prompt-runner.js +111 -15
- package/node_modules/@comis/agent/dist/executor/executor-response-filter.d.ts +20 -17
- package/node_modules/@comis/agent/dist/executor/executor-response-filter.js +132 -52
- package/node_modules/@comis/agent/dist/executor/executor-tool-assembly.js +16 -3
- package/node_modules/@comis/agent/dist/executor/model-retry.d.ts +14 -0
- package/node_modules/@comis/agent/dist/executor/model-retry.js +72 -1
- package/node_modules/@comis/agent/dist/executor/pi-executor.d.ts +3 -0
- package/node_modules/@comis/agent/dist/executor/pi-executor.js +68 -9
- package/node_modules/@comis/agent/dist/executor/post-batch-continuation.d.ts +82 -0
- package/node_modules/@comis/agent/dist/executor/post-batch-continuation.js +200 -0
- package/node_modules/@comis/agent/dist/executor/stream-wrappers/request-body-injector.js +1 -9
- package/node_modules/@comis/agent/dist/executor/tool-deferral.d.ts +37 -2
- package/node_modules/@comis/agent/dist/executor/tool-deferral.js +45 -3
- package/node_modules/@comis/agent/dist/executor/tool-parallelism.js +0 -1
- package/node_modules/@comis/agent/dist/executor/types.d.ts +11 -2
- package/node_modules/@comis/agent/dist/index.d.ts +3 -1
- package/node_modules/@comis/agent/dist/index.js +2 -0
- package/node_modules/@comis/agent/dist/model/last-known-model.d.ts +36 -0
- package/node_modules/@comis/agent/dist/model/last-known-model.js +49 -0
- package/node_modules/@comis/agent/dist/model/model-registry-adapter.d.ts +16 -4
- package/node_modules/@comis/agent/dist/model/model-registry-adapter.js +65 -21
- package/node_modules/@comis/agent/dist/planner/types.d.ts +0 -2
- package/node_modules/@comis/agent/dist/session/comis-session-manager.d.ts +10 -0
- package/node_modules/@comis/agent/dist/session/comis-session-manager.js +5 -0
- package/node_modules/@comis/agent/dist/spawn/pi-mono-adapters.js +7 -0
- package/node_modules/@comis/agent/package.json +1 -1
- package/node_modules/@comis/channels/package.json +1 -1
- package/node_modules/@comis/cli/dist/client/rpc-client.js +6 -1
- package/node_modules/@comis/cli/dist/commands/doctor.js +5 -3
- package/node_modules/@comis/cli/dist/commands/health.js +5 -2
- package/node_modules/@comis/cli/dist/wizard/json-output.js +7 -3
- package/node_modules/@comis/cli/dist/wizard/steps/11-daemon-start.js +130 -0
- package/node_modules/@comis/cli/package.json +1 -1
- package/node_modules/@comis/core/dist/bootstrap.js +5 -0
- package/node_modules/@comis/core/dist/config/env-layer.d.ts +31 -0
- package/node_modules/@comis/core/dist/config/env-layer.js +41 -0
- package/node_modules/@comis/core/dist/config/immutable-keys.d.ts +2 -2
- package/node_modules/@comis/core/dist/config/immutable-keys.js +8 -3
- package/node_modules/@comis/core/dist/config/layered.d.ts +9 -0
- package/node_modules/@comis/core/dist/config/layered.js +11 -0
- package/node_modules/@comis/core/dist/config/managed-sections.d.ts +43 -4
- package/node_modules/@comis/core/dist/config/managed-sections.js +100 -6
- package/node_modules/@comis/core/dist/config/schema-agent.d.ts +39 -0
- package/node_modules/@comis/core/dist/config/schema-agent.js +14 -0
- package/node_modules/@comis/core/dist/config/schema.d.ts +4 -0
- package/node_modules/@comis/core/dist/config/schema.js +14 -0
- package/node_modules/@comis/core/dist/domain/execution-graph.d.ts +1 -1
- package/node_modules/@comis/core/dist/event-bus/events-agent.d.ts +17 -2
- package/node_modules/@comis/core/dist/exports/config.d.ts +2 -2
- package/node_modules/@comis/core/dist/exports/config.js +1 -1
- package/node_modules/@comis/core/package.json +1 -1
- package/node_modules/@comis/daemon/dist/daemon.d.ts +22 -0
- package/node_modules/@comis/daemon/dist/daemon.js +45 -0
- package/node_modules/@comis/daemon/dist/rpc/agent-handlers.d.ts +5 -2
- package/node_modules/@comis/daemon/dist/rpc/agent-handlers.js +80 -1
- package/node_modules/@comis/daemon/dist/rpc/agent-inline-workspace.d.ts +67 -0
- package/node_modules/@comis/daemon/dist/rpc/agent-inline-workspace.js +139 -0
- package/node_modules/@comis/daemon/dist/rpc/model-handlers.d.ts +3 -0
- package/node_modules/@comis/daemon/dist/rpc/model-handlers.js +29 -5
- package/node_modules/@comis/daemon/dist/rpc/probe-provider-auth.d.ts +30 -0
- package/node_modules/@comis/daemon/dist/rpc/probe-provider-auth.js +59 -0
- package/node_modules/@comis/daemon/dist/rpc/provider-handlers.d.ts +37 -0
- package/node_modules/@comis/daemon/dist/rpc/provider-handlers.js +330 -0
- package/node_modules/@comis/daemon/dist/rpc/rpc-dispatch.js +18 -1
- package/node_modules/@comis/daemon/dist/setup-docker-restart-warn.d.ts +4 -0
- package/node_modules/@comis/daemon/dist/setup-docker-restart-warn.js +30 -0
- package/node_modules/@comis/daemon/dist/wiring/setup-agents.d.ts +3 -1
- package/node_modules/@comis/daemon/dist/wiring/setup-agents.js +28 -2
- package/node_modules/@comis/daemon/dist/wiring/setup-cross-session.js +1 -0
- package/node_modules/@comis/daemon/dist/wiring/setup-tools.js +7 -4
- package/node_modules/@comis/daemon/package.json +1 -1
- package/node_modules/@comis/gateway/package.json +1 -1
- package/node_modules/@comis/infra/dist/index.d.ts +1 -0
- package/node_modules/@comis/infra/dist/index.js +2 -0
- package/node_modules/@comis/infra/dist/runtime/is-docker.d.ts +1 -0
- package/node_modules/@comis/infra/dist/runtime/is-docker.js +25 -0
- package/node_modules/@comis/infra/package.json +1 -1
- package/node_modules/@comis/memory/package.json +1 -1
- package/node_modules/@comis/scheduler/package.json +1 -1
- package/node_modules/@comis/shared/package.json +1 -1
- package/node_modules/@comis/skills/dist/bridge/tool-metadata-registry.js +1 -3
- package/node_modules/@comis/skills/dist/builtin/platform/admin-manage-factory.js +24 -1
- package/node_modules/@comis/skills/dist/builtin/platform/agents-manage-tool.d.ts +53 -7
- package/node_modules/@comis/skills/dist/builtin/platform/agents-manage-tool.js +218 -24
- package/node_modules/@comis/skills/dist/builtin/platform/gateway-tool.d.ts +4 -1
- package/node_modules/@comis/skills/dist/builtin/platform/gateway-tool.js +16 -1
- package/node_modules/@comis/skills/dist/builtin/platform/index.d.ts +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/index.js +1 -1
- package/node_modules/@comis/skills/dist/builtin/platform/providers-manage-tool.d.ts +56 -0
- package/node_modules/@comis/skills/dist/builtin/platform/providers-manage-tool.js +203 -0
- package/node_modules/@comis/skills/dist/index.d.ts +1 -1
- package/node_modules/@comis/skills/dist/index.js +2 -2
- package/node_modules/@comis/skills/dist/policy/tool-policy.js +0 -1
- package/node_modules/@comis/skills/package.json +1 -1
- package/node_modules/@comis/web/dist/assets/{agent-detail-BG9MGWWj.js → agent-detail-DqL6Artv.js} +270 -270
- package/node_modules/@comis/web/dist/assets/agent-editor-CNM_h94Y.js +2173 -0
- package/node_modules/@comis/web/dist/assets/{agent-list-LHCJ4rw2.js → agent-list-Dbh-xD_F.js} +170 -170
- package/node_modules/@comis/web/dist/assets/{approvals-q9VH_IKr.js → approvals-C-K6hN2U.js} +13 -13
- package/node_modules/@comis/web/dist/assets/billing-view-C1DmtyzK.js +375 -0
- package/node_modules/@comis/web/dist/assets/{channel-detail-CaInesJM.js → channel-detail-CtCH22N1.js} +265 -265
- package/node_modules/@comis/web/dist/assets/channel-list-C7xXn-60.js +323 -0
- package/node_modules/@comis/web/dist/assets/{chat-console-CNmzl0JW.js → chat-console-C51pjFwk.js} +243 -246
- package/node_modules/@comis/web/dist/assets/{config-editor-DX4ITw6y.js → config-editor-BLArYRB7.js} +477 -477
- package/node_modules/@comis/web/dist/assets/{context-dag-browser-BwiaF5tf.js → context-dag-browser-fuyMinNI.js} +105 -105
- package/node_modules/@comis/web/dist/assets/{context-engine-BZ5Am6hA.js → context-engine-Bngf2bH0.js} +136 -136
- package/node_modules/@comis/web/dist/assets/decorate-BvWYovGE.js +38 -0
- package/node_modules/@comis/web/dist/assets/{delivery-view-OfBZof-m.js → delivery-view-C80hucxX.js} +134 -134
- package/node_modules/@comis/web/dist/assets/{diagnostics-view-YFwCxgr2.js → diagnostics-view-Cl4VbHZ6.js} +82 -82
- package/node_modules/@comis/web/dist/assets/directive-BOYXJ-K-.js +1 -0
- package/node_modules/@comis/web/dist/assets/{extract-variables-BM5qyK-s.js → extract-variables-B7-Doo7l.js} +39 -39
- package/node_modules/@comis/web/dist/assets/{ic-array-editor-B7m6x7-S.js → ic-array-editor-BLoEyeLS.js} +29 -29
- package/node_modules/@comis/web/dist/assets/{ic-breadcrumb-CUMpp3BL.js → ic-breadcrumb-DqN6G3gc.js} +16 -16
- package/node_modules/@comis/web/dist/assets/{ic-budget-segment-bar-BtJ6x5mN.js → ic-budget-segment-bar-zLsMzjDO.js} +20 -20
- package/node_modules/@comis/web/dist/assets/ic-chat-message-ByFUoMm6.js +352 -0
- package/node_modules/@comis/web/dist/assets/{ic-confirm-dialog-CCDbB04e.js → ic-confirm-dialog-DGlPbV1T.js} +26 -26
- package/node_modules/@comis/web/dist/assets/{ic-connection-dot-CnT1b8xr.js → ic-connection-dot-C4nDHgY2.js} +13 -13
- package/node_modules/@comis/web/dist/assets/ic-data-table-CKIvr-ag.js +277 -0
- package/node_modules/@comis/web/dist/assets/ic-delivery-row-B3YwjjuM.js +67 -0
- package/node_modules/@comis/web/dist/assets/{ic-detail-panel-BF83r-if.js → ic-detail-panel-DiCe4hLr.js} +27 -27
- package/node_modules/@comis/web/dist/assets/{ic-empty-state-60l2ePhd.js → ic-empty-state-CM3Wbj2f.js} +19 -19
- package/node_modules/@comis/web/dist/assets/ic-graph-canvas-ByRjij68.js +359 -0
- package/node_modules/@comis/web/dist/assets/ic-icon-BGNCCPpZ.js +33 -0
- package/node_modules/@comis/web/dist/assets/{ic-layer-waterfall-COvEYMg5.js → ic-layer-waterfall-WkaFyu-l.js} +18 -18
- package/node_modules/@comis/web/dist/assets/ic-relative-time-B3UAnTqg.js +12 -0
- package/node_modules/@comis/web/dist/assets/{ic-search-input-CSOxY9g7.js → ic-search-input-B02AGw1i.js} +22 -22
- package/node_modules/@comis/web/dist/assets/{ic-select-Ce-Raudx.js → ic-select-BqfZISjw.js} +29 -29
- package/node_modules/@comis/web/dist/assets/ic-tabs-yBjkWKJH.js +95 -0
- package/node_modules/@comis/web/dist/assets/ic-tag-CvMVQFRR.js +33 -0
- package/node_modules/@comis/web/dist/assets/{ic-time-range-picker-CypCT68y.js → ic-time-range-picker-DXbYeBmY.js} +31 -31
- package/node_modules/@comis/web/dist/assets/{ic-tool-call-7MaXSsCW.js → ic-tool-call-Bh5kq-yY.js} +51 -51
- package/node_modules/@comis/web/dist/assets/index-BBkuC-EU.js +2792 -0
- package/node_modules/@comis/web/dist/assets/index-CVEaS9aY.css +2 -0
- package/node_modules/@comis/web/dist/assets/{mcp-management-BNZPnpDn.js → mcp-management-DB-phOo7.js} +209 -209
- package/node_modules/@comis/web/dist/assets/{media-config-BBvTYxOX.js → media-config-CRqZ1ZUH.js} +154 -154
- package/node_modules/@comis/web/dist/assets/{media-test-BkK3RCRK.js → media-test-C9vE20Oy.js} +259 -259
- package/node_modules/@comis/web/dist/assets/{memory-inspector-1hDGCGat.js → memory-inspector-CeqfnxMZ.js} +450 -450
- package/node_modules/@comis/web/dist/assets/{message-center-CXefwsUu.js → message-center-Daup7Mof.js} +290 -290
- package/node_modules/@comis/web/dist/assets/{models-C1qcU_j3.js → models-DLYnEU8E.js} +371 -371
- package/node_modules/@comis/web/dist/assets/observability-types-D0tkwElU.js +1 -0
- package/node_modules/@comis/web/dist/assets/{observe-view-C0VBhX4C.js → observe-view-BTSt_PO5.js} +399 -399
- package/node_modules/@comis/web/dist/assets/pipeline-builder-DknfzyLt.js +1495 -0
- package/node_modules/@comis/web/dist/assets/{pipeline-history-DkfOQ6SW.js → pipeline-history-JnHZdeU_.js} +124 -124
- package/node_modules/@comis/web/dist/assets/{pipeline-history-detail-hyHgD0ai.js → pipeline-history-detail-Dg4knsEb.js} +65 -65
- package/node_modules/@comis/web/dist/assets/{pipeline-list-BPW8hV-q.js → pipeline-list-AEnibjsp.js} +227 -227
- package/node_modules/@comis/web/dist/assets/{pipeline-monitor-Bip16T7e.js → pipeline-monitor-DG7RbIOO.js} +298 -298
- package/node_modules/@comis/web/dist/assets/{scheduler-BGgwKd06.js → scheduler-uL1fYKAT.js} +486 -486
- package/node_modules/@comis/web/dist/assets/{security-D15st4xx.js → security-C3DywRLH.js} +389 -389
- package/node_modules/@comis/web/dist/assets/{session-detail-SGEYNJ0M.js → session-detail-BtqCNWXV.js} +294 -294
- package/node_modules/@comis/web/dist/assets/session-key-parser-Dkqcj2Ss.js +1 -0
- package/node_modules/@comis/web/dist/assets/session-list-CJXWa2XT.js +231 -0
- package/node_modules/@comis/web/dist/assets/{setup-wizard-nT0tz9QP.js → setup-wizard-ywn7oJvu.js} +486 -494
- package/node_modules/@comis/web/dist/assets/{skills-D8yVfSUy.js → skills-DX0KYnWD.js} +329 -329
- package/node_modules/@comis/web/dist/assets/{subagents-HHXMeHYo.js → subagents-B8p5YJEB.js} +74 -74
- package/node_modules/@comis/web/dist/assets/{workspace-manager-BQlr10iH.js → workspace-manager-CgzNIrw1.js} +236 -236
- package/node_modules/@comis/web/dist/index.html +3 -2
- package/node_modules/@comis/web/package.json +1 -1
- package/package.json +15 -15
- package/node_modules/@comis/skills/dist/builtin/platform/agents-list-tool.d.ts +0 -19
- package/node_modules/@comis/skills/dist/builtin/platform/agents-list-tool.js +0 -39
- package/node_modules/@comis/web/dist/assets/agent-editor-C26Q_xCs.js +0 -2173
- package/node_modules/@comis/web/dist/assets/billing-view-CtYvBqTE.js +0 -375
- package/node_modules/@comis/web/dist/assets/channel-list-B8dj3O9a.js +0 -323
- package/node_modules/@comis/web/dist/assets/directive-DoeGSK_T.js +0 -1
- package/node_modules/@comis/web/dist/assets/ic-chat-message-CFyDJd0z.js +0 -352
- package/node_modules/@comis/web/dist/assets/ic-data-table-CKUNTxHw.js +0 -277
- package/node_modules/@comis/web/dist/assets/ic-delivery-row-GP5Fkygs.js +0 -67
- package/node_modules/@comis/web/dist/assets/ic-graph-canvas-C8FuSMe1.js +0 -359
- package/node_modules/@comis/web/dist/assets/ic-icon-xeGTVhVG.js +0 -33
- package/node_modules/@comis/web/dist/assets/ic-relative-time-3FqpjeAI.js +0 -12
- package/node_modules/@comis/web/dist/assets/ic-tabs-B7QtM_v8.js +0 -95
- package/node_modules/@comis/web/dist/assets/ic-tag-CPPUnDLF.js +0 -33
- package/node_modules/@comis/web/dist/assets/index-CEcM1R_C.js +0 -2830
- package/node_modules/@comis/web/dist/assets/index-CIJFuItj.css +0 -1
- package/node_modules/@comis/web/dist/assets/observability-types-D7jUtSde.js +0 -1
- package/node_modules/@comis/web/dist/assets/pipeline-builder-DcUUIrm0.js +0 -1496
- package/node_modules/@comis/web/dist/assets/session-key-parser-DPORMVyU.js +0 -1
- package/node_modules/@comis/web/dist/assets/session-list-6ybUTxbY.js +0 -231
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
import { PerAgentConfigSchema } from "@comis/core";
|
|
17
17
|
import { resolveWorkspaceDir, resolveOperationModel, resolveProviderFamily, OPERATION_TIER_MAP, DEFAULT_PROVIDER_KEYS, } from "@comis/agent";
|
|
18
18
|
import { persistToConfig } from "./persist-to-config.js";
|
|
19
|
+
import { writeInlineWorkspaceFiles, } from "./agent-inline-workspace.js";
|
|
20
|
+
import { probeProviderAuth } from "./probe-provider-auth.js";
|
|
19
21
|
// ---------------------------------------------------------------------------
|
|
20
22
|
// Factory
|
|
21
23
|
// ---------------------------------------------------------------------------
|
|
@@ -36,6 +38,16 @@ export function createAgentHandlers(deps) {
|
|
|
36
38
|
if (deps.agents[agentId] !== undefined) {
|
|
37
39
|
throw new Error(`Agent already exists: ${agentId}`);
|
|
38
40
|
}
|
|
41
|
+
// 260428-vyf L2: extract inlineContent BEFORE config processing.
|
|
42
|
+
// role/identity are write-once side-effects (ROLE.md / IDENTITY.md
|
|
43
|
+
// file writes), NOT durable state — they NEVER enter the persisted
|
|
44
|
+
// config patch. The L1 tool boundary is responsible for stripping
|
|
45
|
+
// them from `config.workspace` before this RPC is called; this
|
|
46
|
+
// handler only consumes the dedicated top-level `inlineContent`
|
|
47
|
+
// field. If a (mis)caller leaves them inside config.workspace, the
|
|
48
|
+
// downstream Zod strict-object will reject them — that's an
|
|
49
|
+
// explicit failure mode, not a silent drop.
|
|
50
|
+
const inlineContent = params.inlineContent ?? undefined;
|
|
39
51
|
const config = params.config ?? {};
|
|
40
52
|
// Strip workspacePath so new agents always get the auto-computed
|
|
41
53
|
// isolated workspace (~/.comis/workspace-{agentId}) instead of
|
|
@@ -83,7 +95,46 @@ export function createAgentHandlers(deps) {
|
|
|
83
95
|
errorKind: "internal" }, "Agent hot-add failed");
|
|
84
96
|
}
|
|
85
97
|
}
|
|
86
|
-
|
|
98
|
+
const workspaceDir = resolveWorkspaceDir(parsedConfig, agentId);
|
|
99
|
+
// 260428-vyf L2: best-effort inline ROLE.md / IDENTITY.md write.
|
|
100
|
+
// Only invoke when inlineContent has at least one populated field
|
|
101
|
+
// AND the persistDeps logger is available (the helper requires a
|
|
102
|
+
// structured logger; the in-memory-only test path skips it).
|
|
103
|
+
let inlineWritesResult;
|
|
104
|
+
if (deps.persistDeps?.logger
|
|
105
|
+
&& inlineContent
|
|
106
|
+
&& (inlineContent.role !== undefined || inlineContent.identity !== undefined)) {
|
|
107
|
+
const writeResult = await writeInlineWorkspaceFiles({ logger: deps.persistDeps.logger }, { workspaceDir, agentId, role: inlineContent.role, identity: inlineContent.identity });
|
|
108
|
+
if (writeResult.ok) {
|
|
109
|
+
inlineWritesResult = writeResult.value;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
// Best-effort: don't fail the create. The helper has already
|
|
113
|
+
// emitted a structured WARN for io / path_traversal. For the
|
|
114
|
+
// oversize branch the helper does NOT log (the schema layer
|
|
115
|
+
// is the canonical gate) — emit a defensive WARN here so the
|
|
116
|
+
// daemon-side surface is not silent.
|
|
117
|
+
if (writeResult.error.kind === "oversize") {
|
|
118
|
+
deps.persistDeps.logger.warn({
|
|
119
|
+
method: "agents.create",
|
|
120
|
+
agentId,
|
|
121
|
+
file: writeResult.error.file,
|
|
122
|
+
limit: writeResult.error.limit,
|
|
123
|
+
actual: writeResult.error.actual,
|
|
124
|
+
hint: "Inline content exceeded size limit at helper layer (schema should have caught this); agent exists with template files.",
|
|
125
|
+
errorKind: "validation",
|
|
126
|
+
}, "Inline workspace content oversize");
|
|
127
|
+
}
|
|
128
|
+
inlineWritesResult = { ok: false, error: writeResult.error };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
agentId,
|
|
133
|
+
config: parsedConfig,
|
|
134
|
+
created: true,
|
|
135
|
+
workspaceDir,
|
|
136
|
+
...(inlineWritesResult !== undefined ? { inlineWritesResult } : {}),
|
|
137
|
+
};
|
|
87
138
|
},
|
|
88
139
|
"agents.get": async (params) => {
|
|
89
140
|
const agentId = params.agentId;
|
|
@@ -142,8 +193,36 @@ export function createAgentHandlers(deps) {
|
|
|
142
193
|
: existing.scheduler.heartbeat,
|
|
143
194
|
};
|
|
144
195
|
}
|
|
196
|
+
// Preserve scalar fields on partial modelFailover updates. fallbackModels,
|
|
197
|
+
// authProfiles, and allowedModels are arrays -- they are replaced wholesale
|
|
198
|
+
// by the spread (no element-wise merge), which matches the documented
|
|
199
|
+
// "user provides the complete desired list" semantic. Scalar fields
|
|
200
|
+
// (cooldownInitialMs, cooldownMultiplier, cooldownCapMs, maxAttempts) are
|
|
201
|
+
// preserved when omitted from the patch.
|
|
202
|
+
if (config.modelFailover && existing.modelFailover) {
|
|
203
|
+
config.modelFailover = {
|
|
204
|
+
...existing.modelFailover,
|
|
205
|
+
...config.modelFailover,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
145
208
|
const merged = { ...existing, ...config };
|
|
146
209
|
const parsedConfig = PerAgentConfigSchema.parse(merged);
|
|
210
|
+
// Probe provider API key when provider or model changes
|
|
211
|
+
const providerChanging = config.provider !== undefined && config.provider !== existing.provider;
|
|
212
|
+
const modelChanging = config.model !== undefined && config.model !== existing.model;
|
|
213
|
+
if ((providerChanging || modelChanging) && deps.providerEntries) {
|
|
214
|
+
const targetProvider = parsedConfig.provider;
|
|
215
|
+
const providerEntry = deps.providerEntries[targetProvider];
|
|
216
|
+
if (providerEntry?.apiKeyName && deps.secretManager) {
|
|
217
|
+
const apiKey = deps.secretManager.get(providerEntry.apiKeyName);
|
|
218
|
+
if (apiKey) {
|
|
219
|
+
const probeResult = await probeProviderAuth(providerEntry.baseUrl, apiKey, parsedConfig.model);
|
|
220
|
+
if (!probeResult.ok) {
|
|
221
|
+
throw new Error(`Cannot switch agent "${agentId}" to provider "${targetProvider}": ${probeResult.error}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
147
226
|
deps.agents[agentId] = parsedConfig;
|
|
148
227
|
// Best-effort persistence to config.yaml
|
|
149
228
|
if (deps.persistDeps) {
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Best-effort writer for inline ROLE.md / IDENTITY.md content supplied via
|
|
3
|
+
* the L2 single-call agents.create path (260428-vyf).
|
|
4
|
+
*
|
|
5
|
+
* Collapses the previous 3-call agent-creation workflow
|
|
6
|
+
* (`agents_manage.create` -> `write(ROLE.md)` -> `write(IDENTITY.md)`)
|
|
7
|
+
* into a single RPC by writing both files atomically as a side-effect of
|
|
8
|
+
* the `agents.create` RPC. role/identity are write-once side-effects, NOT
|
|
9
|
+
* durable state — they never enter the persisted config.yaml.
|
|
10
|
+
*
|
|
11
|
+
* Defense-in-depth posture:
|
|
12
|
+
* - Size limits enforced both at the TypeBox tool-param boundary
|
|
13
|
+
* (maxLength 16384/4096) AND here, so a caller bypassing the tool layer
|
|
14
|
+
* cannot push oversize content past the daemon.
|
|
15
|
+
* - safePath(workspaceDir, "ROLE.md"|"IDENTITY.md") guards against any
|
|
16
|
+
* workspaceDir whose computation could be poisoned upstream. The
|
|
17
|
+
* filename is HARDCODED, not derived from input — defense in depth.
|
|
18
|
+
* - Returns Result<T,E> on every code path (no thrown exceptions).
|
|
19
|
+
*
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
import { type Result } from "@comis/shared";
|
|
23
|
+
import type { ComisLogger } from "@comis/infra";
|
|
24
|
+
export interface AgentInlineWorkspaceParams {
|
|
25
|
+
/** Already-resolved workspace directory (via resolveWorkspaceDir). */
|
|
26
|
+
workspaceDir: string;
|
|
27
|
+
/** Agent identifier — used for structured logging. */
|
|
28
|
+
agentId: string;
|
|
29
|
+
/** Optional inline ROLE.md content. */
|
|
30
|
+
role?: string;
|
|
31
|
+
/** Optional inline IDENTITY.md content. */
|
|
32
|
+
identity?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface AgentInlineWorkspaceResult {
|
|
35
|
+
roleWritten: boolean;
|
|
36
|
+
identityWritten: boolean;
|
|
37
|
+
bytesWritten: number;
|
|
38
|
+
}
|
|
39
|
+
export type AgentInlineWorkspaceError = {
|
|
40
|
+
kind: "oversize";
|
|
41
|
+
file: "ROLE.md" | "IDENTITY.md";
|
|
42
|
+
limit: number;
|
|
43
|
+
actual: number;
|
|
44
|
+
} | {
|
|
45
|
+
kind: "path_traversal";
|
|
46
|
+
file: "ROLE.md" | "IDENTITY.md";
|
|
47
|
+
message: string;
|
|
48
|
+
} | {
|
|
49
|
+
kind: "io";
|
|
50
|
+
file: "ROLE.md" | "IDENTITY.md";
|
|
51
|
+
message: string;
|
|
52
|
+
};
|
|
53
|
+
export interface AgentInlineWorkspaceDeps {
|
|
54
|
+
logger: ComisLogger;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Write inline ROLE.md / IDENTITY.md content to the agent's workspace
|
|
58
|
+
* directory. Best-effort — the caller (`agents.create`) will surface a
|
|
59
|
+
* Result-shaped outcome on the RPC return so the L1 tool can build the
|
|
60
|
+
* appropriate next-step contract for the LLM.
|
|
61
|
+
*
|
|
62
|
+
* @param deps - Required structured logger.
|
|
63
|
+
* @param params - workspaceDir + agentId + optional role/identity content.
|
|
64
|
+
* @returns Result with per-file written flags + total byte count, or one
|
|
65
|
+
* of three structured error kinds (oversize | path_traversal | io).
|
|
66
|
+
*/
|
|
67
|
+
export declare function writeInlineWorkspaceFiles(deps: AgentInlineWorkspaceDeps, params: AgentInlineWorkspaceParams): Promise<Result<AgentInlineWorkspaceResult, AgentInlineWorkspaceError>>;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Best-effort writer for inline ROLE.md / IDENTITY.md content supplied via
|
|
4
|
+
* the L2 single-call agents.create path (260428-vyf).
|
|
5
|
+
*
|
|
6
|
+
* Collapses the previous 3-call agent-creation workflow
|
|
7
|
+
* (`agents_manage.create` -> `write(ROLE.md)` -> `write(IDENTITY.md)`)
|
|
8
|
+
* into a single RPC by writing both files atomically as a side-effect of
|
|
9
|
+
* the `agents.create` RPC. role/identity are write-once side-effects, NOT
|
|
10
|
+
* durable state — they never enter the persisted config.yaml.
|
|
11
|
+
*
|
|
12
|
+
* Defense-in-depth posture:
|
|
13
|
+
* - Size limits enforced both at the TypeBox tool-param boundary
|
|
14
|
+
* (maxLength 16384/4096) AND here, so a caller bypassing the tool layer
|
|
15
|
+
* cannot push oversize content past the daemon.
|
|
16
|
+
* - safePath(workspaceDir, "ROLE.md"|"IDENTITY.md") guards against any
|
|
17
|
+
* workspaceDir whose computation could be poisoned upstream. The
|
|
18
|
+
* filename is HARDCODED, not derived from input — defense in depth.
|
|
19
|
+
* - Returns Result<T,E> on every code path (no thrown exceptions).
|
|
20
|
+
*
|
|
21
|
+
* @module
|
|
22
|
+
*/
|
|
23
|
+
import { writeFile } from "node:fs/promises";
|
|
24
|
+
import { safePath, PathTraversalError } from "@comis/core";
|
|
25
|
+
import { ok, err } from "@comis/shared";
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Constants
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
/** Max ROLE.md inline content size (chars). Mirrored at the TypeBox layer. */
|
|
30
|
+
const ROLE_MAX = 16384;
|
|
31
|
+
/** Max IDENTITY.md inline content size (chars). Mirrored at the TypeBox layer. */
|
|
32
|
+
const IDENTITY_MAX = 4096;
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// Helpers
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
/**
|
|
37
|
+
* Resolve target path via safePath, catching PathTraversalError.
|
|
38
|
+
* Returns Result so the caller can branch without try/catch on its own.
|
|
39
|
+
*/
|
|
40
|
+
function resolveTarget(workspaceDir, filename) {
|
|
41
|
+
try {
|
|
42
|
+
return ok(safePath(workspaceDir, filename));
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
if (e instanceof PathTraversalError) {
|
|
46
|
+
return err({ kind: "path_traversal", file: filename, message: e.message });
|
|
47
|
+
}
|
|
48
|
+
// Unexpected non-traversal error from safePath — treat as IO surface;
|
|
49
|
+
// the caller will WARN-log and the create RPC will still succeed.
|
|
50
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
51
|
+
return err({ kind: "io", file: filename, message });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Attempt a single file write. fs.writeFile rejection -> structured WARN +
|
|
56
|
+
* `{kind:"io"}` Result. Never throws.
|
|
57
|
+
*/
|
|
58
|
+
async function attemptWrite(deps, agentId, filename, targetPath, content) {
|
|
59
|
+
try {
|
|
60
|
+
await writeFile(targetPath, content, { encoding: "utf8" });
|
|
61
|
+
return ok(undefined);
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
65
|
+
deps.logger.warn({
|
|
66
|
+
module: "daemon.rpc.agent-handlers",
|
|
67
|
+
agentId,
|
|
68
|
+
file: filename,
|
|
69
|
+
err: e,
|
|
70
|
+
hint: "Inline ROLE.md/IDENTITY.md write failed; agent exists with template files. User can call write() to customize.",
|
|
71
|
+
errorKind: "resource",
|
|
72
|
+
}, "Inline workspace file write failed");
|
|
73
|
+
return err({ kind: "io", file: filename, message });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// Public API
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
/**
|
|
80
|
+
* Write inline ROLE.md / IDENTITY.md content to the agent's workspace
|
|
81
|
+
* directory. Best-effort — the caller (`agents.create`) will surface a
|
|
82
|
+
* Result-shaped outcome on the RPC return so the L1 tool can build the
|
|
83
|
+
* appropriate next-step contract for the LLM.
|
|
84
|
+
*
|
|
85
|
+
* @param deps - Required structured logger.
|
|
86
|
+
* @param params - workspaceDir + agentId + optional role/identity content.
|
|
87
|
+
* @returns Result with per-file written flags + total byte count, or one
|
|
88
|
+
* of three structured error kinds (oversize | path_traversal | io).
|
|
89
|
+
*/
|
|
90
|
+
export async function writeInlineWorkspaceFiles(deps, params) {
|
|
91
|
+
// Belt-and-braces size limits. Schema layer already enforces these, but
|
|
92
|
+
// a caller that bypasses the tool (e.g. direct RPC) must still be gated.
|
|
93
|
+
if (params.role !== undefined && params.role.length > ROLE_MAX) {
|
|
94
|
+
return err({ kind: "oversize", file: "ROLE.md", limit: ROLE_MAX, actual: params.role.length });
|
|
95
|
+
}
|
|
96
|
+
if (params.identity !== undefined && params.identity.length > IDENTITY_MAX) {
|
|
97
|
+
return err({
|
|
98
|
+
kind: "oversize",
|
|
99
|
+
file: "IDENTITY.md",
|
|
100
|
+
limit: IDENTITY_MAX,
|
|
101
|
+
actual: params.identity.length,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
let roleWritten = false;
|
|
105
|
+
let identityWritten = false;
|
|
106
|
+
let bytesWritten = 0;
|
|
107
|
+
if (params.role !== undefined) {
|
|
108
|
+
const targetResult = resolveTarget(params.workspaceDir, "ROLE.md");
|
|
109
|
+
if (!targetResult.ok)
|
|
110
|
+
return targetResult;
|
|
111
|
+
const writeResult = await attemptWrite(deps, params.agentId, "ROLE.md", targetResult.value, params.role);
|
|
112
|
+
if (!writeResult.ok)
|
|
113
|
+
return writeResult;
|
|
114
|
+
roleWritten = true;
|
|
115
|
+
bytesWritten += params.role.length;
|
|
116
|
+
}
|
|
117
|
+
if (params.identity !== undefined) {
|
|
118
|
+
const targetResult = resolveTarget(params.workspaceDir, "IDENTITY.md");
|
|
119
|
+
if (!targetResult.ok)
|
|
120
|
+
return targetResult;
|
|
121
|
+
const writeResult = await attemptWrite(deps, params.agentId, "IDENTITY.md", targetResult.value, params.identity);
|
|
122
|
+
if (!writeResult.ok)
|
|
123
|
+
return writeResult;
|
|
124
|
+
identityWritten = true;
|
|
125
|
+
bytesWritten += params.identity.length;
|
|
126
|
+
}
|
|
127
|
+
// Emit canonical INFO log only when at least one file was written.
|
|
128
|
+
// Pure no-op invocations (both fields absent) stay silent.
|
|
129
|
+
if (roleWritten || identityWritten) {
|
|
130
|
+
deps.logger.info({
|
|
131
|
+
module: "daemon.rpc.agent-handlers",
|
|
132
|
+
agentId: params.agentId,
|
|
133
|
+
roleBytes: roleWritten ? params.role.length : 0,
|
|
134
|
+
identityBytes: identityWritten ? params.identity.length : 0,
|
|
135
|
+
hint: "customized inline workspace ROLE.md+IDENTITY.md skips the post-create write() roundtrip",
|
|
136
|
+
}, "Wrote inline workspace files (ROLE.md + IDENTITY.md) on agents.create");
|
|
137
|
+
}
|
|
138
|
+
return ok({ roleWritten, identityWritten, bytesWritten });
|
|
139
|
+
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
9
|
import type { ModelCatalog } from "@comis/agent";
|
|
10
|
+
import type { ProviderEntry } from "@comis/core";
|
|
10
11
|
import type { RpcHandler } from "./types.js";
|
|
11
12
|
/** Dependencies required by model management RPC handlers. */
|
|
12
13
|
export interface ModelHandlerDeps {
|
|
@@ -17,6 +18,8 @@ export interface ModelHandlerDeps {
|
|
|
17
18
|
provider: string;
|
|
18
19
|
model: string;
|
|
19
20
|
}>;
|
|
21
|
+
/** Custom provider entries from config.yaml for fallback when catalog is empty. */
|
|
22
|
+
providerEntries?: Record<string, ProviderEntry>;
|
|
20
23
|
}
|
|
21
24
|
/**
|
|
22
25
|
* Create a record of model management RPC handlers bound to the given deps.
|
|
@@ -72,14 +72,15 @@ export function createModelHandlers(deps) {
|
|
|
72
72
|
// Find all agents that use this provider
|
|
73
73
|
const matchingAgents = Object.entries(deps.agents).filter(([, a]) => a.provider === provider);
|
|
74
74
|
if (matchingAgents.length === 0) {
|
|
75
|
-
// Include catalog info so the LLM knows models exist even when
|
|
76
|
-
// no agent is wired to this provider yet.
|
|
77
75
|
const modelsInCatalog = deps.modelCatalog.getByProvider(provider);
|
|
76
|
+
const providerEntry = deps.providerEntries?.[provider];
|
|
77
|
+
const customModelCount = providerEntry?.enabled ? providerEntry.models.length : 0;
|
|
78
78
|
return {
|
|
79
79
|
provider,
|
|
80
80
|
status: "not_configured",
|
|
81
81
|
message: "No agents use this provider",
|
|
82
82
|
modelsInCatalog: modelsInCatalog.length,
|
|
83
|
+
...(customModelCount > 0 ? { customModels: customModelCount } : {}),
|
|
83
84
|
hint: "To switch to this provider, use agents_manage with action 'update' " +
|
|
84
85
|
"to set the agent's provider and model. " +
|
|
85
86
|
"Example: agents_manage({ action: 'update', agent_id: '<id>', " +
|
|
@@ -88,11 +89,34 @@ export function createModelHandlers(deps) {
|
|
|
88
89
|
}
|
|
89
90
|
// Check catalog availability for this provider
|
|
90
91
|
const modelsInCatalog = deps.modelCatalog.getByProvider(provider);
|
|
92
|
+
if (modelsInCatalog.length > 0) {
|
|
93
|
+
return {
|
|
94
|
+
provider,
|
|
95
|
+
status: "available",
|
|
96
|
+
modelsAvailable: modelsInCatalog.length,
|
|
97
|
+
validatedModels: modelsInCatalog.filter((m) => m.validated).length,
|
|
98
|
+
agentsUsing: matchingAgents.map(([id, a]) => ({ agentId: id, model: a.model })),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
// Catalog empty — check custom provider entries (custom providers
|
|
102
|
+
// register with the pi ModelRegistry for routing but are not in the
|
|
103
|
+
// static ModelCatalog used by this management tool).
|
|
104
|
+
const providerEntry = deps.providerEntries?.[provider];
|
|
105
|
+
if (providerEntry && providerEntry.enabled && providerEntry.models.length > 0) {
|
|
106
|
+
return {
|
|
107
|
+
provider,
|
|
108
|
+
status: "available",
|
|
109
|
+
source: "custom_provider",
|
|
110
|
+
modelsAvailable: providerEntry.models.length,
|
|
111
|
+
models: providerEntry.models.map((m) => m.id),
|
|
112
|
+
agentsUsing: matchingAgents.map(([id, a]) => ({ agentId: id, model: a.model })),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
91
115
|
return {
|
|
92
116
|
provider,
|
|
93
|
-
status:
|
|
94
|
-
modelsAvailable:
|
|
95
|
-
validatedModels:
|
|
117
|
+
status: "no_models",
|
|
118
|
+
modelsAvailable: 0,
|
|
119
|
+
validatedModels: 0,
|
|
96
120
|
agentsUsing: matchingAgents.map(([id, a]) => ({ agentId: id, model: a.model })),
|
|
97
121
|
};
|
|
98
122
|
},
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight HTTP probe to validate a provider API key before committing
|
|
3
|
+
* the provider config to config.yaml.
|
|
4
|
+
*
|
|
5
|
+
* Uses a minimal chat/completions request (max_tokens: 1) rather than
|
|
6
|
+
* GET /models because some providers (nvidia NIM) return 200 on /models
|
|
7
|
+
* but 403 on the completions endpoint — the only reliable auth check is
|
|
8
|
+
* hitting the endpoint that will actually be called.
|
|
9
|
+
*
|
|
10
|
+
* Design:
|
|
11
|
+
* - Only 401/403 are treated as definitive auth failures (block config commit).
|
|
12
|
+
* - 5xx, 4xx (other than auth), network errors, and timeouts return ok()
|
|
13
|
+
* so they do NOT block config changes.
|
|
14
|
+
* - Empty baseUrl or apiKey short-circuits to ok() (nothing to probe).
|
|
15
|
+
* - Uses AbortSignal.timeout to cap probe duration (default 5 s).
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
import { type Result } from "@comis/shared";
|
|
20
|
+
/**
|
|
21
|
+
* Probe a provider endpoint to verify the API key is accepted.
|
|
22
|
+
*
|
|
23
|
+
* @param baseUrl - Provider API base URL (e.g. "https://api.openai.com/v1")
|
|
24
|
+
* @param apiKey - The secret API key value
|
|
25
|
+
* @param model - Model ID to use in the probe request (e.g. "gpt-4o")
|
|
26
|
+
* @param timeoutMs - Maximum probe duration in milliseconds (default 5000)
|
|
27
|
+
* @returns ok(undefined) if the key appears valid or the check is inconclusive;
|
|
28
|
+
* err(string) if the provider explicitly rejected the key (401/403).
|
|
29
|
+
*/
|
|
30
|
+
export declare function probeProviderAuth(baseUrl: string, apiKey: string, model?: string, timeoutMs?: number): Promise<Result<void, string>>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Lightweight HTTP probe to validate a provider API key before committing
|
|
4
|
+
* the provider config to config.yaml.
|
|
5
|
+
*
|
|
6
|
+
* Uses a minimal chat/completions request (max_tokens: 1) rather than
|
|
7
|
+
* GET /models because some providers (nvidia NIM) return 200 on /models
|
|
8
|
+
* but 403 on the completions endpoint — the only reliable auth check is
|
|
9
|
+
* hitting the endpoint that will actually be called.
|
|
10
|
+
*
|
|
11
|
+
* Design:
|
|
12
|
+
* - Only 401/403 are treated as definitive auth failures (block config commit).
|
|
13
|
+
* - 5xx, 4xx (other than auth), network errors, and timeouts return ok()
|
|
14
|
+
* so they do NOT block config changes.
|
|
15
|
+
* - Empty baseUrl or apiKey short-circuits to ok() (nothing to probe).
|
|
16
|
+
* - Uses AbortSignal.timeout to cap probe duration (default 5 s).
|
|
17
|
+
*
|
|
18
|
+
* @module
|
|
19
|
+
*/
|
|
20
|
+
import { ok, err } from "@comis/shared";
|
|
21
|
+
const DEFAULT_TIMEOUT_MS = 5_000;
|
|
22
|
+
/**
|
|
23
|
+
* Probe a provider endpoint to verify the API key is accepted.
|
|
24
|
+
*
|
|
25
|
+
* @param baseUrl - Provider API base URL (e.g. "https://api.openai.com/v1")
|
|
26
|
+
* @param apiKey - The secret API key value
|
|
27
|
+
* @param model - Model ID to use in the probe request (e.g. "gpt-4o")
|
|
28
|
+
* @param timeoutMs - Maximum probe duration in milliseconds (default 5000)
|
|
29
|
+
* @returns ok(undefined) if the key appears valid or the check is inconclusive;
|
|
30
|
+
* err(string) if the provider explicitly rejected the key (401/403).
|
|
31
|
+
*/
|
|
32
|
+
export async function probeProviderAuth(baseUrl, apiKey, model, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
33
|
+
if (!baseUrl || !apiKey)
|
|
34
|
+
return ok(undefined);
|
|
35
|
+
let response;
|
|
36
|
+
try {
|
|
37
|
+
response = await fetch(`${baseUrl}/chat/completions`, {
|
|
38
|
+
method: "POST",
|
|
39
|
+
headers: {
|
|
40
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
41
|
+
"Content-Type": "application/json",
|
|
42
|
+
},
|
|
43
|
+
body: JSON.stringify({
|
|
44
|
+
model: model ?? "test",
|
|
45
|
+
messages: [{ role: "user", content: "hi" }],
|
|
46
|
+
max_tokens: 1,
|
|
47
|
+
}),
|
|
48
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return ok(undefined);
|
|
53
|
+
}
|
|
54
|
+
if (response.status === 401 || response.status === 403) {
|
|
55
|
+
return err(`API key rejected by provider (HTTP ${response.status}). ` +
|
|
56
|
+
`Verify the key is correct and has not expired.`);
|
|
57
|
+
}
|
|
58
|
+
return ok(undefined);
|
|
59
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider management RPC handler module.
|
|
3
|
+
* Provides 7 handlers for runtime LLM provider management:
|
|
4
|
+
* providers.list - List all providers with summary and apiKeyConfigured state
|
|
5
|
+
* providers.get - Retrieve full provider config plus agentsUsing list
|
|
6
|
+
* providers.create - Register a new provider entry with validation
|
|
7
|
+
* providers.update - Patch an existing provider config with merge semantics
|
|
8
|
+
* providers.delete - Remove a provider (blocked if agents reference it)
|
|
9
|
+
* providers.enable - Set enabled:true on a disabled provider
|
|
10
|
+
* providers.disable - Set enabled:false (warns but does not block on references)
|
|
11
|
+
*
|
|
12
|
+
* Follows the same factory pattern as agent-handlers.ts. Each handler validates
|
|
13
|
+
* input, operates on the runtime providerEntries map, and returns structured results.
|
|
14
|
+
* API key values are NEVER exposed -- only apiKeyName references and apiKeyConfigured state.
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
import type { ProviderEntry, PerAgentConfig } from "@comis/core";
|
|
18
|
+
import { type PersistToConfigDeps } from "./persist-to-config.js";
|
|
19
|
+
import type { RpcHandler } from "./types.js";
|
|
20
|
+
/** Dependencies required by provider management RPC handlers. */
|
|
21
|
+
export interface ProviderHandlerDeps {
|
|
22
|
+
/** Live provider entries map (NOT a spread copy -- mutations are same-object visible). */
|
|
23
|
+
providerEntries: Record<string, ProviderEntry>;
|
|
24
|
+
/** Runtime agents map for reference checks. */
|
|
25
|
+
agents: Record<string, PerAgentConfig>;
|
|
26
|
+
/** Optional persistence deps for writing changes to config.yaml. */
|
|
27
|
+
persistDeps?: PersistToConfigDeps;
|
|
28
|
+
/** SecretManager for apiKeyConfigured three-state and probe key retrieval. */
|
|
29
|
+
secretManager?: {
|
|
30
|
+
has(key: string): boolean;
|
|
31
|
+
get(key: string): string | undefined;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Create a record of provider management RPC handlers bound to the given deps.
|
|
36
|
+
*/
|
|
37
|
+
export declare function createProviderHandlers(deps: ProviderHandlerDeps): Record<string, RpcHandler>;
|