melchizedek-agents 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +10 -0
- package/AGENT_SETUP.md +3 -1
- package/CHANGELOG.md +232 -0
- package/DOCUMENTATION.md +91 -16
- package/ONBOARDING.md +339 -0
- package/QUICKSTART.md +15 -2
- package/README.md +5 -2
- package/config/agents/examples/council.yaml +1 -1
- package/config/agents/examples/critic.yaml +8 -8
- package/config/agents/examples/image_production.yaml +4 -3
- package/config/agents/syndicate.schema.json +65 -7
- package/config/agents/syndicateSchema.yaml +18 -6
- package/config/agents/templates/intake_extractor.yaml +3 -2
- package/config/agents/templates/systems_operator.yaml +5 -2
- package/dist/lib/a2a/app.d.ts +12 -0
- package/dist/lib/a2a/app.js +22 -11
- package/dist/lib/a2a/executor.js +14 -6
- package/dist/lib/a2a/oauthSetup.d.ts +16 -0
- package/dist/lib/a2a/oauthSetup.js +57 -3
- package/dist/lib/agentDialect.d.ts +18 -8
- package/dist/lib/agentDialect.js +36 -19
- package/dist/lib/compile.d.ts +38 -7
- package/dist/lib/compile.js +57 -37
- package/dist/lib/compileNative.d.ts +11 -3
- package/dist/lib/compileNative.js +18 -1
- package/dist/lib/doctor.d.ts +76 -0
- package/dist/lib/doctor.js +159 -7
- package/dist/lib/loadSyndicate.d.ts +9 -0
- package/dist/lib/models/capabilities.d.ts +9 -0
- package/dist/lib/models/capabilities.js +24 -0
- package/dist/lib/models/chatCompletionsAdapter.d.ts +11 -3
- package/dist/lib/models/chatCompletionsAdapter.js +19 -5
- package/dist/lib/models/claudeAdapter.js +4 -0
- package/dist/lib/models/contract.d.ts +5 -2
- package/dist/lib/models/gateway.d.ts +2 -1
- package/dist/lib/models/gateway.js +4 -2
- package/dist/lib/models/gatewayAdapter.d.ts +7 -1
- package/dist/lib/models/gatewayAdapter.js +8 -0
- package/dist/lib/models/geminiAdapter.js +8 -2
- package/dist/lib/models/genaiMapping.d.ts +8 -4
- package/dist/lib/models/genaiMapping.js +23 -9
- package/dist/lib/models/gptAdapter.d.ts +4 -2
- package/dist/lib/models/gptAdapter.js +11 -4
- package/dist/lib/models/grokAdapter.d.ts +3 -1
- package/dist/lib/models/grokAdapter.js +11 -3
- package/dist/lib/models/kimiAdapter.d.ts +2 -1
- package/dist/lib/models/kimiAdapter.js +3 -3
- package/dist/lib/models/reasoning.d.ts +30 -2
- package/dist/lib/models/reasoning.js +56 -5
- package/dist/lib/onboarding.d.ts +134 -0
- package/dist/lib/onboarding.js +539 -0
- package/dist/lib/runtime/approvals.d.ts +2 -0
- package/dist/lib/runtime/approvals.js +4 -3
- package/dist/lib/runtime/credentials.d.ts +6 -0
- package/dist/lib/runtime/native/agentLoop.d.ts +5 -2
- package/dist/lib/runtime/native/agentLoop.js +2 -0
- package/dist/lib/runtime/native/checkpoint.d.ts +12 -5
- package/dist/lib/runtime/native/checkpoint.js +13 -19
- package/dist/lib/runtime/native/delegate.d.ts +82 -1
- package/dist/lib/runtime/native/delegate.js +157 -2
- package/dist/lib/runtime/native/interrupts.d.ts +88 -5
- package/dist/lib/runtime/native/interrupts.js +220 -30
- package/dist/lib/runtime/syndicateTurn.d.ts +4 -2
- package/dist/lib/runtime/syndicateTurn.js +295 -62
- package/dist/lib/runtime/turnControl.d.ts +41 -0
- package/dist/lib/session/transcript.d.ts +9 -0
- package/dist/lib/session/transcript.js +20 -1
- package/dist/lib/skills.d.ts +18 -0
- package/dist/lib/skills.js +73 -1
- package/dist/lib/storage/postgres/index.d.ts +6 -1
- package/dist/lib/storage/postgres/index.js +1 -1
- package/dist/lib/storage/postgres/taskQueue.d.ts +7 -3
- package/dist/lib/storage/postgres/taskQueue.js +10 -3
- package/dist/lib/syndicateSchema.d.ts +38 -1
- package/dist/lib/syndicateSchema.js +25 -18
- package/dist/lib/tools/auth.d.ts +13 -1
- package/dist/lib/tools/auth.js +10 -2
- package/dist/lib/tools/credentialHosts.d.ts +88 -0
- package/dist/lib/tools/credentialHosts.js +165 -0
- package/dist/lib/tools/credentialUses.d.ts +92 -0
- package/dist/lib/tools/credentialUses.js +112 -0
- package/dist/lib/tools/mcpToolFactory.js +1 -1
- package/dist/lib/tools/oauthConsent.js +8 -1
- package/dist/lib/tools/oauthHosts.d.ts +2 -0
- package/dist/lib/tools/oauthHosts.js +2 -2
- package/dist/lib/tools/oauthTools.d.ts +10 -1
- package/dist/lib/tools/oauthTools.js +29 -3
- package/dist/lib/tools/openapiTools.d.ts +7 -0
- package/dist/lib/tools/openapiTools.js +49 -3
- package/dist/lib/tools/taskTools.d.ts +32 -2
- package/dist/lib/tools/taskTools.js +93 -33
- package/dist/lib/workflow/turn.d.ts +22 -3
- package/dist/lib/workflow/turn.js +151 -31
- package/dist/lib/workflow.d.ts +10 -7
- package/dist/lib/workflow.js +10 -7
- package/dist/scripts/a2a_server.d.ts +2 -2
- package/dist/scripts/a2a_server.js +11 -2
- package/dist/scripts/assistant_worker.d.ts +2 -0
- package/dist/scripts/assistant_worker.js +13 -4
- package/dist/scripts/doctor.d.ts +4 -2
- package/dist/scripts/doctor.js +6 -4
- package/dist/scripts/setup.d.ts +23 -0
- package/dist/scripts/setup.js +154 -0
- package/dist/scripts/skills_install.d.ts +3 -1
- package/dist/scripts/skills_install.js +17 -2
- package/dist/scripts/yaml_codemod.d.ts +12 -2
- package/dist/scripts/yaml_codemod.js +115 -33
- package/package.json +4 -1
- package/skills/README.md +11 -2
- package/skills/briefs/_shared.md +1 -1
- package/skills/briefs/melchizedek-models.md +4 -2
- package/skills/briefs/skills-readme.md +3 -1
- package/skills/melchizedek-models/SKILL.md +6 -2
- package/skills/melchizedek-onboard/SKILL.md +49 -0
- package/skills/melchizedek-onboard-cloud/SKILL.md +38 -0
- package/skills/melchizedek-onboard-keys/SKILL.md +35 -0
- package/skills/melchizedek-onboard-local/SKILL.md +41 -0
- package/skills/melchizedek-onboard-serve/SKILL.md +43 -0
package/dist/lib/compile.js
CHANGED
|
@@ -264,31 +264,44 @@ function nestedOptions(ref, opts) {
|
|
|
264
264
|
return { ...opts, [NESTING]: [...chain, ref] };
|
|
265
265
|
}
|
|
266
266
|
/**
|
|
267
|
-
* A nested `yaml_reference:` syndicate, loaded
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
* route, which never runs nested, is refused;
|
|
274
|
-
* - a nested workflow's gates pause the walk, which reaches the caller
|
|
275
|
-
* only through a delegated call's open call (`delegated`): as a
|
|
276
|
-
* dispatch route or a workflow node, its pause cannot reach the turn
|
|
277
|
-
* yet, so they are refused there.
|
|
267
|
+
* A nested `yaml_reference:` syndicate, loaded. Every pause inside one
|
|
268
|
+
* reaches the turn wherever it runs (ADR 0110, ADR 0111, ADR 0119,
|
|
269
|
+
* ADR 0120): a nested delegate syndicate's through the open call, a nested
|
|
270
|
+
* workflow's through its walk, a nested dispatch syndicate's through its
|
|
271
|
+
* own turn, whose routes run as they run at the top. Nothing is refused
|
|
272
|
+
* here any more.
|
|
278
273
|
*/
|
|
279
|
-
function loadNestedSyndicate(ref, opts
|
|
274
|
+
function loadNestedSyndicate(ref, opts) {
|
|
280
275
|
opts.log?.(`Loading nested syndicate: ${ref}`);
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
276
|
+
return (opts.loadNested ?? loadSyndicate)(ref);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* A nested dispatch syndicate as its entry runs it (ADR 0120). Its routes
|
|
280
|
+
* compile when its turn routes to one, as at the top, so a route that
|
|
281
|
+
* cannot compile fails that turn; the references below it are followed
|
|
282
|
+
* here (loaded, not compiled: no tool is resolved and no MCP server is
|
|
283
|
+
* reached), so a reference cycle or a depth past the limit is refused at
|
|
284
|
+
* load, by name, before any model call. A route may not carry the entry's
|
|
285
|
+
* name: the pause walk tells the nested syndicate from its routes by name.
|
|
286
|
+
*/
|
|
287
|
+
function compileDispatchEntry(subCfg, nested, nestedOpts) {
|
|
288
|
+
const ref = subCfg.yaml_reference;
|
|
289
|
+
for (const route of nested.subagents ?? []) {
|
|
290
|
+
if (route.name === subCfg.name) {
|
|
291
|
+
throw new Error(`${ref}: the route '${route.name}' has the name its caller gives the nested syndicate; rename the route or the entry.`);
|
|
286
292
|
}
|
|
287
293
|
}
|
|
288
|
-
|
|
289
|
-
|
|
294
|
+
followReferences(nested, nestedOpts);
|
|
295
|
+
return { name: subCfg.name, description: subCfg.description ?? '', config: nested, ref, compile: nestedOpts };
|
|
296
|
+
}
|
|
297
|
+
/** Loads every `yaml_reference` below `config`, depth first, through nestedOptions: a cycle or a chain past the limit throws by name. */
|
|
298
|
+
function followReferences(config, opts) {
|
|
299
|
+
for (const entry of config.subagents ?? []) {
|
|
300
|
+
if (!entry.yaml_reference || entry.a2a_agent_url)
|
|
301
|
+
continue;
|
|
302
|
+
const below = nestedOptions(entry.yaml_reference, opts);
|
|
303
|
+
followReferences(loadNestedSyndicate(entry.yaml_reference, below), below);
|
|
290
304
|
}
|
|
291
|
-
return nested;
|
|
292
305
|
}
|
|
293
306
|
/**
|
|
294
307
|
* The spec of a workflow syndicate: every agent through compileSubagentSpec
|
|
@@ -296,35 +309,34 @@ function loadNestedSyndicate(ref, opts, delegated = false) {
|
|
|
296
309
|
* lookup for its tool nodes. `name` and `description` name a nested one
|
|
297
310
|
* after its subagent entry (ADR 0098); `ref` is the file it came from.
|
|
298
311
|
*
|
|
299
|
-
* A nested workflow
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
* by name there.
|
|
312
|
+
* A nested workflow's pauses (an `ask_user` node, a gate) reach the turn
|
|
313
|
+
* wherever it runs (ADR 0111, ADR 0119). `delegated` is accepted for the
|
|
314
|
+
* callers that pass it and no longer changes what compiles.
|
|
303
315
|
*/
|
|
304
316
|
export async function compileWorkflowSpec(config, opts = {}, name, description, ref, delegated = false) {
|
|
305
317
|
if (!isWorkflowSyndicate(config))
|
|
306
318
|
throw new Error(`${config.syndicate_name}: no workflow block`);
|
|
307
|
-
if (name && !delegated) {
|
|
308
|
-
const asking = Object.entries(config.workflow.nodes ?? {}).find(([, node]) => nodeKind(node) === 'ask_user');
|
|
309
|
-
if (asking) {
|
|
310
|
-
throw new Error(`${ref ?? config.syndicate_name}: the ask_user node '${asking[0]}' pauses for a person, which a workflow run as a dispatch route or a workflow node (${name}) cannot carry to the turn yet; delegate to the workflow as a subagent, run it as its own syndicate, or remove the node.`);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
319
|
const workflowName = name || config.syndicate_name;
|
|
314
320
|
const agents = [];
|
|
315
321
|
const workflows = [];
|
|
322
|
+
const dispatches = [];
|
|
316
323
|
for (const yaml of [{ description: '', ...config.orchestrator }, ...(config.subagents ?? [])]) {
|
|
317
324
|
const entry = await compileEntrySpec(yaml, opts);
|
|
318
325
|
if (entry.kind === 'workflow')
|
|
319
326
|
workflows.push({ yaml, workflow: entry.workflow });
|
|
327
|
+
else if (entry.kind === 'dispatch')
|
|
328
|
+
dispatches.push({ yaml, dispatch: entry.dispatch });
|
|
320
329
|
else
|
|
321
330
|
agents.push({ yaml, spec: entry.spec });
|
|
322
331
|
}
|
|
323
|
-
// A map runs one agent per item; a nested graph is not an agent a map item can be (ADR 0106).
|
|
332
|
+
// A map runs one agent per item; a nested graph or a nested dispatch syndicate is not an agent a map item can be (ADR 0106, ADR 0120).
|
|
324
333
|
for (const [node, entry] of Object.entries(config.workflow.nodes ?? {})) {
|
|
325
|
-
|
|
334
|
+
if (nodeKind(entry) !== 'map')
|
|
335
|
+
continue;
|
|
336
|
+
const mapped = workflows.find((w) => w.yaml.name === entry.map) ?? dispatches.find((d) => d.yaml.name === entry.map);
|
|
326
337
|
if (mapped) {
|
|
327
|
-
|
|
338
|
+
const what = 'workflow' in mapped ? 'a workflow syndicate' : 'a dispatch syndicate';
|
|
339
|
+
throw new Error(`${ref ?? config.syndicate_name}: the map node '${node}' runs '${mapped.yaml.name}', ${what} (${mapped.yaml.yaml_reference}); a map runs one agent per item, so make it a node of its own.`);
|
|
328
340
|
}
|
|
329
341
|
}
|
|
330
342
|
return {
|
|
@@ -333,6 +345,7 @@ export async function compileWorkflowSpec(config, opts = {}, name, description,
|
|
|
333
345
|
config: { ...config, syndicate_name: workflowName },
|
|
334
346
|
agents,
|
|
335
347
|
workflows,
|
|
348
|
+
dispatches,
|
|
336
349
|
resolveTool: (tool) => resolveNamedTools([tool], opts.onUnknownTool)[0],
|
|
337
350
|
};
|
|
338
351
|
}
|
|
@@ -346,16 +359,20 @@ export function workflowAgentSpecs(spec) {
|
|
|
346
359
|
* as its whole graph under the entry's name and description. A delegated
|
|
347
360
|
* subagent, a dispatch route and a workflow node all compile through here,
|
|
348
361
|
* so the graph a file describes is the graph that runs (ADR 0106).
|
|
349
|
-
*
|
|
350
|
-
*
|
|
362
|
+
* A nested workflow pauses wherever it runs (ADR 0111, ADR 0119);
|
|
363
|
+
* `delegated` is accepted for the callers that pass it and no longer
|
|
364
|
+
* changes what compiles.
|
|
351
365
|
*/
|
|
352
366
|
export async function compileEntrySpec(subCfg, opts = {}, delegated = false) {
|
|
353
367
|
if (subCfg.yaml_reference && !subCfg.a2a_agent_url) {
|
|
354
368
|
const nestedOpts = nestedOptions(subCfg.yaml_reference, opts);
|
|
355
|
-
const nested = loadNestedSyndicate(subCfg.yaml_reference, nestedOpts
|
|
369
|
+
const nested = loadNestedSyndicate(subCfg.yaml_reference, nestedOpts);
|
|
356
370
|
if (isWorkflowSyndicate(nested)) {
|
|
357
371
|
return { kind: 'workflow', workflow: await compileWorkflowSpec(nested, nestedOpts, subCfg.name, subCfg.description, subCfg.yaml_reference, delegated) };
|
|
358
372
|
}
|
|
373
|
+
// A nested dispatch syndicate classifies and routes as at the top (ADR 0120), wherever it runs.
|
|
374
|
+
if (isDispatchSyndicate(nested))
|
|
375
|
+
return { kind: 'dispatch', dispatch: compileDispatchEntry(subCfg, nested, nestedOpts) };
|
|
359
376
|
return { kind: 'agent', spec: await compileSpec(nested, nestedOpts, subCfg.name, subCfg.description) };
|
|
360
377
|
}
|
|
361
378
|
return { kind: 'agent', spec: await compileSubagentSpec(subCfg, opts) };
|
|
@@ -389,6 +406,9 @@ export async function compileSubagentSpec(subCfg, opts = {}) {
|
|
|
389
406
|
if (isWorkflowSyndicate(nested)) {
|
|
390
407
|
throw new Error(`${subCfg.yaml_reference}: '${subCfg.name}' is a workflow syndicate, which runs as its whole graph, not as one agent; compile the entry with compileEntrySpec (ADR 0106).`);
|
|
391
408
|
}
|
|
409
|
+
if (isDispatchSyndicate(nested)) {
|
|
410
|
+
throw new Error(`${subCfg.yaml_reference}: '${subCfg.name}' is a dispatch syndicate, which runs as its own turn (its classifier, then a route), not as one agent; compile the entry with compileEntrySpec (ADR 0120).`);
|
|
411
|
+
}
|
|
392
412
|
return compileSpec(nested, nestedOpts, subCfg.name, subCfg.description);
|
|
393
413
|
}
|
|
394
414
|
// YAML v2 (ADR 0115): the engine form, for a config built in code (a loaded one already is).
|
|
@@ -427,7 +447,7 @@ export async function compileSpec(config, opts = {}, overrideName, overrideDescr
|
|
|
427
447
|
}
|
|
428
448
|
// A delegated subagent: a pause inside it, a nested workflow's included, reaches the caller (ADR 0110, ADR 0111).
|
|
429
449
|
const entry = await compileEntrySpec(subCfg, opts, true);
|
|
430
|
-
return entry.kind === '
|
|
450
|
+
return entry.kind === 'agent' ? { kind: 'agent', agent: entry.spec } : entry;
|
|
431
451
|
}));
|
|
432
452
|
const orchestrator = toEngineAgent(config.orchestrator); // YAML v2 (ADR 0115), for a config built in code
|
|
433
453
|
const name = overrideName || orchestrator.name;
|
|
@@ -32,13 +32,14 @@
|
|
|
32
32
|
* caller's. ADK classes are told apart by ADK's own Symbol.for marks, so
|
|
33
33
|
* this file imports nothing from ADK.
|
|
34
34
|
*/
|
|
35
|
-
import type { AgentSpec, CompileOptions, WorkflowSpec } from './compile.ts';
|
|
35
|
+
import type { AgentSpec, CompileOptions, DispatchSpec, WorkflowSpec } from './compile.ts';
|
|
36
36
|
import type { SubagentYamlConfig, SyndicateYamlConfig } from './loadSyndicate.ts';
|
|
37
37
|
import type { ModelAdapter } from './models/contract.ts';
|
|
38
38
|
import type { WorkflowSubagent } from './runtime/native/delegate.ts';
|
|
39
39
|
import type { NativeAgent } from './runtime/native/request.ts';
|
|
40
40
|
export { UnsupportedOnRuntimeError, unsupportedOnNative } from './runtime/runtimeFlag.ts';
|
|
41
41
|
import type { WorkflowGraph } from './workflow/graph.ts';
|
|
42
|
+
import type { NestedRun } from './workflow/turn.ts';
|
|
42
43
|
/**
|
|
43
44
|
* The class name of an ADK model the runtime cannot run, else undefined:
|
|
44
45
|
* any ADK BaseLlm (ADR 0107; the ADK runtime that ran one left in 1.0.0).
|
|
@@ -67,8 +68,8 @@ export interface NativeWorkflow {
|
|
|
67
68
|
graph: WorkflowGraph;
|
|
68
69
|
agents: Map<string, NativeAgent>;
|
|
69
70
|
resolveTool: (name: string) => unknown;
|
|
70
|
-
/** The nodes that are a nested workflow syndicate, each compiled the same way,
|
|
71
|
-
workflows: Map<string, NativeWorkflow>;
|
|
71
|
+
/** The nodes that are a nested workflow syndicate, each compiled the same way (ADR 0106), or a nested dispatch syndicate, its own turn (ADR 0120), by YAML name. */
|
|
72
|
+
workflows: Map<string, NativeWorkflow | NestedRun>;
|
|
72
73
|
}
|
|
73
74
|
/**
|
|
74
75
|
* A workflow spec for the walk: the graph, every agent compiled from its
|
|
@@ -82,6 +83,13 @@ export declare function compileNativeWorkflow(spec: WorkflowSpec): NativeWorkflo
|
|
|
82
83
|
* session lib/runtime/native/delegate.ts opened.
|
|
83
84
|
*/
|
|
84
85
|
export declare function workflowSubagentOf(workflow: NativeWorkflow): WorkflowSubagent;
|
|
86
|
+
/**
|
|
87
|
+
* A nested dispatch syndicate as the delegated subagent the native loop
|
|
88
|
+
* calls, or the node a walk runs (ADR 0120): one call runs it as its own
|
|
89
|
+
* turn (nestedDispatchWalk) on the child session lib/runtime/native/delegate.ts
|
|
90
|
+
* (or lib/workflow/turn.ts) opened.
|
|
91
|
+
*/
|
|
92
|
+
export declare function dispatchSubagentOf(dispatch: DispatchSpec): WorkflowSubagent & NestedRun;
|
|
85
93
|
/** One subagent entry (a dispatch route) for the native loop: compileSubagentSpec, then compileNative. */
|
|
86
94
|
export declare function compileNativeSubagent(subCfg: SubagentYamlConfig, opts?: CompileOptions): Promise<NativeAgent>;
|
|
87
95
|
/** What nativeAdapterFor reads from a spec: its model resolution, the other ids it calls, and its delegated subagents. */
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
import { remoteAgentOwnTool } from './a2a/remoteAgent.js';
|
|
36
36
|
import { compileSpec, compileSubagentSpec, workflowAgentSpecs } from './compile.js';
|
|
37
37
|
import { resolveAdapter } from './models/registry.js';
|
|
38
|
-
import { subagentTool, workflowSubagentTool } from './runtime/native/delegate.js';
|
|
38
|
+
import { nestedDispatchWalk, subagentTool, workflowSubagentTool } from './runtime/native/delegate.js';
|
|
39
39
|
import { unsupportedOnNative } from './runtime/runtimeFlag.js';
|
|
40
40
|
export { UnsupportedOnRuntimeError, unsupportedOnNative } from './runtime/runtimeFlag.js';
|
|
41
41
|
import { instructionToolOf, toolOf, toolsetOf } from './tools/tool.js';
|
|
@@ -108,6 +108,8 @@ export function compileNative(spec) {
|
|
|
108
108
|
tools.push(subagentTool(compileNative(entry.agent)));
|
|
109
109
|
else if (entry.kind === 'workflow')
|
|
110
110
|
tools.push(workflowSubagentTool(workflowSubagentOf(compileNativeWorkflow(entry.workflow))));
|
|
111
|
+
else if (entry.kind === 'dispatch')
|
|
112
|
+
tools.push(workflowSubagentTool(dispatchSubagentOf(entry.dispatch)));
|
|
111
113
|
else if (entry.kind === 'remote')
|
|
112
114
|
tools.push(remoteAgentOwnTool({ name: entry.name, description: entry.description, url: entry.url }));
|
|
113
115
|
else
|
|
@@ -182,6 +184,8 @@ export function compileNativeWorkflow(spec) {
|
|
|
182
184
|
const workflows = new Map();
|
|
183
185
|
for (const { yaml, workflow } of spec.workflows)
|
|
184
186
|
workflows.set(yaml.name, compileNativeWorkflow(workflow));
|
|
187
|
+
for (const { yaml, dispatch } of spec.dispatches ?? [])
|
|
188
|
+
workflows.set(yaml.name, dispatchSubagentOf(dispatch));
|
|
185
189
|
refuseUnrunnableNodes(graph, spec.resolveTool);
|
|
186
190
|
return { name: spec.name, description: spec.description, graph, agents, resolveTool: spec.resolveTool, workflows };
|
|
187
191
|
}
|
|
@@ -214,6 +218,19 @@ export function workflowSubagentOf(workflow) {
|
|
|
214
218
|
}),
|
|
215
219
|
};
|
|
216
220
|
}
|
|
221
|
+
/**
|
|
222
|
+
* A nested dispatch syndicate as the delegated subagent the native loop
|
|
223
|
+
* calls, or the node a walk runs (ADR 0120): one call runs it as its own
|
|
224
|
+
* turn (nestedDispatchWalk) on the child session lib/runtime/native/delegate.ts
|
|
225
|
+
* (or lib/workflow/turn.ts) opened.
|
|
226
|
+
*/
|
|
227
|
+
export function dispatchSubagentOf(dispatch) {
|
|
228
|
+
return {
|
|
229
|
+
name: dispatch.name,
|
|
230
|
+
...(dispatch.description ? { description: dispatch.description } : {}),
|
|
231
|
+
walk: (run) => nestedDispatchWalk({ name: dispatch.name, config: dispatch.config, compile: dispatch.compile }, run),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
217
234
|
/** One subagent entry (a dispatch route) for the native loop: compileSubagentSpec, then compileNative. */
|
|
218
235
|
export async function compileNativeSubagent(subCfg, opts = {}) {
|
|
219
236
|
return compileNative(await compileSubagentSpec(subCfg, opts));
|
package/dist/lib/doctor.d.ts
CHANGED
|
@@ -75,6 +75,21 @@ export interface DoctorOAuth {
|
|
|
75
75
|
set: Record<string, boolean>;
|
|
76
76
|
problems: string[];
|
|
77
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* The static credential variables the syndicates send, under the operator's
|
|
80
|
+
* credential host allowlist (ADR 0122). Present when any syndicate sends one
|
|
81
|
+
* or MELCHIZEDEK_CREDENTIAL_HOSTS is set. Names and hosts only.
|
|
82
|
+
*/
|
|
83
|
+
export interface DoctorCredentials {
|
|
84
|
+
/** Whether MELCHIZEDEK_CREDENTIAL_HOSTS is set. */
|
|
85
|
+
set: boolean;
|
|
86
|
+
/** Every variable the syndicates send (bearer_env, api_key.env, client_secret_env), sorted. */
|
|
87
|
+
envs: string[];
|
|
88
|
+
/** With no allowlist: the variables sent wherever the YAML says, named. Not a --check failure. */
|
|
89
|
+
warning?: string;
|
|
90
|
+
/** A malformed allowlist, and each use it refuses (`file: agent · tools · role: why`). A --check failure. */
|
|
91
|
+
problems: string[];
|
|
92
|
+
}
|
|
78
93
|
export interface DoctorSyndicate {
|
|
79
94
|
/** Path relative to the agents dir, e.g. "examples/council.yaml". */
|
|
80
95
|
file: string;
|
|
@@ -106,6 +121,11 @@ export interface DoctorSyndicate {
|
|
|
106
121
|
};
|
|
107
122
|
/** The tools that need an OAuth grant (ADR 0112), when any do. */
|
|
108
123
|
grants?: DoctorGrant[];
|
|
124
|
+
/** The static credential variables its tools send (ADR 0122), and what the allowlist refuses; when it sends any. */
|
|
125
|
+
credentials?: {
|
|
126
|
+
envs: string[];
|
|
127
|
+
problems: string[];
|
|
128
|
+
};
|
|
109
129
|
}
|
|
110
130
|
export interface Unlock {
|
|
111
131
|
env: string;
|
|
@@ -141,6 +161,45 @@ export interface DoctorResult {
|
|
|
141
161
|
counts: Record<VerdictState, number>;
|
|
142
162
|
/** The OAuth setup, when any syndicate declares a grant or its variables are set. */
|
|
143
163
|
oauth?: DoctorOAuth;
|
|
164
|
+
/** The credential host allowlist (ADR 0122), when any syndicate sends a static credential or it is set. */
|
|
165
|
+
credentials?: DoctorCredentials;
|
|
166
|
+
/** How each provider would be reached here: the providers line, as data. */
|
|
167
|
+
providers: ProviderPath[];
|
|
168
|
+
/** How the A2A server would authenticate and bill, when any of its variables is set. */
|
|
169
|
+
serving?: DoctorServing;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* How one provider would be reached under this environment (the doctor's
|
|
173
|
+
* providers line): funded or not, directly or through the gateway, and on
|
|
174
|
+
* which platform. `melchizedek-setup --auto` reads its levels from this.
|
|
175
|
+
*/
|
|
176
|
+
export interface ProviderPath {
|
|
177
|
+
provider: ProviderId;
|
|
178
|
+
label: string;
|
|
179
|
+
funded: boolean;
|
|
180
|
+
transport: 'direct' | 'gateway';
|
|
181
|
+
/** The gateway id when transport is 'gateway'. */
|
|
182
|
+
gateway?: string;
|
|
183
|
+
/** `local` for Ollama; a cloud platform (vertex, bedrock, azure) or `direct` otherwise. */
|
|
184
|
+
platform: Platform | 'local';
|
|
185
|
+
keyEnv: string | null;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* The A2A server's identity and billing settings (scripts/a2a_server.ts),
|
|
189
|
+
* names and presence only: which `A2A_AUTH` mode, whether the secret and the
|
|
190
|
+
* mode's own variables are set, and who pays (`A2A_KEY_MODE`).
|
|
191
|
+
*/
|
|
192
|
+
export interface DoctorServing {
|
|
193
|
+
/** A2A_AUTH as set, or `secret` (the server's default). */
|
|
194
|
+
auth: string;
|
|
195
|
+
/** True when A2A_AUTH is set explicitly. */
|
|
196
|
+
authDeclared: boolean;
|
|
197
|
+
/** A2A_KEY_MODE as set, or `server`. */
|
|
198
|
+
keyMode: string;
|
|
199
|
+
/** Which of the serving variables are set (never their values). */
|
|
200
|
+
set: Record<string, boolean>;
|
|
201
|
+
/** What the server would refuse to start on, by variable name. */
|
|
202
|
+
problems: string[];
|
|
144
203
|
}
|
|
145
204
|
export interface DoctorEndpoint {
|
|
146
205
|
provider: ProviderId;
|
|
@@ -166,6 +225,16 @@ export interface DoctorEndpoint {
|
|
|
166
225
|
export declare function runtimeReport(env?: NodeJS.ProcessEnv): DoctorRuntime;
|
|
167
226
|
/** One row per provider that is not on its vendor's public API with an env key. */
|
|
168
227
|
export declare function endpointRows(): DoctorEndpoint[];
|
|
228
|
+
/** One status per provider, probed with an id of its own prefix (the providers line). */
|
|
229
|
+
export declare function providerPaths(): ProviderPath[];
|
|
230
|
+
/** The serving variables the doctor reports by presence. */
|
|
231
|
+
export declare const SERVING_ENV: readonly ['A2A_AUTH', 'A2A_SERVER_SECRET', 'A2A_CALLERS', 'A2A_JWT_ISSUER', 'A2A_JWT_AUDIENCE', 'A2A_JWT_JWKS_URL', 'A2A_JWT_SECRET', 'A2A_TRUSTED_USER_HEADER', 'A2A_KEY_MODE', 'PUBLIC_URL'];
|
|
232
|
+
/**
|
|
233
|
+
* How the A2A server would authenticate callers and who would pay, from the
|
|
234
|
+
* environment; undefined when none of its variables is set. The checks are
|
|
235
|
+
* the ones scripts/a2a_server.ts stops on, reported by name.
|
|
236
|
+
*/
|
|
237
|
+
export declare function servingReport(env?: NodeJS.ProcessEnv): DoctorServing | undefined;
|
|
169
238
|
export declare const CONSOLE_URLS: Record<ProviderId, string>;
|
|
170
239
|
/**
|
|
171
240
|
* Every syndicate file the loader can see: the root (this deployment's
|
|
@@ -194,6 +263,13 @@ export declare function runDoctor(options?: {
|
|
|
194
263
|
agentsDir?: string;
|
|
195
264
|
scripts?: Record<string, string>;
|
|
196
265
|
}): DoctorResult;
|
|
266
|
+
/**
|
|
267
|
+
* Why `melchizedek-doctor --check` exits non-zero, one line each; empty when
|
|
268
|
+
* it passes. A blocked syndicate, a runtime problem, the OAuth setup's and
|
|
269
|
+
* the credential allowlist's problems, and each grant whose hosts the OAuth
|
|
270
|
+
* host allowlist refuses (ADR 0114). Names and hosts only, never a value.
|
|
271
|
+
*/
|
|
272
|
+
export declare function checkProblems(result: DoctorResult): string[];
|
|
197
273
|
export declare function renderDoctor(result: DoctorResult, opts?: {
|
|
198
274
|
color?: boolean;
|
|
199
275
|
}): string;
|
package/dist/lib/doctor.js
CHANGED
|
@@ -38,6 +38,9 @@ import { agentOAuthGrants, oauthEnvNames } from './tools/oauthTools.js';
|
|
|
38
38
|
import { grantHostProblems, OAUTH_REDIRECT_URI_ENV, oauthEnvProblems } from './a2a/oauthSetup.js';
|
|
39
39
|
import { CREDENTIAL_KEY_ENV } from './tools/credentialCipher.js';
|
|
40
40
|
import { OAUTH_HOSTS_ENV } from './tools/oauthHosts.js';
|
|
41
|
+
import { CREDENTIAL_HOSTS_ENV, credentialHosts } from './tools/credentialHosts.js';
|
|
42
|
+
import { syndicateCredentialUses } from './tools/credentialUses.js';
|
|
43
|
+
import { credentialHostEnvProblems, unboundCredentialWarning } from './a2a/oauthSetup.js';
|
|
41
44
|
function resolvable(module) {
|
|
42
45
|
try {
|
|
43
46
|
createRequire(import.meta.url).resolve(module);
|
|
@@ -100,6 +103,69 @@ export function endpointRows() {
|
|
|
100
103
|
}
|
|
101
104
|
return rows;
|
|
102
105
|
}
|
|
106
|
+
/** One status per provider, probed with an id of its own prefix (the providers line). */
|
|
107
|
+
export function providerPaths() {
|
|
108
|
+
return Object.keys(PROVIDERS).map((p) => {
|
|
109
|
+
const r = describeCapabilities(PROBE_ID[p]);
|
|
110
|
+
return {
|
|
111
|
+
provider: p,
|
|
112
|
+
label: PROVIDERS[p].label,
|
|
113
|
+
funded: r.funded,
|
|
114
|
+
transport: r.transport,
|
|
115
|
+
...(r.gateway ? { gateway: r.gateway } : {}),
|
|
116
|
+
platform: p === 'ollama' ? 'local' : (r.platform ?? 'direct'),
|
|
117
|
+
keyEnv: r.keyEnv,
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/** The serving variables the doctor reports by presence. */
|
|
122
|
+
export const SERVING_ENV = [
|
|
123
|
+
'A2A_AUTH',
|
|
124
|
+
'A2A_SERVER_SECRET',
|
|
125
|
+
'A2A_CALLERS',
|
|
126
|
+
'A2A_JWT_ISSUER',
|
|
127
|
+
'A2A_JWT_AUDIENCE',
|
|
128
|
+
'A2A_JWT_JWKS_URL',
|
|
129
|
+
'A2A_JWT_SECRET',
|
|
130
|
+
'A2A_TRUSTED_USER_HEADER',
|
|
131
|
+
'A2A_KEY_MODE',
|
|
132
|
+
'PUBLIC_URL',
|
|
133
|
+
];
|
|
134
|
+
/**
|
|
135
|
+
* How the A2A server would authenticate callers and who would pay, from the
|
|
136
|
+
* environment; undefined when none of its variables is set. The checks are
|
|
137
|
+
* the ones scripts/a2a_server.ts stops on, reported by name.
|
|
138
|
+
*/
|
|
139
|
+
export function servingReport(env = process.env) {
|
|
140
|
+
const has = (name) => !!env[name]?.trim();
|
|
141
|
+
const set = Object.fromEntries(SERVING_ENV.map((name) => [name, has(name)]));
|
|
142
|
+
if (!Object.values(set).some(Boolean))
|
|
143
|
+
return undefined;
|
|
144
|
+
// A value outside the known modes is reported as `invalid`, never echoed:
|
|
145
|
+
// a secret pasted into the wrong variable must not reach the terminal.
|
|
146
|
+
const rawAuth = (env.A2A_AUTH ?? '').trim().toLowerCase() || 'secret';
|
|
147
|
+
const rawKeyMode = (env.A2A_KEY_MODE ?? '').trim().toLowerCase() || 'server';
|
|
148
|
+
const auth = ['secret', 'callers', 'jwt', 'header'].includes(rawAuth) ? rawAuth : 'invalid';
|
|
149
|
+
const keyMode = rawKeyMode === 'server' || rawKeyMode === 'byok' ? rawKeyMode : 'invalid';
|
|
150
|
+
const problems = [];
|
|
151
|
+
if (auth === 'invalid')
|
|
152
|
+
problems.push('A2A_AUTH must be secret, callers, jwt or header');
|
|
153
|
+
if (auth === 'callers' && !has('A2A_CALLERS'))
|
|
154
|
+
problems.push('A2A_AUTH=callers needs A2A_CALLERS');
|
|
155
|
+
if (auth === 'jwt' && (!has('A2A_JWT_ISSUER') || !has('A2A_JWT_AUDIENCE')))
|
|
156
|
+
problems.push('A2A_AUTH=jwt needs A2A_JWT_ISSUER and A2A_JWT_AUDIENCE');
|
|
157
|
+
if (auth === 'jwt' && !has('A2A_JWT_JWKS_URL') && !has('A2A_JWT_SECRET'))
|
|
158
|
+
problems.push('A2A_AUTH=jwt needs A2A_JWT_JWKS_URL or A2A_JWT_SECRET');
|
|
159
|
+
if (auth === 'header' && (!has('A2A_TRUSTED_USER_HEADER') || !has('A2A_SERVER_SECRET')))
|
|
160
|
+
problems.push('A2A_AUTH=header needs A2A_TRUSTED_USER_HEADER and A2A_SERVER_SECRET');
|
|
161
|
+
if (keyMode !== 'server' && keyMode !== 'byok')
|
|
162
|
+
problems.push("A2A_KEY_MODE must be 'server' or 'byok'");
|
|
163
|
+
if (has('PUBLIC_URL') && !has('A2A_AUTH'))
|
|
164
|
+
problems.push('PUBLIC_URL is set, so A2A_AUTH must be set explicitly (with A2A_SERVED_AGENTS and A2A_TRUST_PROXY)');
|
|
165
|
+
if (auth === 'secret' && !has('A2A_SERVER_SECRET') && keyMode === 'byok')
|
|
166
|
+
problems.push('A2A_KEY_MODE=byok without A2A_SERVER_SECRET (or caller tokens) serves unauthenticated, loopback only');
|
|
167
|
+
return { auth, authDeclared: has('A2A_AUTH'), keyMode, set, problems };
|
|
168
|
+
}
|
|
103
169
|
// ── Where to get a key ───────────────────────────────────────────────────────
|
|
104
170
|
export const CONSOLE_URLS = {
|
|
105
171
|
gemini: 'https://aistudio.google.com',
|
|
@@ -305,6 +371,17 @@ export function diagnoseSyndicate(file, agentsDir, scripts) {
|
|
|
305
371
|
const nestedErrors = [];
|
|
306
372
|
const grants = [];
|
|
307
373
|
walk(config, '', { load, seen: new Set([file, path.basename(file)]), nestedErrors, grants }, rows);
|
|
374
|
+
// Static credentials and the credential host allowlist (ADR 0122); a nested file that cannot load is reported above.
|
|
375
|
+
const loadQuiet = (ref) => {
|
|
376
|
+
try {
|
|
377
|
+
return load(ref);
|
|
378
|
+
}
|
|
379
|
+
catch {
|
|
380
|
+
return {};
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
const credentialEnvs = [...new Set(syndicateCredentialUses([config], loadQuiet).map((u) => u.env))].sort();
|
|
384
|
+
const credentials = credentialEnvs.length ? { envs: credentialEnvs, problems: credentialHostEnvProblems([config], process.env, loadQuiet) } : undefined;
|
|
308
385
|
const memory = config.memory_system === 'long-term'
|
|
309
386
|
? {
|
|
310
387
|
namespace: config.memory_namespace ?? LEGACY_MEMORY_APP_NAME,
|
|
@@ -324,6 +401,7 @@ export function diagnoseSyndicate(file, agentsDir, scripts) {
|
|
|
324
401
|
...(nestedErrors.length ? { error: nestedErrors.join('\n') } : {}),
|
|
325
402
|
...(memory ? { memory } : {}),
|
|
326
403
|
...(grants.length ? { grants } : {}),
|
|
404
|
+
...(credentials ? { credentials } : {}),
|
|
327
405
|
};
|
|
328
406
|
}
|
|
329
407
|
catch (err) {
|
|
@@ -405,8 +483,9 @@ export function runDoctor(options = {}) {
|
|
|
405
483
|
const rawGateway = (process.env[GATEWAY_ENV] ?? '').trim();
|
|
406
484
|
const gateway = rawGateway
|
|
407
485
|
? {
|
|
408
|
-
|
|
409
|
-
|
|
486
|
+
// An unrecognised value is never echoed: it may be a key pasted into the wrong variable.
|
|
487
|
+
id: cfg?.gateway.id ?? 'unrecognised',
|
|
488
|
+
label: cfg?.gateway.label ?? 'an unrecognised gateway',
|
|
410
489
|
usable: gatewayUsable(),
|
|
411
490
|
...(gatewayProblem() ? { problem: gatewayProblem() } : {}),
|
|
412
491
|
}
|
|
@@ -424,7 +503,39 @@ export function runDoctor(options = {}) {
|
|
|
424
503
|
const grants = syndicates.flatMap((s) => s.grants ?? []).map((g) => ({ agent: g.agent, tools: g.tools, oauth2: { provider: g.provider, grant: g.grant, token_url: '' } }));
|
|
425
504
|
const set = Object.fromEntries([CREDENTIAL_KEY_ENV, OAUTH_REDIRECT_URI_ENV, OAUTH_HOSTS_ENV].map((name) => [name, !!process.env[name]?.trim()]));
|
|
426
505
|
const oauth = grants.length || Object.values(set).some(Boolean) ? { set, problems: oauthEnvProblems(grants) } : undefined;
|
|
427
|
-
|
|
506
|
+
// The credential host allowlist (ADR 0122): unbound variables named, refusals listed, names and hosts only.
|
|
507
|
+
const credentialSet = !!process.env[CREDENTIAL_HOSTS_ENV]?.trim();
|
|
508
|
+
const envs = [...new Set(syndicates.flatMap((s) => s.credentials?.envs ?? []))].sort();
|
|
509
|
+
let credentials;
|
|
510
|
+
if (envs.length || credentialSet) {
|
|
511
|
+
const problems = [];
|
|
512
|
+
try {
|
|
513
|
+
credentialHosts();
|
|
514
|
+
}
|
|
515
|
+
catch (err) {
|
|
516
|
+
problems.push(err instanceof Error ? err.message : `${CREDENTIAL_HOSTS_ENV} is malformed`);
|
|
517
|
+
}
|
|
518
|
+
if (!problems.length)
|
|
519
|
+
for (const s of syndicates)
|
|
520
|
+
for (const p of s.credentials?.problems ?? [])
|
|
521
|
+
problems.push(`${s.file}: ${p}`);
|
|
522
|
+
const warning = unboundCredentialWarning(envs);
|
|
523
|
+
credentials = { set: credentialSet, envs, ...(warning ? { warning } : {}), problems };
|
|
524
|
+
}
|
|
525
|
+
const serving = servingReport();
|
|
526
|
+
return {
|
|
527
|
+
agentsDir,
|
|
528
|
+
runtime: runtimeReport(),
|
|
529
|
+
syndicates,
|
|
530
|
+
unlocks,
|
|
531
|
+
gateway,
|
|
532
|
+
endpoints: endpointRows(),
|
|
533
|
+
counts,
|
|
534
|
+
...(oauth ? { oauth } : {}),
|
|
535
|
+
...(credentials ? { credentials } : {}),
|
|
536
|
+
providers: providerPaths(),
|
|
537
|
+
...(serving ? { serving } : {}),
|
|
538
|
+
};
|
|
428
539
|
}
|
|
429
540
|
// ── Rendering ────────────────────────────────────────────────────────────────
|
|
430
541
|
const ANSI = {
|
|
@@ -445,6 +556,29 @@ const PROBE_ID = {
|
|
|
445
556
|
moonshot: 'kimi-x',
|
|
446
557
|
ollama: 'ollama/x',
|
|
447
558
|
};
|
|
559
|
+
/**
|
|
560
|
+
* Why `melchizedek-doctor --check` exits non-zero, one line each; empty when
|
|
561
|
+
* it passes. A blocked syndicate, a runtime problem, the OAuth setup's and
|
|
562
|
+
* the credential allowlist's problems, and each grant whose hosts the OAuth
|
|
563
|
+
* host allowlist refuses (ADR 0114). Names and hosts only, never a value.
|
|
564
|
+
*/
|
|
565
|
+
export function checkProblems(result) {
|
|
566
|
+
const problems = [];
|
|
567
|
+
if (result.counts.blocked > 0)
|
|
568
|
+
problems.push(`${result.counts.blocked} syndicate(s) blocked`);
|
|
569
|
+
if (result.runtime.problem)
|
|
570
|
+
problems.push(result.runtime.problem);
|
|
571
|
+
for (const p of result.oauth?.problems ?? [])
|
|
572
|
+
problems.push(`oauth: ${p}`);
|
|
573
|
+
for (const p of result.credentials?.problems ?? [])
|
|
574
|
+
problems.push(`credentials: ${p}`);
|
|
575
|
+
for (const s of result.syndicates) {
|
|
576
|
+
for (const g of s.grants ?? [])
|
|
577
|
+
for (const p of g.hostProblems ?? [])
|
|
578
|
+
problems.push(`${s.file}: ${g.agent} · ${g.provider}: ${p}`);
|
|
579
|
+
}
|
|
580
|
+
return problems;
|
|
581
|
+
}
|
|
448
582
|
function pad(s, n) {
|
|
449
583
|
return s.length >= n ? s.slice(0, n - 1) + '…' : s.padEnd(n);
|
|
450
584
|
}
|
|
@@ -471,11 +605,10 @@ export function renderDoctor(result, opts = {}) {
|
|
|
471
605
|
// Provider line: what is funded, and how. Each provider is probed with an
|
|
472
606
|
// id of its own prefix, so the line names ITS key (a fallthrough once put
|
|
473
607
|
// xAI's key beside Moonshot).
|
|
474
|
-
const providerBits =
|
|
475
|
-
const r = describeCapabilities(PROBE_ID[p]);
|
|
608
|
+
const providerBits = (result.providers ?? providerPaths()).map((r) => {
|
|
476
609
|
const mark = !r.funded ? `${c.red}✗${c.reset}` : r.transport === 'gateway' ? `${c.yellow}◇${c.reset}` : `${c.green}✓${c.reset}`;
|
|
477
|
-
const how = !r.funded ? `${c.dim}${r.keyEnv} not set${c.reset}` : r.transport === 'gateway' ? `${c.dim}via gateway:${r.gateway}${c.reset}` :
|
|
478
|
-
return `${mark} ${
|
|
610
|
+
const how = !r.funded ? `${c.dim}${r.keyEnv} not set${c.reset}` : r.transport === 'gateway' ? `${c.dim}via gateway:${r.gateway}${c.reset}` : r.platform === 'local' ? `${c.dim}local${c.reset}` : `${c.dim}${r.platform}${c.reset}`;
|
|
611
|
+
return `${mark} ${r.label} ${how}`;
|
|
479
612
|
});
|
|
480
613
|
lines.push('providers ' + providerBits.join(' '));
|
|
481
614
|
for (const e of result.endpoints ?? []) {
|
|
@@ -490,6 +623,15 @@ export function renderDoctor(result, opts = {}) {
|
|
|
490
623
|
? `gateway ${c.red}✗${c.reset} ${result.gateway.problem}`
|
|
491
624
|
: `gateway ${c.yellow}◇${c.reset} ${result.gateway.label} fills in for any provider whose direct key is absent ${c.dim}(native search is lost on that path)${c.reset}`);
|
|
492
625
|
}
|
|
626
|
+
if (result.serving) {
|
|
627
|
+
// Names only: the A2A identity mode, who pays, and what the server would refuse.
|
|
628
|
+
const sv = result.serving;
|
|
629
|
+
const mark = sv.problems.length ? `${c.red}✗${c.reset}` : `${c.green}✓${c.reset}`;
|
|
630
|
+
const set = Object.entries(sv.set).filter(([, on]) => on).map(([name]) => name).join(', ');
|
|
631
|
+
lines.push(`serving ${mark} A2A_AUTH=${sv.auth}${sv.authDeclared ? '' : ' (default)'} · A2A_KEY_MODE=${sv.keyMode} ${c.dim}· set: ${set || 'none'}${c.reset}`);
|
|
632
|
+
for (const p of sv.problems)
|
|
633
|
+
lines.push(` ${c.red}✗ ${p}${c.reset}`);
|
|
634
|
+
}
|
|
493
635
|
if (result.oauth) {
|
|
494
636
|
// Names only: which variables are set, never their values (ADR 0114).
|
|
495
637
|
const set = Object.entries(result.oauth.set).map(([name, on]) => `${name} ${on ? 'set' : 'unset'}`).join(' · ');
|
|
@@ -498,6 +640,16 @@ export function renderDoctor(result, opts = {}) {
|
|
|
498
640
|
for (const p of result.oauth.problems)
|
|
499
641
|
lines.push(` ${c.red}✗ ${p}${c.reset}`);
|
|
500
642
|
}
|
|
643
|
+
if (result.credentials) {
|
|
644
|
+
// Names only: which variables are sent, never their values (ADR 0122).
|
|
645
|
+
const cr = result.credentials;
|
|
646
|
+
const mark = cr.problems.length ? `${c.red}✗${c.reset}` : cr.warning ? `${c.yellow}⚠${c.reset}` : `${c.green}✓${c.reset}`;
|
|
647
|
+
lines.push(`credentials ${mark} ${c.dim}${CREDENTIAL_HOSTS_ENV} ${cr.set ? 'set' : 'unset'}${cr.envs.length ? ` · sent: ${cr.envs.join(', ')}` : ''}${c.reset}`);
|
|
648
|
+
if (cr.warning)
|
|
649
|
+
lines.push(` ${c.yellow}⚠ ${cr.warning}${c.reset}`);
|
|
650
|
+
for (const p of cr.problems)
|
|
651
|
+
lines.push(` ${c.red}✗ ${p}${c.reset}`);
|
|
652
|
+
}
|
|
501
653
|
lines.push('');
|
|
502
654
|
// The table: one line per syndicate (name, verdict, run command), then
|
|
503
655
|
// one indented line per agent.
|
|
@@ -129,6 +129,15 @@ export interface AgentYamlConfig {
|
|
|
129
129
|
schema?: Record<string, unknown>;
|
|
130
130
|
mime?: 'application/json' | 'text/plain';
|
|
131
131
|
};
|
|
132
|
+
/**
|
|
133
|
+
* Which tools the model may call (v2, ADR 0117): `auto`, `none`,
|
|
134
|
+
* `required`, or `{ name }` for one tool. Replaces
|
|
135
|
+
* generateContentConfig.toolConfig.functionCallingConfig; folded into it
|
|
136
|
+
* by the loader (lib/agentDialect.ts toEngineAgent).
|
|
137
|
+
*/
|
|
138
|
+
tool_choice?: 'auto' | 'none' | 'required' | {
|
|
139
|
+
name: string;
|
|
140
|
+
};
|
|
132
141
|
/**
|
|
133
142
|
* Per-provider instruction (v2), keyed by provider id (lib/models/providerMap.ts):
|
|
134
143
|
* `instruction` replaces the agent's instruction, `instruction_append` is
|
|
@@ -74,6 +74,15 @@ export declare function capabilityOf(model: string, capability: Capability, opts
|
|
|
74
74
|
}): CapabilityCell & {
|
|
75
75
|
row: MatrixRow;
|
|
76
76
|
};
|
|
77
|
+
/**
|
|
78
|
+
* The reasoning levels above `high` (ADR 0117), per row: what `reasoning:
|
|
79
|
+
* xhigh` and `reasoning: max` reach the provider as. A level a model lacks
|
|
80
|
+
* goes as its highest setting, and the request's `llm.request` span carries
|
|
81
|
+
* `llm.reasoning.weakened` with the level asked for. Asserted against the
|
|
82
|
+
* request bodies in tests/toolChoiceEffort.test.ts; the direct adapters read
|
|
83
|
+
* lib/models/reasoning.ts effortCeiling.
|
|
84
|
+
*/
|
|
85
|
+
export declare const REASONING_ABOVE_HIGH: Readonly<Record<MatrixRow, string>>;
|
|
77
86
|
/**
|
|
78
87
|
* Whether an outputSchema goes in the same request as the agent's tools, as
|
|
79
88
|
* the provider's own structured-output field, on the path `model` takes
|