comisai 1.0.25 → 1.0.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/node_modules/@comis/agent/dist/bootstrap/sections/tool-descriptions.js +130 -10
  2. package/node_modules/@comis/agent/dist/bootstrap/sections/tooling-sections.d.ts +1 -1
  3. package/node_modules/@comis/agent/dist/bootstrap/sections/tooling-sections.js +9 -2
  4. package/node_modules/@comis/agent/dist/bridge/bridge-metrics.d.ts +8 -0
  5. package/node_modules/@comis/agent/dist/bridge/bridge-metrics.js +2 -0
  6. package/node_modules/@comis/agent/dist/bridge/pi-event-bridge.d.ts +29 -0
  7. package/node_modules/@comis/agent/dist/bridge/pi-event-bridge.js +242 -2
  8. package/node_modules/@comis/agent/dist/bridge/thinking-block-hash-invariant.d.ts +210 -0
  9. package/node_modules/@comis/agent/dist/bridge/thinking-block-hash-invariant.js +566 -0
  10. package/node_modules/@comis/agent/dist/context-engine/context-engine.js +8 -6
  11. package/node_modules/@comis/agent/dist/context-engine/signature-replay-scrubber.d.ts +51 -30
  12. package/node_modules/@comis/agent/dist/context-engine/signature-replay-scrubber.js +109 -36
  13. package/node_modules/@comis/agent/dist/executor/executor-context-engine-setup.js +5 -1
  14. package/node_modules/@comis/agent/dist/executor/executor-post-execution.js +22 -20
  15. package/node_modules/@comis/agent/dist/executor/executor-prompt-runner.d.ts +2 -0
  16. package/node_modules/@comis/agent/dist/executor/executor-prompt-runner.js +111 -15
  17. package/node_modules/@comis/agent/dist/executor/executor-response-filter.d.ts +20 -17
  18. package/node_modules/@comis/agent/dist/executor/executor-response-filter.js +132 -52
  19. package/node_modules/@comis/agent/dist/executor/executor-tool-assembly.js +16 -3
  20. package/node_modules/@comis/agent/dist/executor/model-retry.d.ts +14 -0
  21. package/node_modules/@comis/agent/dist/executor/model-retry.js +72 -1
  22. package/node_modules/@comis/agent/dist/executor/pi-executor.d.ts +3 -0
  23. package/node_modules/@comis/agent/dist/executor/pi-executor.js +68 -9
  24. package/node_modules/@comis/agent/dist/executor/post-batch-continuation.d.ts +82 -0
  25. package/node_modules/@comis/agent/dist/executor/post-batch-continuation.js +200 -0
  26. package/node_modules/@comis/agent/dist/executor/stream-wrappers/request-body-injector.js +1 -9
  27. package/node_modules/@comis/agent/dist/executor/tool-deferral.d.ts +37 -2
  28. package/node_modules/@comis/agent/dist/executor/tool-deferral.js +45 -3
  29. package/node_modules/@comis/agent/dist/executor/tool-parallelism.js +0 -1
  30. package/node_modules/@comis/agent/dist/executor/types.d.ts +11 -2
  31. package/node_modules/@comis/agent/dist/index.d.ts +3 -1
  32. package/node_modules/@comis/agent/dist/index.js +2 -0
  33. package/node_modules/@comis/agent/dist/model/last-known-model.d.ts +36 -0
  34. package/node_modules/@comis/agent/dist/model/last-known-model.js +49 -0
  35. package/node_modules/@comis/agent/dist/model/model-registry-adapter.d.ts +16 -4
  36. package/node_modules/@comis/agent/dist/model/model-registry-adapter.js +65 -21
  37. package/node_modules/@comis/agent/dist/planner/types.d.ts +0 -2
  38. package/node_modules/@comis/agent/dist/session/comis-session-manager.d.ts +10 -0
  39. package/node_modules/@comis/agent/dist/session/comis-session-manager.js +5 -0
  40. package/node_modules/@comis/agent/dist/spawn/pi-mono-adapters.js +7 -0
  41. package/node_modules/@comis/agent/package.json +1 -1
  42. package/node_modules/@comis/channels/package.json +1 -1
  43. package/node_modules/@comis/cli/dist/client/rpc-client.js +6 -1
  44. package/node_modules/@comis/cli/dist/commands/doctor.js +5 -3
  45. package/node_modules/@comis/cli/dist/commands/health.js +5 -2
  46. package/node_modules/@comis/cli/dist/wizard/json-output.js +7 -3
  47. package/node_modules/@comis/cli/dist/wizard/steps/11-daemon-start.js +130 -0
  48. package/node_modules/@comis/cli/package.json +1 -1
  49. package/node_modules/@comis/core/dist/config/immutable-keys.d.ts +2 -2
  50. package/node_modules/@comis/core/dist/config/immutable-keys.js +8 -3
  51. package/node_modules/@comis/core/dist/config/managed-sections.d.ts +43 -4
  52. package/node_modules/@comis/core/dist/config/managed-sections.js +100 -6
  53. package/node_modules/@comis/core/dist/config/schema-agent.d.ts +39 -0
  54. package/node_modules/@comis/core/dist/config/schema-agent.js +14 -0
  55. package/node_modules/@comis/core/dist/config/schema.d.ts +4 -0
  56. package/node_modules/@comis/core/dist/config/schema.js +14 -0
  57. package/node_modules/@comis/core/dist/domain/execution-graph.d.ts +1 -1
  58. package/node_modules/@comis/core/dist/event-bus/events-agent.d.ts +17 -2
  59. package/node_modules/@comis/core/dist/exports/config.d.ts +2 -2
  60. package/node_modules/@comis/core/dist/exports/config.js +1 -1
  61. package/node_modules/@comis/core/package.json +1 -1
  62. package/node_modules/@comis/daemon/dist/daemon.d.ts +22 -0
  63. package/node_modules/@comis/daemon/dist/daemon.js +42 -0
  64. package/node_modules/@comis/daemon/dist/rpc/agent-handlers.d.ts +5 -2
  65. package/node_modules/@comis/daemon/dist/rpc/agent-handlers.js +80 -1
  66. package/node_modules/@comis/daemon/dist/rpc/agent-inline-workspace.d.ts +67 -0
  67. package/node_modules/@comis/daemon/dist/rpc/agent-inline-workspace.js +139 -0
  68. package/node_modules/@comis/daemon/dist/rpc/model-handlers.d.ts +3 -0
  69. package/node_modules/@comis/daemon/dist/rpc/model-handlers.js +29 -5
  70. package/node_modules/@comis/daemon/dist/rpc/probe-provider-auth.d.ts +30 -0
  71. package/node_modules/@comis/daemon/dist/rpc/probe-provider-auth.js +59 -0
  72. package/node_modules/@comis/daemon/dist/rpc/provider-handlers.d.ts +37 -0
  73. package/node_modules/@comis/daemon/dist/rpc/provider-handlers.js +330 -0
  74. package/node_modules/@comis/daemon/dist/rpc/rpc-dispatch.js +18 -1
  75. package/node_modules/@comis/daemon/dist/setup-docker-restart-warn.d.ts +4 -0
  76. package/node_modules/@comis/daemon/dist/setup-docker-restart-warn.js +30 -0
  77. package/node_modules/@comis/daemon/dist/wiring/setup-agents.d.ts +3 -1
  78. package/node_modules/@comis/daemon/dist/wiring/setup-agents.js +28 -2
  79. package/node_modules/@comis/daemon/dist/wiring/setup-cross-session.js +1 -0
  80. package/node_modules/@comis/daemon/dist/wiring/setup-tools.js +7 -4
  81. package/node_modules/@comis/daemon/package.json +1 -1
  82. package/node_modules/@comis/gateway/package.json +1 -1
  83. package/node_modules/@comis/infra/dist/index.d.ts +1 -0
  84. package/node_modules/@comis/infra/dist/index.js +2 -0
  85. package/node_modules/@comis/infra/dist/runtime/is-docker.d.ts +1 -0
  86. package/node_modules/@comis/infra/dist/runtime/is-docker.js +25 -0
  87. package/node_modules/@comis/infra/package.json +1 -1
  88. package/node_modules/@comis/memory/package.json +1 -1
  89. package/node_modules/@comis/scheduler/package.json +1 -1
  90. package/node_modules/@comis/shared/package.json +1 -1
  91. package/node_modules/@comis/skills/dist/bridge/tool-metadata-registry.js +1 -3
  92. package/node_modules/@comis/skills/dist/builtin/platform/admin-manage-factory.js +24 -1
  93. package/node_modules/@comis/skills/dist/builtin/platform/agents-manage-tool.d.ts +53 -7
  94. package/node_modules/@comis/skills/dist/builtin/platform/agents-manage-tool.js +218 -24
  95. package/node_modules/@comis/skills/dist/builtin/platform/gateway-tool.d.ts +4 -1
  96. package/node_modules/@comis/skills/dist/builtin/platform/gateway-tool.js +16 -1
  97. package/node_modules/@comis/skills/dist/builtin/platform/index.d.ts +1 -1
  98. package/node_modules/@comis/skills/dist/builtin/platform/index.js +1 -1
  99. package/node_modules/@comis/skills/dist/builtin/platform/providers-manage-tool.d.ts +56 -0
  100. package/node_modules/@comis/skills/dist/builtin/platform/providers-manage-tool.js +203 -0
  101. package/node_modules/@comis/skills/dist/index.d.ts +1 -1
  102. package/node_modules/@comis/skills/dist/index.js +2 -2
  103. package/node_modules/@comis/skills/dist/policy/tool-policy.js +0 -1
  104. package/node_modules/@comis/skills/package.json +1 -1
  105. package/node_modules/@comis/web/dist/assets/{agent-detail-ru-AhppM.js → agent-detail-DqL6Artv.js} +1 -1
  106. package/node_modules/@comis/web/dist/assets/{agent-editor-hjwRuFVp.js → agent-editor-CNM_h94Y.js} +1 -1
  107. package/node_modules/@comis/web/dist/assets/{agent-list-6Uotjatr.js → agent-list-Dbh-xD_F.js} +1 -1
  108. package/node_modules/@comis/web/dist/assets/{billing-view-CxysXH0p.js → billing-view-C1DmtyzK.js} +1 -1
  109. package/node_modules/@comis/web/dist/assets/{channel-detail-BBCKtmne.js → channel-detail-CtCH22N1.js} +1 -1
  110. package/node_modules/@comis/web/dist/assets/{channel-list-FkfeOLBQ.js → channel-list-C7xXn-60.js} +1 -1
  111. package/node_modules/@comis/web/dist/assets/{chat-console-BumBaIgO.js → chat-console-C51pjFwk.js} +1 -1
  112. package/node_modules/@comis/web/dist/assets/{config-editor-C9BSwHGy.js → config-editor-BLArYRB7.js} +1 -1
  113. package/node_modules/@comis/web/dist/assets/{context-dag-browser-BHm00mJD.js → context-dag-browser-fuyMinNI.js} +1 -1
  114. package/node_modules/@comis/web/dist/assets/{context-engine-BENY3pWE.js → context-engine-Bngf2bH0.js} +1 -1
  115. package/node_modules/@comis/web/dist/assets/{delivery-view-BCnkPsAp.js → delivery-view-C80hucxX.js} +1 -1
  116. package/node_modules/@comis/web/dist/assets/{diagnostics-view-C_jQFG2H.js → diagnostics-view-Cl4VbHZ6.js} +1 -1
  117. package/node_modules/@comis/web/dist/assets/{ic-chat-message-FdQcZsSQ.js → ic-chat-message-ByFUoMm6.js} +1 -1
  118. package/node_modules/@comis/web/dist/assets/{ic-connection-dot-BgYiK2N4.js → ic-connection-dot-C4nDHgY2.js} +1 -1
  119. package/node_modules/@comis/web/dist/assets/{ic-tool-call-DMPHsLyx.js → ic-tool-call-Bh5kq-yY.js} +1 -1
  120. package/node_modules/@comis/web/dist/assets/{index-FLPhHz8p.js → index-BBkuC-EU.js} +2 -2
  121. package/node_modules/@comis/web/dist/assets/{mcp-management-5jyScQis.js → mcp-management-DB-phOo7.js} +1 -1
  122. package/node_modules/@comis/web/dist/assets/{media-config-J9oT9PPs.js → media-config-CRqZ1ZUH.js} +1 -1
  123. package/node_modules/@comis/web/dist/assets/{media-test-DGTCtM8-.js → media-test-C9vE20Oy.js} +1 -1
  124. package/node_modules/@comis/web/dist/assets/{memory-inspector-D5Re9ptG.js → memory-inspector-CeqfnxMZ.js} +1 -1
  125. package/node_modules/@comis/web/dist/assets/{message-center-cRLK6ZmG.js → message-center-Daup7Mof.js} +1 -1
  126. package/node_modules/@comis/web/dist/assets/{models-D5vu07MR.js → models-DLYnEU8E.js} +1 -1
  127. package/node_modules/@comis/web/dist/assets/{observe-view-CalNNEmd.js → observe-view-BTSt_PO5.js} +1 -1
  128. package/node_modules/@comis/web/dist/assets/{pipeline-builder-DUYDGwZf.js → pipeline-builder-DknfzyLt.js} +1 -1
  129. package/node_modules/@comis/web/dist/assets/{pipeline-history-BAO8brOe.js → pipeline-history-JnHZdeU_.js} +1 -1
  130. package/node_modules/@comis/web/dist/assets/{pipeline-history-detail-DectIoQt.js → pipeline-history-detail-Dg4knsEb.js} +1 -1
  131. package/node_modules/@comis/web/dist/assets/{pipeline-list-BHlaBKww.js → pipeline-list-AEnibjsp.js} +1 -1
  132. package/node_modules/@comis/web/dist/assets/{pipeline-monitor-BhtpNEHf.js → pipeline-monitor-DG7RbIOO.js} +1 -1
  133. package/node_modules/@comis/web/dist/assets/{scheduler-VafN_8xi.js → scheduler-uL1fYKAT.js} +1 -1
  134. package/node_modules/@comis/web/dist/assets/{security-QQXMRTlo.js → security-C3DywRLH.js} +1 -1
  135. package/node_modules/@comis/web/dist/assets/{session-detail-BpZ_8Yih.js → session-detail-BtqCNWXV.js} +1 -1
  136. package/node_modules/@comis/web/dist/assets/{session-list-DfCm8Cec.js → session-list-CJXWa2XT.js} +1 -1
  137. package/node_modules/@comis/web/dist/assets/{setup-wizard-C-z477CG.js → setup-wizard-ywn7oJvu.js} +1 -1
  138. package/node_modules/@comis/web/dist/assets/{skills-BCOGPf6s.js → skills-DX0KYnWD.js} +1 -1
  139. package/node_modules/@comis/web/dist/assets/{subagents-l-auUraL.js → subagents-B8p5YJEB.js} +1 -1
  140. package/node_modules/@comis/web/dist/assets/{workspace-manager-DlvBixiq.js → workspace-manager-CgzNIrw1.js} +1 -1
  141. package/node_modules/@comis/web/dist/index.html +1 -1
  142. package/node_modules/@comis/web/package.json +1 -1
  143. package/package.json +14 -14
  144. package/node_modules/@comis/skills/dist/builtin/platform/agents-list-tool.d.ts +0 -19
  145. package/node_modules/@comis/skills/dist/builtin/platform/agents-list-tool.js +0 -39
@@ -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: modelsInCatalog.length > 0 ? "available" : "no_models",
94
- modelsAvailable: modelsInCatalog.length,
95
- validatedModels: modelsInCatalog.filter((m) => m.validated).length,
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>;