@mcoda/codali 0.1.108 → 0.1.110
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/dist/agents/AgentInventory.d.ts +56 -0
- package/dist/agents/AgentInventory.d.ts.map +1 -0
- package/dist/agents/AgentInventory.js +131 -0
- package/dist/agents/RoleResolution.d.ts +49 -0
- package/dist/agents/RoleResolution.d.ts.map +1 -0
- package/dist/agents/RoleResolution.js +166 -0
- package/dist/api/ChatCompletionsAdapter.d.ts +76 -0
- package/dist/api/ChatCompletionsAdapter.d.ts.map +1 -0
- package/dist/api/ChatCompletionsAdapter.js +54 -0
- package/dist/api/CodaliApi.d.ts +78 -0
- package/dist/api/CodaliApi.d.ts.map +1 -0
- package/dist/api/CodaliApi.js +273 -0
- package/dist/api/CodaliServer.d.ts +42 -0
- package/dist/api/CodaliServer.d.ts.map +1 -0
- package/dist/api/CodaliServer.js +118 -0
- package/dist/cli/AskCommand.d.ts +66 -0
- package/dist/cli/AskCommand.d.ts.map +1 -0
- package/dist/cli/AskCommand.js +536 -0
- package/dist/cli/AuthCommand.d.ts +19 -0
- package/dist/cli/AuthCommand.d.ts.map +1 -0
- package/dist/cli/AuthCommand.js +161 -0
- package/dist/cli/ServeCommand.d.ts +5 -0
- package/dist/cli/ServeCommand.d.ts.map +1 -0
- package/dist/cli/ServeCommand.js +87 -0
- package/dist/cli/ToolsCommand.d.ts +22 -0
- package/dist/cli/ToolsCommand.d.ts.map +1 -0
- package/dist/cli/ToolsCommand.js +208 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +29 -1
- package/dist/connectors/http/HttpToolDefinition.d.ts +74 -0
- package/dist/connectors/http/HttpToolDefinition.d.ts.map +1 -0
- package/dist/connectors/http/HttpToolDefinition.js +190 -0
- package/dist/connectors/http/HttpToolSource.d.ts +18 -0
- package/dist/connectors/http/HttpToolSource.d.ts.map +1 -0
- package/dist/connectors/http/HttpToolSource.js +29 -0
- package/dist/connectors/mcp/McpClient.d.ts +132 -0
- package/dist/connectors/mcp/McpClient.d.ts.map +1 -0
- package/dist/connectors/mcp/McpClient.js +215 -0
- package/dist/connectors/mcp/McpServerRegistry.d.ts +75 -0
- package/dist/connectors/mcp/McpServerRegistry.d.ts.map +1 -0
- package/dist/connectors/mcp/McpServerRegistry.js +180 -0
- package/dist/connectors/mcp/McpToolAdapter.d.ts +48 -0
- package/dist/connectors/mcp/McpToolAdapter.d.ts.map +1 -0
- package/dist/connectors/mcp/McpToolAdapter.js +101 -0
- package/dist/connectors/mcp/McpToolSource.d.ts +27 -0
- package/dist/connectors/mcp/McpToolSource.d.ts.map +1 -0
- package/dist/connectors/mcp/McpToolSource.js +33 -0
- package/dist/connectors/media/ImageGenerationTool.d.ts +43 -0
- package/dist/connectors/media/ImageGenerationTool.d.ts.map +1 -0
- package/dist/connectors/media/ImageGenerationTool.js +122 -0
- package/dist/connectors/oauth/DeviceCodeAuth.d.ts +74 -0
- package/dist/connectors/oauth/DeviceCodeAuth.d.ts.map +1 -0
- package/dist/connectors/oauth/DeviceCodeAuth.js +153 -0
- package/dist/docdex/DocdexClient.d.ts +2 -0
- package/dist/docdex/DocdexClient.d.ts.map +1 -1
- package/dist/docdex/DocdexClient.js +4 -0
- package/dist/gateway/CodaliGateway.d.ts +51 -3
- package/dist/gateway/CodaliGateway.d.ts.map +1 -1
- package/dist/gateway/CodaliGateway.js +241 -3
- package/dist/gateway/CodaliGatewayTypes.d.ts +34 -0
- package/dist/gateway/CodaliGatewayTypes.d.ts.map +1 -1
- package/dist/gateway/EvidenceNormalizer.d.ts.map +1 -1
- package/dist/gateway/EvidenceNormalizer.js +98 -1
- package/dist/gateway/Finalizer.d.ts +50 -0
- package/dist/gateway/Finalizer.d.ts.map +1 -0
- package/dist/gateway/Finalizer.js +79 -0
- package/dist/gateway/GatewayPlanner.d.ts +13 -0
- package/dist/gateway/GatewayPlanner.d.ts.map +1 -1
- package/dist/gateway/GatewayPlanner.js +81 -16
- package/dist/gateway/GatewayStateMachine.d.ts.map +1 -1
- package/dist/gateway/GatewayStateMachine.js +6 -0
- package/dist/gateway/GatewayTracer.d.ts +93 -0
- package/dist/gateway/GatewayTracer.d.ts.map +1 -0
- package/dist/gateway/GatewayTracer.js +214 -0
- package/dist/gateway/LocalGatewayProvider.d.ts +59 -0
- package/dist/gateway/LocalGatewayProvider.d.ts.map +1 -0
- package/dist/gateway/LocalGatewayProvider.js +195 -0
- package/dist/gateway/LocalGatewayTaskRunner.d.ts +79 -0
- package/dist/gateway/LocalGatewayTaskRunner.d.ts.map +1 -0
- package/dist/gateway/LocalGatewayTaskRunner.js +361 -0
- package/dist/gateway/RelevanceJudge.d.ts +25 -0
- package/dist/gateway/RelevanceJudge.d.ts.map +1 -0
- package/dist/gateway/RelevanceJudge.js +124 -0
- package/dist/gateway/ResponseSchema.d.ts +32 -0
- package/dist/gateway/ResponseSchema.d.ts.map +1 -0
- package/dist/gateway/ResponseSchema.js +120 -0
- package/dist/gateway/TemporalContext.d.ts +36 -0
- package/dist/gateway/TemporalContext.d.ts.map +1 -0
- package/dist/gateway/TemporalContext.js +115 -0
- package/dist/gateway/ToolCapabilityCompiler.d.ts +5 -0
- package/dist/gateway/ToolCapabilityCompiler.d.ts.map +1 -1
- package/dist/gateway/ToolCapabilityCompiler.js +27 -2
- package/dist/gateway/ToolDescriptorSource.d.ts +6 -0
- package/dist/gateway/ToolDescriptorSource.d.ts.map +1 -0
- package/dist/gateway/ToolDescriptorSource.js +30 -0
- package/dist/gateway/ToolExposure.d.ts +36 -0
- package/dist/gateway/ToolExposure.d.ts.map +1 -0
- package/dist/gateway/ToolExposure.js +154 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +45 -0
- package/dist/providers/ClaudeCliProvider.d.ts +2 -0
- package/dist/providers/ClaudeCliProvider.d.ts.map +1 -1
- package/dist/providers/ClaudeCliProvider.js +2 -0
- package/dist/providers/CodexCliProvider.d.ts +2 -0
- package/dist/providers/CodexCliProvider.d.ts.map +1 -1
- package/dist/providers/CodexCliProvider.js +2 -0
- package/dist/providers/MswarmWorkerProvider.d.ts +2 -0
- package/dist/providers/MswarmWorkerProvider.d.ts.map +1 -1
- package/dist/providers/MswarmWorkerProvider.js +2 -0
- package/dist/providers/OllamaRemoteProvider.d.ts +2 -0
- package/dist/providers/OllamaRemoteProvider.d.ts.map +1 -1
- package/dist/providers/OllamaRemoteProvider.js +2 -0
- package/dist/providers/OpenAiCompatibleProvider.d.ts +2 -0
- package/dist/providers/OpenAiCompatibleProvider.d.ts.map +1 -1
- package/dist/providers/OpenAiCompatibleProvider.js +2 -0
- package/dist/providers/ProviderTypes.d.ts +13 -0
- package/dist/providers/ProviderTypes.d.ts.map +1 -1
- package/dist/providers/ProviderTypes.js +1 -1
- package/dist/runcontext/CredentialFile.d.ts +12 -0
- package/dist/runcontext/CredentialFile.d.ts.map +1 -0
- package/dist/runcontext/CredentialFile.js +81 -0
- package/dist/runcontext/RunContextResolver.d.ts +126 -0
- package/dist/runcontext/RunContextResolver.d.ts.map +1 -0
- package/dist/runcontext/RunContextResolver.js +389 -0
- package/dist/storage/CodaliDatasetPrivacyEngine.d.ts +1 -1
- package/dist/storage/CodaliDatasetPrivacyEngine.d.ts.map +1 -1
- package/dist/storage/CodaliDatasetPrivacyEngine.js +12 -0
- package/dist/tools/ToolRegistry.d.ts +14 -1
- package/dist/tools/ToolRegistry.d.ts.map +1 -1
- package/dist/tools/ToolRegistry.js +38 -1
- package/dist/tools/ToolTypes.d.ts +35 -0
- package/dist/tools/ToolTypes.d.ts.map +1 -1
- package/dist/tools/ToolTypes.js +21 -0
- package/dist/tools/TruncateResult.d.ts +29 -0
- package/dist/tools/TruncateResult.d.ts.map +1 -0
- package/dist/tools/TruncateResult.js +91 -0
- package/dist/tools/diff/DiffTool.d.ts.map +1 -1
- package/dist/tools/diff/DiffTool.js +2 -0
- package/dist/tools/docdex/DocdexTools.d.ts +21 -1
- package/dist/tools/docdex/DocdexTools.d.ts.map +1 -1
- package/dist/tools/docdex/DocdexTools.js +151 -17
- package/dist/tools/filesystem/FileTools.d.ts.map +1 -1
- package/dist/tools/filesystem/FileTools.js +8 -0
- package/dist/tools/search/SearchTool.d.ts.map +1 -1
- package/dist/tools/search/SearchTool.js +3 -1
- package/dist/tools/shell/ShellTool.d.ts.map +1 -1
- package/dist/tools/shell/ShellTool.js +2 -0
- package/package.json +4 -3
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { attachHttpTools } from "../connectors/http/HttpToolSource.js";
|
|
3
|
+
import { attachMcpTools } from "../connectors/mcp/McpToolSource.js";
|
|
4
|
+
import { createImageGenerationTool, } from "../connectors/media/ImageGenerationTool.js";
|
|
5
|
+
import { DocdexClient } from "../docdex/DocdexClient.js";
|
|
6
|
+
import { createCodaliGateway } from "../gateway/CodaliGateway.js";
|
|
7
|
+
import { createLocalGatewayTaskRunner } from "../gateway/LocalGatewayTaskRunner.js";
|
|
8
|
+
import { createProviderForAssignment } from "../gateway/LocalGatewayProvider.js";
|
|
9
|
+
import { gatewayToolDescriptorsFromRegistry } from "../gateway/ToolDescriptorSource.js";
|
|
10
|
+
import { GatewayTracer } from "../gateway/GatewayTracer.js";
|
|
11
|
+
import { getAgentInventory, filterLocallyDrivable } from "../agents/AgentInventory.js";
|
|
12
|
+
import { resolveConfigurableRoles } from "../agents/RoleResolution.js";
|
|
13
|
+
import { providerSupportsToolCalls } from "../providers/ProviderTypes.js";
|
|
14
|
+
import { ToolRegistry } from "../tools/ToolRegistry.js";
|
|
15
|
+
import { createDocdexTools } from "../tools/docdex/DocdexTools.js";
|
|
16
|
+
import { LocalConfigRunContextResolver } from "../runcontext/RunContextResolver.js";
|
|
17
|
+
const DEFAULTS = {
|
|
18
|
+
maxRounds: 3,
|
|
19
|
+
maxToolCalls: 20,
|
|
20
|
+
maxModelCalls: 12,
|
|
21
|
+
deadlineMs: 600000,
|
|
22
|
+
maxParallelWorkers: 3,
|
|
23
|
+
};
|
|
24
|
+
const DEFAULT_DOCDEX_TOOLS = [
|
|
25
|
+
"docdex_search",
|
|
26
|
+
"docdex_open",
|
|
27
|
+
"docdex_symbols",
|
|
28
|
+
"docdex_tree",
|
|
29
|
+
"docdex_files",
|
|
30
|
+
"docdex_impact_graph",
|
|
31
|
+
"docdex_batch_search",
|
|
32
|
+
"docdex_web_research",
|
|
33
|
+
"docdex_stats",
|
|
34
|
+
];
|
|
35
|
+
/** Flattens a message list into the single query the gateway plans against. */
|
|
36
|
+
export const messagesToQuery = (messages) => {
|
|
37
|
+
const lastUser = [...messages].reverse().find((message) => message.role === "user");
|
|
38
|
+
if (!lastUser)
|
|
39
|
+
return "";
|
|
40
|
+
const priorContext = messages
|
|
41
|
+
.slice(0, messages.indexOf(lastUser))
|
|
42
|
+
.filter((message) => message.role !== "system")
|
|
43
|
+
.slice(-4)
|
|
44
|
+
.map((message) => `${message.role}: ${message.content}`);
|
|
45
|
+
return priorContext.length > 0
|
|
46
|
+
? `${priorContext.join("\n")}\n\nCurrent question: ${lastUser.content}`
|
|
47
|
+
: lastUser.content;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Runs one Codali request end to end.
|
|
51
|
+
*
|
|
52
|
+
* The same code path the CLI uses, so a product and the terminal cannot drift.
|
|
53
|
+
*/
|
|
54
|
+
export const runCodali = async (request, deps = {}) => {
|
|
55
|
+
const workspaceRoot = request.workspaceRoot ?? process.cwd();
|
|
56
|
+
const query = messagesToQuery(request.messages);
|
|
57
|
+
const tracer = new GatewayTracer(query);
|
|
58
|
+
if (!query.trim()) {
|
|
59
|
+
return {
|
|
60
|
+
status: "failed",
|
|
61
|
+
answer: "The request contained no user message.",
|
|
62
|
+
output: undefined,
|
|
63
|
+
sources: [],
|
|
64
|
+
artifacts: [],
|
|
65
|
+
warnings: ["empty_request"],
|
|
66
|
+
traceId: request.requestId ?? randomUUID(),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// Host-supplied context wins; otherwise fall back to local trusted config.
|
|
70
|
+
const context = request.runContext ??
|
|
71
|
+
(deps.resolveRunContext
|
|
72
|
+
? await deps.resolveRunContext(workspaceRoot)
|
|
73
|
+
: await new LocalConfigRunContextResolver().resolve({ workspaceRoot }));
|
|
74
|
+
for (const warning of context.warnings ?? [])
|
|
75
|
+
tracer.addWarning(warning);
|
|
76
|
+
const inventory = await (deps.loadInventory ?? getAgentInventory)();
|
|
77
|
+
const drivable = filterLocallyDrivable(inventory.agents);
|
|
78
|
+
const roles = resolveConfigurableRoles({
|
|
79
|
+
inventory: drivable,
|
|
80
|
+
bindings: context.agentRoles,
|
|
81
|
+
roles: request.media ? ["orchestrator", "synthesizer", "media"] : undefined,
|
|
82
|
+
});
|
|
83
|
+
for (const warning of roles.warnings)
|
|
84
|
+
tracer.addWarning(warning);
|
|
85
|
+
const orchestrator = roles.assignments.orchestrator;
|
|
86
|
+
const synthesizer = roles.assignments.synthesizer;
|
|
87
|
+
if (!orchestrator && !synthesizer) {
|
|
88
|
+
return {
|
|
89
|
+
status: "failed",
|
|
90
|
+
answer: "No usable model agent could be resolved for this run.",
|
|
91
|
+
output: undefined,
|
|
92
|
+
sources: [],
|
|
93
|
+
artifacts: [],
|
|
94
|
+
warnings: [...tracer.snapshot().warnings, "no_agents_resolved"],
|
|
95
|
+
traceId: request.requestId ?? randomUUID(),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
const makeProvider = deps.createProvider ?? createProviderForAssignment;
|
|
99
|
+
const primary = makeProvider((synthesizer ?? orchestrator));
|
|
100
|
+
const orchestratorProvider = orchestrator ? makeProvider(orchestrator) : undefined;
|
|
101
|
+
const workerCandidates = [roles.toolCapable, orchestrator, synthesizer].filter(Boolean);
|
|
102
|
+
let worker = primary;
|
|
103
|
+
for (const candidate of workerCandidates) {
|
|
104
|
+
const provider = makeProvider(candidate);
|
|
105
|
+
if (providerSupportsToolCalls(provider)) {
|
|
106
|
+
worker = provider;
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const toolsExecutable = providerSupportsToolCalls(worker);
|
|
111
|
+
// ---- Tools ------------------------------------------------------------
|
|
112
|
+
const registry = deps.buildRegistry?.(workspaceRoot, context) ?? buildDefaultRegistry(workspaceRoot, context);
|
|
113
|
+
const mcp = await (deps.attachMcp ?? attachMcpTools)({ context, toolRegistry: registry });
|
|
114
|
+
for (const warning of mcp.warnings)
|
|
115
|
+
tracer.addWarning(warning);
|
|
116
|
+
const http = attachHttpTools({ context, toolRegistry: registry });
|
|
117
|
+
for (const warning of http.warnings)
|
|
118
|
+
tracer.addWarning(warning);
|
|
119
|
+
const artifacts = [];
|
|
120
|
+
const runId = request.requestId ?? `codali-${randomUUID()}`;
|
|
121
|
+
if (request.media) {
|
|
122
|
+
registry.register(createImageGenerationTool({
|
|
123
|
+
config: request.media,
|
|
124
|
+
workspaceRoot,
|
|
125
|
+
runId,
|
|
126
|
+
onArtifact: (artifact) => artifacts.push(artifact),
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
const registered = registry.list().map((tool) => tool.name);
|
|
130
|
+
const readOnlyMcpTools = registry
|
|
131
|
+
.catalog(mcp.registered)
|
|
132
|
+
.filter((descriptor) => descriptor.readOnly)
|
|
133
|
+
.map((descriptor) => descriptor.name);
|
|
134
|
+
const defaultAllowed = [
|
|
135
|
+
...DEFAULT_DOCDEX_TOOLS,
|
|
136
|
+
...readOnlyMcpTools,
|
|
137
|
+
...http.registered,
|
|
138
|
+
...(request.media ? ["media_generate_image"] : []),
|
|
139
|
+
];
|
|
140
|
+
const allowedTools = toolsExecutable
|
|
141
|
+
? (context.allowedTools ?? defaultAllowed).filter((tool) => registered.includes(tool))
|
|
142
|
+
: [];
|
|
143
|
+
// Every allowed tool must be declared to the capability compiler, which
|
|
144
|
+
// otherwise discards it as `not_declared` — including docdex tools outside
|
|
145
|
+
// its hardcoded backing-tool set, such as `docdex_web_research`. That
|
|
146
|
+
// omission kept the "web" capability off the classifier's list entirely, so
|
|
147
|
+
// no question could ever reach the public web.
|
|
148
|
+
const connectorTools = allowedTools;
|
|
149
|
+
// ---- Budgets ----------------------------------------------------------
|
|
150
|
+
const min = (a, b) => (b === undefined ? a : Math.min(a, b));
|
|
151
|
+
const maxRounds = min(request.budgets?.maxRounds ?? DEFAULTS.maxRounds, context.limits?.maxRounds);
|
|
152
|
+
const maxToolCalls = min(request.budgets?.maxToolCalls ?? DEFAULTS.maxToolCalls, context.limits?.maxToolCalls);
|
|
153
|
+
const deadlineMs = min(request.budgets?.deadlineMs ?? DEFAULTS.deadlineMs, context.limits?.deadlineMs);
|
|
154
|
+
const gatewayRequest = {
|
|
155
|
+
id: runId,
|
|
156
|
+
query,
|
|
157
|
+
mode: request.mode ?? "balanced",
|
|
158
|
+
product: request.product ?? { name: "codali-api" },
|
|
159
|
+
tenant: context.tenant,
|
|
160
|
+
tools: connectorTools.length > 0
|
|
161
|
+
? { actualTools: connectorTools, readOnlyTools: connectorTools }
|
|
162
|
+
: undefined,
|
|
163
|
+
docdex: {
|
|
164
|
+
enabled: true,
|
|
165
|
+
baseUrl: context.docdex?.baseUrl,
|
|
166
|
+
repoRoot: context.repo?.root ?? workspaceRoot,
|
|
167
|
+
repoId: context.docdex?.repoId,
|
|
168
|
+
},
|
|
169
|
+
policy: {
|
|
170
|
+
allowedTools,
|
|
171
|
+
deniedTools: context.deniedTools,
|
|
172
|
+
maxIterations: maxRounds,
|
|
173
|
+
maxRuntimeMs: deadlineMs,
|
|
174
|
+
maxToolCalls,
|
|
175
|
+
maxModelCalls: request.budgets?.maxModelCalls ?? DEFAULTS.maxModelCalls,
|
|
176
|
+
maxEvidenceItems: 40,
|
|
177
|
+
maxImageArtifacts: request.media ? 4 : 0,
|
|
178
|
+
maxContextPackTokens: 16000,
|
|
179
|
+
allowWrites: false,
|
|
180
|
+
allowShell: false,
|
|
181
|
+
allowDestructiveOperations: false,
|
|
182
|
+
allowOutsideWorkspace: false,
|
|
183
|
+
requireFinalLargeModel: false,
|
|
184
|
+
allowDegradedFinalAnswer: true,
|
|
185
|
+
allowImageWorker: Boolean(request.media),
|
|
186
|
+
},
|
|
187
|
+
response: {
|
|
188
|
+
format: request.responseSchema ? "json" : (request.responseMode === "json" ? "json" : "text"),
|
|
189
|
+
schema: request.responseSchema,
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
const gateway = createCodaliGateway({
|
|
193
|
+
provider: primary,
|
|
194
|
+
// Classify and plan on the orchestrator model; only the final answer pays
|
|
195
|
+
// for the large one.
|
|
196
|
+
plannerProvider: orchestratorProvider ?? primary,
|
|
197
|
+
// So the trace names the model that actually ran.
|
|
198
|
+
finalAgent: synthesizer ?? orchestrator,
|
|
199
|
+
agentInventory: drivable,
|
|
200
|
+
toolDescriptors: gatewayToolDescriptorsFromRegistry(registry, allowedTools),
|
|
201
|
+
taskRunner: createLocalGatewayTaskRunner({
|
|
202
|
+
provider: worker,
|
|
203
|
+
registry,
|
|
204
|
+
toolContext: {
|
|
205
|
+
workspaceRoot,
|
|
206
|
+
allowShell: false,
|
|
207
|
+
allowDestructiveOperations: false,
|
|
208
|
+
allowOutsideWorkspace: false,
|
|
209
|
+
},
|
|
210
|
+
onEvent: (event) => {
|
|
211
|
+
if (event.type === "tool_call") {
|
|
212
|
+
tracer.recordToolCall({ taskId: event.taskId, tool: event.tool, args: event.args });
|
|
213
|
+
}
|
|
214
|
+
else if (event.type === "tool_result") {
|
|
215
|
+
tracer.recordToolResult({
|
|
216
|
+
taskId: event.taskId,
|
|
217
|
+
tool: event.tool,
|
|
218
|
+
ok: event.ok,
|
|
219
|
+
latencyMs: event.latencyMs,
|
|
220
|
+
errorCode: event.errorCode,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
}),
|
|
225
|
+
workerOptions: {
|
|
226
|
+
maxRuntimeMs: deadlineMs,
|
|
227
|
+
maxToolCalls,
|
|
228
|
+
perTaskTimeoutMs: Math.max(60000, Math.floor(deadlineMs / 2)),
|
|
229
|
+
maxParallelWorkers: DEFAULTS.maxParallelWorkers,
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
try {
|
|
233
|
+
const result = await gateway.run(gatewayRequest);
|
|
234
|
+
return {
|
|
235
|
+
status: result.status,
|
|
236
|
+
answer: result.answer,
|
|
237
|
+
output: result.output ?? result.answer,
|
|
238
|
+
sources: result.sources,
|
|
239
|
+
// Artifacts collected by the media tool are authoritative; the gateway's
|
|
240
|
+
// own list covers anything a worker persisted directly.
|
|
241
|
+
artifacts: artifacts.length > 0 ? artifacts : result.artifacts,
|
|
242
|
+
warnings: [...tracer.snapshot().warnings, ...result.warnings],
|
|
243
|
+
traceId: result.runId,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
return {
|
|
248
|
+
status: "failed",
|
|
249
|
+
answer: `Codali run failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
250
|
+
output: undefined,
|
|
251
|
+
sources: [],
|
|
252
|
+
artifacts,
|
|
253
|
+
warnings: tracer.snapshot().warnings,
|
|
254
|
+
traceId: runId,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
finally {
|
|
258
|
+
await mcp.registry?.close();
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
const buildDefaultRegistry = (workspaceRoot, context) => {
|
|
262
|
+
const registry = new ToolRegistry();
|
|
263
|
+
const client = new DocdexClient({
|
|
264
|
+
baseUrl: context.docdex?.baseUrl ?? "http://127.0.0.1:28491",
|
|
265
|
+
repoRoot: context.repo?.root ?? workspaceRoot,
|
|
266
|
+
repoId: context.docdex?.repoId,
|
|
267
|
+
apiKey: context.docdex?.apiKey,
|
|
268
|
+
allowedOperations: context.docdex?.allowedOperations,
|
|
269
|
+
});
|
|
270
|
+
for (const tool of createDocdexTools(client))
|
|
271
|
+
registry.register(tool);
|
|
272
|
+
return registry;
|
|
273
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type Server } from "node:http";
|
|
2
|
+
import { runCodali } from "./CodaliApi.js";
|
|
3
|
+
import type { RunContext, RunContextTenant } from "../runcontext/RunContextResolver.js";
|
|
4
|
+
/**
|
|
5
|
+
* Local HTTP surface for Codali.
|
|
6
|
+
*
|
|
7
|
+
* Exposes the same result contract the CLI produces, so a product integrating
|
|
8
|
+
* over HTTP and an engineer debugging on a terminal are exercising one
|
|
9
|
+
* implementation.
|
|
10
|
+
*
|
|
11
|
+
* ## Tenant identity
|
|
12
|
+
*
|
|
13
|
+
* Tenant is derived from the **authenticated caller**, never read from a
|
|
14
|
+
* request body or a client-supplied header. A tenant id in a body field is an
|
|
15
|
+
* assertion by an untrusted party; honouring it would let any caller read
|
|
16
|
+
* another tenant's tools and credentials by editing one JSON value.
|
|
17
|
+
*/
|
|
18
|
+
export interface CodaliServerPrincipal {
|
|
19
|
+
tenant: RunContextTenant;
|
|
20
|
+
/** Tools, credentials and limits this principal's tenant may use. */
|
|
21
|
+
runContext?: RunContext;
|
|
22
|
+
requesterId?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface CodaliServerOptions {
|
|
25
|
+
port?: number;
|
|
26
|
+
host?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Resolves an API key to a principal. Returning undefined rejects the
|
|
29
|
+
* request. There is no anonymous mode: an unauthenticated caller has no
|
|
30
|
+
* tenant, and a run without a tenant scope has no business reaching
|
|
31
|
+
* connectors.
|
|
32
|
+
*/
|
|
33
|
+
authenticate: (apiKey: string | undefined) => Promise<CodaliServerPrincipal | undefined>;
|
|
34
|
+
workspaceRoot?: string;
|
|
35
|
+
run?: typeof runCodali;
|
|
36
|
+
}
|
|
37
|
+
export declare const createCodaliServer: (options: CodaliServerOptions) => Server;
|
|
38
|
+
export declare const startCodaliServer: (options: CodaliServerOptions) => Promise<{
|
|
39
|
+
server: Server;
|
|
40
|
+
port: number;
|
|
41
|
+
}>;
|
|
42
|
+
//# sourceMappingURL=CodaliServer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CodaliServer.d.ts","sourceRoot":"","sources":["../../src/api/CodaliServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,MAAM,EAAuB,MAAM,WAAW,CAAC;AAEjG,OAAO,EAAE,SAAS,EAAsB,MAAM,gBAAgB,CAAC;AAC/D,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AAExF;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,gBAAgB,CAAC;IACzB,qEAAqE;IACrE,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,KAAK,OAAO,CAAC,qBAAqB,GAAG,SAAS,CAAC,CAAC;IACzF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,OAAO,SAAS,CAAC;CACxB;AAuDD,eAAO,MAAM,kBAAkB,GAAI,SAAS,mBAAmB,KAAG,MAyEjE,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,SAAS,mBAAmB,KAC3B,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAW1C,CAAC"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import { handleChatCompletion } from "./ChatCompletionsAdapter.js";
|
|
3
|
+
import { runCodali } from "./CodaliApi.js";
|
|
4
|
+
const readBody = async (request) => {
|
|
5
|
+
const chunks = [];
|
|
6
|
+
for await (const chunk of request)
|
|
7
|
+
chunks.push(chunk);
|
|
8
|
+
if (chunks.length === 0)
|
|
9
|
+
return undefined;
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
const send = (response, status, payload) => {
|
|
18
|
+
const body = JSON.stringify(payload);
|
|
19
|
+
response.writeHead(status, {
|
|
20
|
+
"Content-Type": "application/json",
|
|
21
|
+
"Content-Length": Buffer.byteLength(body),
|
|
22
|
+
});
|
|
23
|
+
response.end(body);
|
|
24
|
+
};
|
|
25
|
+
const readApiKey = (request) => {
|
|
26
|
+
const header = request.headers.authorization;
|
|
27
|
+
if (typeof header === "string" && header.toLowerCase().startsWith("bearer ")) {
|
|
28
|
+
return header.slice(7).trim();
|
|
29
|
+
}
|
|
30
|
+
const apiKey = request.headers["x-api-key"];
|
|
31
|
+
return typeof apiKey === "string" ? apiKey.trim() : undefined;
|
|
32
|
+
};
|
|
33
|
+
const isRecord = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
34
|
+
/**
|
|
35
|
+
* Strips any caller-supplied tenant or run context and substitutes what the
|
|
36
|
+
* authenticated principal is actually entitled to. This is the single point
|
|
37
|
+
* where cross-tenant access is prevented, so it is deliberately unconditional
|
|
38
|
+
* rather than a validation the caller could satisfy.
|
|
39
|
+
*/
|
|
40
|
+
const applyPrincipalScope = (payload, principal) => ({
|
|
41
|
+
...payload,
|
|
42
|
+
runContext: {
|
|
43
|
+
...(principal.runContext ?? {}),
|
|
44
|
+
tenant: principal.tenant,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
export const createCodaliServer = (options) => {
|
|
48
|
+
const run = options.run ?? runCodali;
|
|
49
|
+
return createServer(async (request, response) => {
|
|
50
|
+
const url = new URL(request.url ?? "/", "http://localhost");
|
|
51
|
+
if (request.method === "GET" && url.pathname === "/healthz") {
|
|
52
|
+
send(response, 200, { ok: true });
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (request.method !== "POST") {
|
|
56
|
+
send(response, 405, { error: { code: "method_not_allowed" } });
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const principal = await options.authenticate(readApiKey(request));
|
|
60
|
+
if (!principal) {
|
|
61
|
+
send(response, 401, { error: { code: "unauthorized" } });
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const body = await readBody(request);
|
|
65
|
+
if (!isRecord(body)) {
|
|
66
|
+
send(response, 400, { error: { code: "invalid_request_body" } });
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
if (url.pathname === "/v1/chat/completions") {
|
|
71
|
+
const chatRequest = body;
|
|
72
|
+
const scoped = {
|
|
73
|
+
...chatRequest,
|
|
74
|
+
codali: {
|
|
75
|
+
...(chatRequest.codali ?? {}),
|
|
76
|
+
// Caller-supplied context is discarded, not merged.
|
|
77
|
+
runContext: {
|
|
78
|
+
...(principal.runContext ?? {}),
|
|
79
|
+
tenant: principal.tenant,
|
|
80
|
+
},
|
|
81
|
+
workspaceRoot: options.workspaceRoot ?? chatRequest.codali?.workspaceRoot,
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
send(response, 200, await handleChatCompletion(scoped, { run }));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (url.pathname === "/v1/codali/run") {
|
|
88
|
+
const codaliRequest = applyPrincipalScope(body, principal);
|
|
89
|
+
send(response, 200, await run({
|
|
90
|
+
...codaliRequest,
|
|
91
|
+
workspaceRoot: options.workspaceRoot ?? codaliRequest.workspaceRoot,
|
|
92
|
+
}));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
send(response, 404, { error: { code: "not_found" } });
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
send(response, 500, {
|
|
99
|
+
error: {
|
|
100
|
+
code: "internal_error",
|
|
101
|
+
message: error instanceof Error ? error.message : String(error),
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
export const startCodaliServer = async (options) => {
|
|
108
|
+
const server = createCodaliServer(options);
|
|
109
|
+
const port = options.port ?? 8787;
|
|
110
|
+
await new Promise((resolve) => {
|
|
111
|
+
server.listen(port, options.host ?? "127.0.0.1", resolve);
|
|
112
|
+
});
|
|
113
|
+
const address = server.address();
|
|
114
|
+
return {
|
|
115
|
+
server,
|
|
116
|
+
port: typeof address === "object" && address ? address.port : port,
|
|
117
|
+
};
|
|
118
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { getAgentInventory } from "../agents/AgentInventory.js";
|
|
2
|
+
import { attachMcpTools } from "../connectors/mcp/McpToolSource.js";
|
|
3
|
+
import { createProviderForAssignment } from "../gateway/LocalGatewayProvider.js";
|
|
4
|
+
import { GatewayTracer } from "../gateway/GatewayTracer.js";
|
|
5
|
+
import { ToolRegistry } from "../tools/ToolRegistry.js";
|
|
6
|
+
import type { RunContext } from "../runcontext/RunContextResolver.js";
|
|
7
|
+
import type { CodaliGatewayMode, CodaliGatewayResult } from "../gateway/CodaliGatewayTypes.js";
|
|
8
|
+
import type { Provider } from "../providers/ProviderTypes.js";
|
|
9
|
+
/**
|
|
10
|
+
* `codali ask` — the terminal entry point to the orchestration gateway.
|
|
11
|
+
*
|
|
12
|
+
* Until this existed the gateway had exactly one caller (mswarm's cloud job
|
|
13
|
+
* path) and could not be exercised locally at all, which made every other
|
|
14
|
+
* improvement unverifiable.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Default budgets. Deliberately small: an orchestrator that can quietly spend
|
|
18
|
+
* fifty tool calls on one question is not one you can put in front of users.
|
|
19
|
+
*/
|
|
20
|
+
export declare const DEFAULT_MAX_ROUNDS = 3;
|
|
21
|
+
export declare const DEFAULT_MAX_TOOL_CALLS = 20;
|
|
22
|
+
export declare const DEFAULT_MAX_MODEL_CALLS = 12;
|
|
23
|
+
/**
|
|
24
|
+
* Wall-clock budget. Generous because CLI-backed agents (codex, claude,
|
|
25
|
+
* gemini) commonly take 30-90s per call, and several tasks run per question.
|
|
26
|
+
*/
|
|
27
|
+
export declare const DEFAULT_DEADLINE_MS = 600000;
|
|
28
|
+
/**
|
|
29
|
+
* Independent worker tasks executed concurrently. Bounded rather than
|
|
30
|
+
* unlimited: each task holds a model call, and a tenant's connectors have their
|
|
31
|
+
* own rate limits.
|
|
32
|
+
*/
|
|
33
|
+
export declare const DEFAULT_MAX_PARALLEL_WORKERS = 3;
|
|
34
|
+
export interface AskOptions {
|
|
35
|
+
query: string;
|
|
36
|
+
workspaceRoot: string;
|
|
37
|
+
mode: CodaliGatewayMode;
|
|
38
|
+
trace: boolean;
|
|
39
|
+
json: boolean;
|
|
40
|
+
maxRounds: number;
|
|
41
|
+
maxToolCalls: number;
|
|
42
|
+
deadlineMs: number;
|
|
43
|
+
docdexBaseUrl?: string;
|
|
44
|
+
responseSchemaPath?: string;
|
|
45
|
+
responseFormat?: "text" | "json";
|
|
46
|
+
noTools: boolean;
|
|
47
|
+
}
|
|
48
|
+
export declare const parseAskArgs: (argv: string[]) => AskOptions;
|
|
49
|
+
export interface AskDependencies {
|
|
50
|
+
loadInventory?: typeof getAgentInventory;
|
|
51
|
+
resolveRunContext?: (workspaceRoot: string) => Promise<RunContext>;
|
|
52
|
+
buildRegistry?: (options: AskOptions, context: RunContext) => ToolRegistry;
|
|
53
|
+
createProvider?: (assignment: Parameters<typeof createProviderForAssignment>[0]) => Provider;
|
|
54
|
+
attachMcp?: typeof attachMcpTools;
|
|
55
|
+
write?: (line: string) => void;
|
|
56
|
+
}
|
|
57
|
+
export interface AskOutcome {
|
|
58
|
+
result?: CodaliGatewayResult;
|
|
59
|
+
tracer: GatewayTracer;
|
|
60
|
+
exitCode: number;
|
|
61
|
+
}
|
|
62
|
+
export declare const runAsk: (argv: string[], deps?: AskDependencies) => Promise<AskOutcome>;
|
|
63
|
+
export declare const AskCommand: {
|
|
64
|
+
run(argv: string[]): Promise<void>;
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=AskCommand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AskCommand.d.ts","sourceRoot":"","sources":["../../src/cli/AskCommand.ts"],"names":[],"mappings":"AACA,OAAO,EAAyB,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAGvF,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAIpE,OAAO,EACL,2BAA2B,EAE5B,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAE5D,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AACtE,OAAO,KAAK,EACV,iBAAiB,EAEjB,mBAAmB,EACpB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAE9D;;;;;;GAMG;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB,IAAI,CAAC;AACpC,eAAO,MAAM,sBAAsB,KAAK,CAAC;AACzC,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C;;;GAGG;AACH,eAAO,MAAM,mBAAmB,SAAU,CAAC;AAE3C;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAe9C,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,iBAAiB,CAAC;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACjC,OAAO,EAAE,OAAO,CAAC;CAClB;AA+BD,eAAO,MAAM,YAAY,GAAI,MAAM,MAAM,EAAE,KAAG,UAuE7C,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,aAAa,CAAC,EAAE,OAAO,iBAAiB,CAAC;IACzC,iBAAiB,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IACnE,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,KAAK,YAAY,CAAC;IAC3E,cAAc,CAAC,EAAE,CACf,UAAU,EAAE,UAAU,CAAC,OAAO,2BAA2B,CAAC,CAAC,CAAC,CAAC,KAC1D,QAAQ,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,cAAc,CAAC;IAClC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AA6BD,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,MAAM,EAAE,aAAa,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,MAAM,GACjB,MAAM,MAAM,EAAE,EACd,OAAM,eAAoB,KACzB,OAAO,CAAC,UAAU,CAwXpB,CAAC;AAEF,eAAO,MAAM,UAAU;cACL,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAMzC,CAAC"}
|