melchizedek-agents 0.18.0 → 0.20.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 +12 -0
- package/CHANGELOG.md +1001 -0
- package/DOCUMENTATION.md +252 -65
- package/QUICKSTART.md +9 -6
- package/README.md +42 -4
- package/config/agents/examples/harness.yaml +3 -1
- package/config/agents/examples/model_zoo.yaml +10 -9
- package/config/agents/syndicateSchema.yaml +53 -16
- package/config/agents/templates/account_memory.yaml +2 -6
- package/config/agents/templates/case_desk.yaml +3 -9
- package/config/agents/templates/draft_review.yaml +3 -9
- package/config/agents/templates/intake_extractor.yaml +1 -3
- package/config/agents/templates/research_brief.yaml +3 -9
- package/config/agents/templates/research_desk.yaml +3 -9
- package/config/agents/templates/review_panel.yaml +4 -12
- package/config/agents/templates/support_triage.yaml +4 -12
- package/config/agents/templates/systems_operator.yaml +2 -6
- package/db/hardening.sql +7 -1
- package/db/migrations/0013_tool_credentials.sql +260 -0
- package/dist/lib/a2a/app.d.ts +70 -6
- package/dist/lib/a2a/app.js +107 -2
- package/dist/lib/a2a/executor.d.ts +20 -3
- package/dist/lib/a2a/executor.js +66 -7
- package/dist/lib/a2a/remoteAgent.d.ts +24 -9
- package/dist/lib/a2a/remoteAgent.js +31 -14
- package/dist/lib/adkPeer.d.ts +63 -0
- package/dist/lib/adkPeer.js +240 -0
- package/dist/lib/compile.d.ts +159 -30
- package/dist/lib/compile.js +215 -134
- package/dist/lib/compileAdk.d.ts +32 -0
- package/dist/lib/compileAdk.js +142 -0
- package/dist/lib/compileNative.d.ts +111 -0
- package/dist/lib/compileNative.js +278 -0
- package/dist/lib/config.d.ts +6 -5
- package/dist/lib/config.js +6 -5
- package/dist/lib/doctor.d.ts +28 -1
- package/dist/lib/doctor.js +64 -2
- package/dist/lib/index.d.ts +6 -1
- package/dist/lib/index.js +7 -1
- package/dist/lib/loadSyndicate.d.ts +18 -0
- package/dist/lib/memory/erase.d.ts +5 -3
- package/dist/lib/memory/erase.js +5 -2
- package/dist/lib/memory/namespace.d.ts +8 -1
- package/dist/lib/memory/namespace.js +12 -6
- package/dist/lib/memory/providers.js +2 -1
- package/dist/lib/memory/supabaseMemoryService.d.ts +37 -3
- package/dist/lib/memory/supabaseMemoryService.js +42 -2
- package/dist/lib/model.d.ts +47 -0
- package/dist/lib/model.js +37 -0
- package/dist/lib/models/adapterResolver.d.ts +115 -0
- package/dist/lib/models/adapterResolver.js +137 -0
- package/dist/lib/models/adkGeminiAdapter.d.ts +90 -0
- package/dist/lib/models/adkGeminiAdapter.js +369 -0
- package/dist/lib/models/adkShim.d.ts +111 -0
- package/dist/lib/models/adkShim.js +136 -0
- package/dist/lib/models/capabilities.d.ts +3 -0
- package/dist/lib/models/capabilities.js +33 -23
- package/dist/lib/models/chatCompletionsAdapter.d.ts +279 -0
- package/dist/lib/models/chatCompletionsAdapter.js +854 -0
- package/dist/lib/models/circuitBreaker.d.ts +42 -0
- package/dist/lib/models/circuitBreaker.js +70 -0
- package/dist/lib/models/claudeAdapter.d.ts +103 -0
- package/dist/lib/models/claudeAdapter.js +669 -0
- package/dist/lib/models/claudeLlm.d.ts +39 -62
- package/dist/lib/models/claudeLlm.js +45 -350
- package/dist/lib/models/claudeModels.d.ts +127 -0
- package/dist/lib/models/claudeModels.js +237 -0
- package/dist/lib/models/contract.d.ts +395 -0
- package/dist/lib/models/contract.js +39 -0
- package/dist/lib/models/endpoints.js +2 -1
- package/dist/lib/models/errorResponse.d.ts +50 -0
- package/dist/lib/models/errorResponse.js +48 -0
- package/dist/lib/models/fallback.d.ts +18 -11
- package/dist/lib/models/fallback.js +54 -43
- package/dist/lib/models/fallbackAdapter.d.ts +55 -0
- package/dist/lib/models/fallbackAdapter.js +62 -0
- package/dist/lib/models/gateway.js +2 -1
- package/dist/lib/models/gatewayAdapter.d.ts +51 -0
- package/dist/lib/models/gatewayAdapter.js +81 -0
- package/dist/lib/models/gatewayLlm.d.ts +12 -33
- package/dist/lib/models/gatewayLlm.js +16 -77
- package/dist/lib/models/geminiAdapter.d.ts +125 -0
- package/dist/lib/models/geminiAdapter.js +854 -0
- package/dist/lib/models/geminiState.d.ts +46 -0
- package/dist/lib/models/geminiState.js +51 -0
- package/dist/lib/models/genaiMapping.d.ts +283 -0
- package/dist/lib/models/genaiMapping.js +965 -0
- package/dist/lib/models/gptAdapter.d.ts +240 -0
- package/dist/lib/models/gptAdapter.js +804 -0
- package/dist/lib/models/gptLlm.d.ts +63 -123
- package/dist/lib/models/gptLlm.js +89 -603
- package/dist/lib/models/grokAdapter.d.ts +86 -0
- package/dist/lib/models/grokAdapter.js +142 -0
- package/dist/lib/models/grokLlm.d.ts +22 -45
- package/dist/lib/models/grokLlm.js +24 -69
- package/dist/lib/models/kimiAdapter.d.ts +157 -0
- package/dist/lib/models/kimiAdapter.js +203 -0
- package/dist/lib/models/kimiLlm.d.ts +15 -107
- package/dist/lib/models/kimiLlm.js +19 -155
- package/dist/lib/models/ollamaAdapter.d.ts +125 -0
- package/dist/lib/models/ollamaAdapter.js +155 -0
- package/dist/lib/models/ollamaLlm.d.ts +11 -107
- package/dist/lib/models/ollamaLlm.js +15 -153
- package/dist/lib/models/openAiCompatibleLlm.d.ts +73 -216
- package/dist/lib/models/openAiCompatibleLlm.js +109 -671
- package/dist/lib/models/providerState.d.ts +63 -0
- package/dist/lib/models/providerState.js +61 -0
- package/dist/lib/models/reasoning.d.ts +26 -0
- package/dist/lib/models/reasoning.js +59 -0
- package/dist/lib/models/registry.d.ts +62 -35
- package/dist/lib/models/registry.js +100 -124
- package/dist/lib/models/retry.d.ts +2 -1
- package/dist/lib/models/retry.js +3 -2
- package/dist/lib/models/schemaNormalize.d.ts +98 -0
- package/dist/lib/models/schemaNormalize.js +321 -1
- package/dist/lib/models/tracedGemini.d.ts +41 -0
- package/dist/lib/models/tracedGemini.js +98 -0
- package/dist/lib/models/urls.d.ts +11 -0
- package/dist/lib/models/urls.js +16 -0
- package/dist/lib/net/redirects.d.ts +21 -2
- package/dist/lib/net/redirects.js +11 -34
- package/dist/lib/observability/audit.d.ts +5 -1
- package/dist/lib/observability/lineage.d.ts +14 -0
- package/dist/lib/observability/lineage.js +33 -0
- package/dist/lib/observability/supabaseSpanExporter.d.ts +10 -4
- package/dist/lib/observability/supabaseSpanExporter.js +17 -9
- package/dist/lib/observability/tracer.d.ts +26 -8
- package/dist/lib/observability/tracer.js +43 -18
- package/dist/lib/runtime/adkMemoryBridge.d.ts +46 -0
- package/dist/lib/runtime/adkMemoryBridge.js +68 -0
- package/dist/lib/runtime/adkSessionBridge.d.ts +78 -0
- package/dist/lib/runtime/adkSessionBridge.js +176 -0
- package/dist/lib/runtime/approvals.d.ts +5 -0
- package/dist/lib/runtime/approvals.js +39 -8
- package/dist/lib/runtime/credentials.d.ts +61 -0
- package/dist/lib/runtime/credentials.js +67 -0
- package/dist/lib/runtime/events.d.ts +229 -0
- package/dist/lib/runtime/events.js +274 -0
- package/dist/lib/runtime/logging.d.ts +32 -0
- package/dist/lib/runtime/logging.js +55 -0
- package/dist/lib/runtime/memoryService.d.ts +72 -0
- package/dist/lib/runtime/memoryService.js +25 -0
- package/dist/lib/runtime/native/agentLoop.d.ts +209 -0
- package/dist/lib/runtime/native/agentLoop.js +705 -0
- package/dist/lib/runtime/native/compaction.d.ts +103 -0
- package/dist/lib/runtime/native/compaction.js +191 -0
- package/dist/lib/runtime/native/delegate.d.ts +151 -0
- package/dist/lib/runtime/native/delegate.js +256 -0
- package/dist/lib/runtime/native/history.d.ts +83 -0
- package/dist/lib/runtime/native/history.js +312 -0
- package/dist/lib/runtime/native/interrupts.d.ts +129 -0
- package/dist/lib/runtime/native/interrupts.js +310 -0
- package/dist/lib/runtime/native/request.d.ts +215 -0
- package/dist/lib/runtime/native/request.js +585 -0
- package/dist/lib/runtime/native/selfCorrection.d.ts +164 -0
- package/dist/lib/runtime/native/selfCorrection.js +327 -0
- package/dist/lib/runtime/native/step.d.ts +156 -0
- package/dist/lib/runtime/native/step.js +263 -0
- package/dist/lib/runtime/native/taskMode.d.ts +64 -0
- package/dist/lib/runtime/native/taskMode.js +126 -0
- package/dist/lib/runtime/native/telemetry.d.ts +75 -0
- package/dist/lib/runtime/native/telemetry.js +299 -0
- package/dist/lib/runtime/native/tempState.d.ts +31 -0
- package/dist/lib/runtime/native/tempState.js +56 -0
- package/dist/lib/runtime/nativeTurn.d.ts +86 -0
- package/dist/lib/runtime/nativeTurn.js +107 -0
- package/dist/lib/runtime/questions.d.ts +29 -7
- package/dist/lib/runtime/questions.js +47 -32
- package/dist/lib/runtime/runtimeFlag.d.ts +38 -0
- package/dist/lib/runtime/runtimeFlag.js +58 -0
- package/dist/lib/runtime/sessions.d.ts +166 -0
- package/dist/lib/runtime/sessions.js +201 -0
- package/dist/lib/runtime/syndicateTurn.d.ts +46 -5
- package/dist/lib/runtime/syndicateTurn.js +233 -36
- package/dist/lib/runtime/valueDepth.d.ts +23 -0
- package/dist/lib/runtime/valueDepth.js +41 -0
- package/dist/lib/session/supabaseSessionService.d.ts +65 -18
- package/dist/lib/session/supabaseSessionService.js +148 -105
- package/dist/lib/session/transcript.d.ts +69 -23
- package/dist/lib/session/transcript.js +105 -29
- package/dist/lib/skills.js +33 -7
- package/dist/lib/storage/postgres/credentialStore.d.ts +20 -0
- package/dist/lib/storage/postgres/credentialStore.js +82 -0
- package/dist/lib/storage/postgres/index.d.ts +12 -0
- package/dist/lib/storage/postgres/index.js +6 -1
- package/dist/lib/storage/postgres/sessionService.d.ts +44 -16
- package/dist/lib/storage/postgres/sessionService.js +128 -66
- package/dist/lib/syndicateSchema.d.ts +49 -0
- package/dist/lib/syndicateSchema.js +99 -9
- package/dist/lib/toolRegistry.d.ts +26 -2
- package/dist/lib/toolRegistry.js +73 -17
- package/dist/lib/tools/adkTool.d.ts +81 -0
- package/dist/lib/tools/adkTool.js +268 -0
- package/dist/lib/tools/auth.d.ts +118 -0
- package/dist/lib/tools/auth.js +89 -0
- package/dist/lib/tools/collectionsSearchTool.d.ts +8 -7
- package/dist/lib/tools/collectionsSearchTool.js +12 -27
- package/dist/lib/tools/credentialCipher.d.ts +58 -0
- package/dist/lib/tools/credentialCipher.js +116 -0
- package/dist/lib/tools/credentialStore.d.ts +77 -0
- package/dist/lib/tools/credentialStore.js +246 -0
- package/dist/lib/tools/examples.d.ts +43 -0
- package/dist/lib/tools/examples.js +62 -0
- package/dist/lib/tools/generateImageTool.d.ts +15 -6
- package/dist/lib/tools/generateImageTool.js +19 -29
- package/dist/lib/tools/inspectImageTool.d.ts +9 -6
- package/dist/lib/tools/inspectImageTool.js +13 -17
- package/dist/lib/tools/mcpToolFactory.d.ts +42 -1
- package/dist/lib/tools/mcpToolFactory.js +46 -47
- package/dist/lib/tools/memoryTools.d.ts +61 -0
- package/dist/lib/tools/memoryTools.js +119 -0
- package/dist/lib/tools/nativeTools.d.ts +38 -0
- package/dist/lib/tools/nativeTools.js +43 -0
- package/dist/lib/tools/oauthConsent.d.ts +181 -0
- package/dist/lib/tools/oauthConsent.js +330 -0
- package/dist/lib/tools/openapi/call.d.ts +108 -0
- package/dist/lib/tools/openapi/call.js +295 -0
- package/dist/lib/tools/openapi/parse.d.ts +147 -0
- package/dist/lib/tools/openapi/parse.js +570 -0
- package/dist/lib/tools/openapiTools.d.ts +25 -17
- package/dist/lib/tools/openapiTools.js +124 -121
- package/dist/lib/tools/scienceTools.d.ts +14 -14
- package/dist/lib/tools/skillToolset.d.ts +58 -87
- package/dist/lib/tools/skillToolset.js +61 -142
- package/dist/lib/tools/skills/env.d.ts +58 -0
- package/dist/lib/tools/skills/env.js +165 -0
- package/dist/lib/tools/skills/executor.d.ts +114 -0
- package/dist/lib/tools/skills/executor.js +331 -0
- package/dist/lib/tools/skills/frontmatter.d.ts +79 -0
- package/dist/lib/tools/skills/frontmatter.js +215 -0
- package/dist/lib/tools/skills/loader.d.ts +91 -0
- package/dist/lib/tools/skills/loader.js +257 -0
- package/dist/lib/tools/skills/tools.d.ts +187 -0
- package/dist/lib/tools/skills/tools.js +386 -0
- package/dist/lib/tools/taskTools.d.ts +15 -15
- package/dist/lib/tools/tool.d.ts +354 -0
- package/dist/lib/tools/tool.js +304 -0
- package/dist/lib/tools/toolContract.d.ts +89 -33
- package/dist/lib/tools/toolContract.js +104 -82
- package/dist/lib/tools/urlContextTool.d.ts +4 -1
- package/dist/lib/tools/urlContextTool.js +6 -2
- package/dist/lib/tools/webExtractTool.d.ts +3 -3
- package/dist/lib/tools/webExtractTool.js +2 -1
- package/dist/lib/tools/webSearchTool.d.ts +7 -10
- package/dist/lib/tools/webSearchTool.js +15 -60
- package/dist/lib/tools/wikiTools.d.ts +19 -19
- package/dist/lib/tools/wikiTools.js +13 -9
- package/dist/lib/tools/xApiSearchTool.d.ts +3 -3
- package/dist/lib/tools/xApiSearchTool.js +2 -1
- package/dist/lib/tools/xSearchTool.d.ts +8 -10
- package/dist/lib/tools/xSearchTool.js +12 -63
- package/dist/lib/tools/xaiSearchParams.d.ts +41 -0
- package/dist/lib/tools/xaiSearchParams.js +126 -0
- package/dist/lib/wiki/agentRun.d.ts +12 -0
- package/dist/lib/wiki/agentRun.js +74 -35
- package/dist/lib/wiki/entities.d.ts +12 -0
- package/dist/lib/wiki/entities.js +45 -3
- package/dist/lib/workflow/agentNode.d.ts +153 -0
- package/dist/lib/workflow/agentNode.js +365 -0
- package/dist/lib/workflow/graph.d.ts +178 -0
- package/dist/lib/workflow/graph.js +468 -0
- package/dist/lib/workflow/nodeEvents.d.ts +50 -0
- package/dist/lib/workflow/nodeEvents.js +101 -0
- package/dist/lib/workflow/pause.d.ts +122 -0
- package/dist/lib/workflow/pause.js +180 -0
- package/dist/lib/workflow/resume.d.ts +166 -0
- package/dist/lib/workflow/resume.js +441 -0
- package/dist/lib/workflow/route.d.ts +58 -0
- package/dist/lib/workflow/route.js +69 -0
- package/dist/lib/workflow/scheduler.d.ts +418 -0
- package/dist/lib/workflow/scheduler.js +756 -0
- package/dist/lib/workflow/toolNode.d.ts +127 -0
- package/dist/lib/workflow/toolNode.js +236 -0
- package/dist/lib/workflow/turn.d.ts +118 -0
- package/dist/lib/workflow/turn.js +216 -0
- package/dist/lib/workflow.d.ts +33 -11
- package/dist/lib/workflow.js +49 -27
- package/dist/lib/workflowConfig.d.ts +7 -2
- package/dist/lib/workflowConfig.js +7 -12
- package/dist/scripts/a2a_server.js +4 -3
- package/dist/scripts/assistant_worker.d.ts +2 -1
- package/dist/scripts/assistant_worker.js +9 -4
- package/dist/scripts/doctor.d.ts +3 -1
- package/dist/scripts/doctor.js +4 -2
- package/dist/scripts/syndicate_chat.js +19 -10
- package/package.json +9 -3
- package/skills/briefs/melchizedek-author.md +3 -2
- package/skills/briefs/melchizedek-models.md +13 -9
- package/skills/briefs/melchizedek.md +3 -3
- package/skills/melchizedek/SKILL.md +2 -1
- package/skills/melchizedek-author/SKILL.md +2 -1
- package/skills/melchizedek-author/assets/minimal.yaml +2 -0
- package/skills/melchizedek-models/SKILL.md +23 -6
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* lib/storage/postgres/sessionService.ts —
|
|
3
|
-
* connection (ADR 0021).
|
|
2
|
+
* lib/storage/postgres/sessionService.ts — sessions on a direct Postgres
|
|
3
|
+
* connection (ADR 0021), for either runtime (ADR 0052, ADR 0058).
|
|
4
4
|
*
|
|
5
5
|
* WHY it differs from the Supabase session service:
|
|
6
6
|
* That one re-uploads a conversation's WHOLE events array on every event.
|
|
@@ -15,23 +15,44 @@
|
|
|
15
15
|
* event; appending each event once removes that cost, and a DELEGATE
|
|
16
16
|
* conversation that is replayed to the model gets back exactly what it sent.
|
|
17
17
|
*
|
|
18
|
+
* TWO FACES, ONE STORE:
|
|
19
|
+
* The class is the engine's SessionService (create, get, list, delete,
|
|
20
|
+
* append) and ADK's BaseSessionService (createSession, getSession, …), so
|
|
21
|
+
* the ADK runtime and the native one read and write the same rows. The ADK
|
|
22
|
+
* methods call the engine's, except appendEvent, which applies the event
|
|
23
|
+
* to the runner's session through ADK's base service (its write-order
|
|
24
|
+
* check included), where append uses applyEvent. Both record it the same
|
|
25
|
+
* way. The rules are lib/runtime/sessions.ts's: a create of an existing
|
|
26
|
+
* id keeps the conversation, `afterTimestamp` is strict and filters
|
|
27
|
+
* before `numRecentEvents` counts, an event whose id the caller's session
|
|
28
|
+
* already holds replaces that row in place (as applyEvent replaces it in
|
|
29
|
+
* the session), and lastUpdateTime is the appended event's timestamp.
|
|
30
|
+
*
|
|
18
31
|
* Rows share adk_sessions with the Supabase service (same id scheme:
|
|
19
32
|
* '<appName>:<userId>:<sessionId>'), so erase and the session prune cover
|
|
20
33
|
* both. Tables: db/migrations/0001_base.sql and 0003_postgres_storage.sql.
|
|
21
34
|
*/
|
|
22
35
|
import { randomUUID } from 'node:crypto';
|
|
23
|
-
import { BaseSessionService
|
|
36
|
+
import { BaseSessionService } from '../../adkPeer.js';
|
|
37
|
+
import { applyEvent, listPage, listWindow, withoutTempKeys } from '../../runtime/sessions.js';
|
|
24
38
|
function dbId(appName, userId, sessionId) {
|
|
25
39
|
return `${appName}:${userId}:${sessionId}`;
|
|
26
40
|
}
|
|
27
|
-
/** The persisted part of a state delta:
|
|
41
|
+
/** The persisted part of a state delta: `temp:` keys are never stored. */
|
|
28
42
|
export function persistedDelta(delta) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
43
|
+
return withoutTempKeys(delta ?? {});
|
|
44
|
+
}
|
|
45
|
+
/** Whether the session already holds an event with this one's id: the append replaces it. */
|
|
46
|
+
function holds(session, event) {
|
|
47
|
+
return Boolean(event.id) && session.events.some((e) => e.id === event.id);
|
|
48
|
+
}
|
|
49
|
+
/** An event's timestamp, or the clock for an event without one. */
|
|
50
|
+
function eventTime(ms) {
|
|
51
|
+
return typeof ms === 'number' && Number.isFinite(ms) ? ms : Date.now();
|
|
52
|
+
}
|
|
53
|
+
/** last_update_time is BIGINT milliseconds. */
|
|
54
|
+
function rowTime(ms) {
|
|
55
|
+
return Math.floor(eventTime(ms));
|
|
35
56
|
}
|
|
36
57
|
/**
|
|
37
58
|
* A conversation written by the Supabase session service keeps its events as
|
|
@@ -52,6 +73,18 @@ const IMPORT_LEGACY_EVENTS = `
|
|
|
52
73
|
jsonb_array_elements(coalesce(s.events, '[]'::jsonb)) WITH ORDINALITY AS e(elem, ord)
|
|
53
74
|
WHERE s.id = $1
|
|
54
75
|
AND NOT EXISTS (SELECT 1 FROM adk_session_events WHERE session_id = $1)`;
|
|
76
|
+
/** The conversation's next event row. Runs under the session's row lock. */
|
|
77
|
+
const INSERT_EVENT = `
|
|
78
|
+
INSERT INTO adk_session_events (session_id, seq, ts, event)
|
|
79
|
+
VALUES ($1, (SELECT coalesce(max(seq), 0) + 1 FROM adk_session_events WHERE session_id = $1), $2, $3::jsonb)`;
|
|
80
|
+
/**
|
|
81
|
+
* An event whose id the session already holds replaces that row in place,
|
|
82
|
+
* as applyEvent replaces it in the session. It reads every event of the
|
|
83
|
+
* conversation, so it runs only when the caller's session held the id.
|
|
84
|
+
*/
|
|
85
|
+
const REPLACE_EVENT = `
|
|
86
|
+
UPDATE adk_session_events SET ts = $2, event = $3::jsonb
|
|
87
|
+
WHERE session_id = $1 AND event->>'id' = $4`;
|
|
55
88
|
export class PostgresSessionService extends BaseSessionService {
|
|
56
89
|
ttlMs;
|
|
57
90
|
pool;
|
|
@@ -63,28 +96,23 @@ export class PostgresSessionService extends BaseSessionService {
|
|
|
63
96
|
expireAt() {
|
|
64
97
|
return new Date(Date.now() + this.ttlMs).toISOString();
|
|
65
98
|
}
|
|
66
|
-
|
|
99
|
+
// ── The engine's interface (lib/runtime/sessions.ts) ─────────────────────
|
|
100
|
+
async create(request) {
|
|
67
101
|
const sessionId = request.sessionId || randomUUID();
|
|
68
102
|
const id = dbId(request.appName, request.userId, sessionId);
|
|
103
|
+
const state = withoutTempKeys(request.state ?? {});
|
|
69
104
|
const now = Date.now();
|
|
70
|
-
// A
|
|
71
|
-
// there, events and all, instead of resetting it.
|
|
105
|
+
// A create for an id that exists, concurrent or not, keeps the row
|
|
106
|
+
// that is already there, events and all, instead of resetting it.
|
|
72
107
|
const inserted = await this.pool.query(`INSERT INTO adk_sessions (id, app_name, user_id, state, events, last_update_time, expire_at)
|
|
73
108
|
VALUES ($1, $2, $3, $4::jsonb, '[]'::jsonb, $5, $6)
|
|
74
|
-
ON CONFLICT (id) DO NOTHING`, [id, request.appName, request.userId, JSON.stringify(
|
|
109
|
+
ON CONFLICT (id) DO NOTHING`, [id, request.appName, request.userId, JSON.stringify(state), now, this.expireAt()]);
|
|
75
110
|
if (inserted.rowCount === 0) {
|
|
76
|
-
const existing = await this.
|
|
111
|
+
const existing = await this.get({ appName: request.appName, userId: request.userId, sessionId });
|
|
77
112
|
if (existing)
|
|
78
113
|
return existing;
|
|
79
114
|
}
|
|
80
|
-
return
|
|
81
|
-
id: sessionId,
|
|
82
|
-
appName: request.appName,
|
|
83
|
-
userId: request.userId,
|
|
84
|
-
state: request.state ?? {},
|
|
85
|
-
events: [],
|
|
86
|
-
lastUpdateTime: now,
|
|
87
|
-
});
|
|
115
|
+
return { id: sessionId, appName: request.appName, userId: request.userId, state, events: [], lastUpdateTime: now };
|
|
88
116
|
}
|
|
89
117
|
/** Copy a legacy JSON history into rows, once, under the conversation's row lock. */
|
|
90
118
|
async importLegacyEvents(id) {
|
|
@@ -103,8 +131,9 @@ export class PostgresSessionService extends BaseSessionService {
|
|
|
103
131
|
client.release();
|
|
104
132
|
}
|
|
105
133
|
}
|
|
106
|
-
|
|
107
|
-
|
|
134
|
+
/** selectEvents' rules, pushed down: strictly after the timestamp, then the newest N, oldest first. */
|
|
135
|
+
async get(key, options = {}) {
|
|
136
|
+
const id = dbId(key.appName, key.userId, key.sessionId);
|
|
108
137
|
const row = await this.pool.query(`SELECT app_name, user_id, state, last_update_time,
|
|
109
138
|
(jsonb_array_length(coalesce(events, '[]'::jsonb)) > 0
|
|
110
139
|
AND NOT EXISTS (SELECT 1 FROM adk_session_events WHERE session_id = $1)) AS legacy
|
|
@@ -113,20 +142,20 @@ export class PostgresSessionService extends BaseSessionService {
|
|
|
113
142
|
return undefined;
|
|
114
143
|
if (row.rows[0].legacy)
|
|
115
144
|
await this.importLegacyEvents(id);
|
|
116
|
-
const cfg = request.config ?? {};
|
|
117
145
|
const params = [id];
|
|
118
146
|
let where = 'session_id = $1';
|
|
119
|
-
if (
|
|
120
|
-
params.push(
|
|
147
|
+
if (options.afterTimestamp) {
|
|
148
|
+
params.push(options.afterTimestamp);
|
|
121
149
|
where += ` AND ts > $${params.length}`;
|
|
122
150
|
}
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
|
|
151
|
+
const n = options.numRecentEvents;
|
|
152
|
+
const recent = typeof n === 'number' && Number.isFinite(n) ? Math.floor(n) : 0;
|
|
153
|
+
const events = recent > 0
|
|
154
|
+
? await this.pool.query(`SELECT event FROM (SELECT event, seq FROM adk_session_events WHERE ${where} ORDER BY seq DESC LIMIT $${params.length + 1}) recent ORDER BY seq ASC`, [...params, recent])
|
|
126
155
|
: await this.pool.query(`SELECT event FROM adk_session_events WHERE ${where} ORDER BY seq ASC`, params);
|
|
127
156
|
const r = row.rows[0];
|
|
128
157
|
return {
|
|
129
|
-
id:
|
|
158
|
+
id: key.sessionId,
|
|
130
159
|
appName: r.app_name,
|
|
131
160
|
userId: r.user_id,
|
|
132
161
|
state: r.state ?? {},
|
|
@@ -135,15 +164,13 @@ export class PostgresSessionService extends BaseSessionService {
|
|
|
135
164
|
};
|
|
136
165
|
}
|
|
137
166
|
/**
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
167
|
+
* listWindow and listPage's paging, with the real total beside the page.
|
|
168
|
+
* Every user's sessions when `userId` is absent. By last update, ties by
|
|
169
|
+
* id, when an order is asked for; in the order the rows were created
|
|
170
|
+
* otherwise. Events are left out.
|
|
141
171
|
*/
|
|
142
|
-
async
|
|
143
|
-
const {
|
|
144
|
-
const offset = limit !== undefined && request.page !== undefined
|
|
145
|
-
? (Math.max(1, request.page) - 1) * limit
|
|
146
|
-
: (request.offset ?? 0);
|
|
172
|
+
async list(request) {
|
|
173
|
+
const { offset, limit } = listWindow(request);
|
|
147
174
|
const params = [request.appName];
|
|
148
175
|
let where = 'app_name = $1';
|
|
149
176
|
if (request.userId !== undefined) {
|
|
@@ -151,9 +178,18 @@ export class PostgresSessionService extends BaseSessionService {
|
|
|
151
178
|
where += ` AND user_id = $${params.length}`;
|
|
152
179
|
}
|
|
153
180
|
const total = await this.pool.query(`SELECT count(*)::int AS n FROM adk_sessions WHERE ${where}`, params);
|
|
154
|
-
const orderBy = order
|
|
155
|
-
|
|
156
|
-
|
|
181
|
+
const orderBy = request.order
|
|
182
|
+
? `ORDER BY last_update_time ${request.order === 'asc' ? 'ASC' : 'DESC'}, id`
|
|
183
|
+
: 'ORDER BY created_at, id';
|
|
184
|
+
const paged = [...params];
|
|
185
|
+
let window = '';
|
|
186
|
+
if (limit !== undefined) {
|
|
187
|
+
paged.push(limit);
|
|
188
|
+
window += `LIMIT $${paged.length} `;
|
|
189
|
+
}
|
|
190
|
+
paged.push(offset);
|
|
191
|
+
window += `OFFSET $${paged.length}`;
|
|
192
|
+
const rows = await this.pool.query(`SELECT id, app_name, user_id, state, last_update_time FROM adk_sessions WHERE ${where} ${orderBy} ${window}`, paged);
|
|
157
193
|
const sessions = rows.rows.map((row) => ({
|
|
158
194
|
id: String(row.id).slice(String(row.app_name).length + String(row.user_id).length + 2),
|
|
159
195
|
appName: row.app_name,
|
|
@@ -162,33 +198,32 @@ export class PostgresSessionService extends BaseSessionService {
|
|
|
162
198
|
events: [],
|
|
163
199
|
lastUpdateTime: Number(row.last_update_time ?? 0),
|
|
164
200
|
}));
|
|
165
|
-
|
|
166
|
-
return {
|
|
167
|
-
sessions,
|
|
168
|
-
page: limit ? Math.floor(offset / limit) + 1 : 1,
|
|
169
|
-
limit: limit ?? totalItems,
|
|
170
|
-
totalItems,
|
|
171
|
-
totalPages: limit ? Math.max(1, Math.ceil(totalItems / limit)) : 1,
|
|
172
|
-
};
|
|
201
|
+
return { sessions, ...listPage(total.rows[0].n, request) };
|
|
173
202
|
}
|
|
174
|
-
async
|
|
203
|
+
async delete(key) {
|
|
175
204
|
// Events go with it (ON DELETE CASCADE).
|
|
176
|
-
await this.pool.query('DELETE FROM adk_sessions WHERE id = $1', [
|
|
177
|
-
dbId(request.appName, request.userId, request.sessionId),
|
|
178
|
-
]);
|
|
205
|
+
await this.pool.query('DELETE FROM adk_sessions WHERE id = $1', [dbId(key.appName, key.userId, key.sessionId)]);
|
|
179
206
|
}
|
|
180
|
-
async
|
|
181
|
-
const { session, event } = request;
|
|
207
|
+
async append(session, event) {
|
|
182
208
|
// Streaming fragments are appended whole once complete.
|
|
183
209
|
if (event.partial)
|
|
184
210
|
return event;
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
await
|
|
188
|
-
|
|
211
|
+
const replacing = holds(session, event);
|
|
212
|
+
const stored = applyEvent(session, event);
|
|
213
|
+
await this.record(session, stored, replacing);
|
|
214
|
+
return stored;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* One event into the store, in one transaction: its row, the state delta
|
|
218
|
+
* merged into the stored state, and the times. Two copies of a session
|
|
219
|
+
* appending in turn both land, since each writes only its own event.
|
|
220
|
+
*/
|
|
221
|
+
async record(session, event, replacing) {
|
|
189
222
|
const id = dbId(session.appName, session.userId, session.id);
|
|
190
223
|
const delta = persistedDelta(event.actions?.stateDelta);
|
|
191
|
-
const
|
|
224
|
+
const ts = typeof event.timestamp === 'number' ? event.timestamp : null;
|
|
225
|
+
const updated = rowTime(event.timestamp);
|
|
226
|
+
const json = JSON.stringify(event);
|
|
192
227
|
const client = await this.pool.connect();
|
|
193
228
|
try {
|
|
194
229
|
await client.query('BEGIN');
|
|
@@ -198,19 +233,20 @@ export class PostgresSessionService extends BaseSessionService {
|
|
|
198
233
|
if (locked.rowCount === 0) {
|
|
199
234
|
await client.query(`INSERT INTO adk_sessions (id, app_name, user_id, state, events, last_update_time, expire_at)
|
|
200
235
|
VALUES ($1, $2, $3, '{}'::jsonb, '[]'::jsonb, $4, $5)
|
|
201
|
-
ON CONFLICT (id) DO NOTHING`, [id, session.appName, session.userId,
|
|
236
|
+
ON CONFLICT (id) DO NOTHING`, [id, session.appName, session.userId, updated, this.expireAt()]);
|
|
202
237
|
await client.query('SELECT 1 FROM adk_sessions WHERE id = $1 FOR UPDATE', [id]);
|
|
203
238
|
}
|
|
204
239
|
// A conversation the Supabase service wrote: its history first.
|
|
205
240
|
await client.query(IMPORT_LEGACY_EVENTS, [id]);
|
|
206
|
-
await client.query(
|
|
207
|
-
|
|
241
|
+
const replaced = replacing ? await client.query(REPLACE_EVENT, [id, ts, json, event.id]) : undefined;
|
|
242
|
+
if (!replaced?.rowCount)
|
|
243
|
+
await client.query(INSERT_EVENT, [id, ts, json]);
|
|
208
244
|
await client.query(`UPDATE adk_sessions
|
|
209
245
|
SET state = coalesce(state, '{}'::jsonb) || $2::jsonb,
|
|
210
246
|
last_update_time = $3,
|
|
211
247
|
expire_at = $4,
|
|
212
248
|
updated_at = now()
|
|
213
|
-
WHERE id = $1`, [id, JSON.stringify(delta),
|
|
249
|
+
WHERE id = $1`, [id, JSON.stringify(delta), updated, this.expireAt()]);
|
|
214
250
|
await client.query('COMMIT');
|
|
215
251
|
}
|
|
216
252
|
catch (err) {
|
|
@@ -220,6 +256,32 @@ export class PostgresSessionService extends BaseSessionService {
|
|
|
220
256
|
finally {
|
|
221
257
|
client.release();
|
|
222
258
|
}
|
|
259
|
+
}
|
|
260
|
+
// ── ADK's BaseSessionService, for the ADK runtime ─────────────────────────
|
|
261
|
+
async createSession(request) {
|
|
262
|
+
return (await this.create(request));
|
|
263
|
+
}
|
|
264
|
+
async getSession(request) {
|
|
265
|
+
const key = { appName: request.appName, userId: request.userId, sessionId: request.sessionId };
|
|
266
|
+
return (await this.get(key, request.config));
|
|
267
|
+
}
|
|
268
|
+
async listSessions(request) {
|
|
269
|
+
return (await this.list(request));
|
|
270
|
+
}
|
|
271
|
+
async deleteSession(request) {
|
|
272
|
+
await this.delete(request);
|
|
273
|
+
}
|
|
274
|
+
async appendEvent(request) {
|
|
275
|
+
const { session, event } = request;
|
|
276
|
+
if (event.partial)
|
|
277
|
+
return event;
|
|
278
|
+
// ADK's own merge: applies the state delta to the live session, strips
|
|
279
|
+
// temp: keys, replaces or pushes the event. It must run: a bare push
|
|
280
|
+
// drops every state write (see the Supabase service).
|
|
281
|
+
const replacing = holds(session, event);
|
|
282
|
+
await super.appendEvent({ session, event });
|
|
283
|
+
session.lastUpdateTime = eventTime(event.timestamp);
|
|
284
|
+
await this.record(session, event, replacing);
|
|
223
285
|
return event;
|
|
224
286
|
}
|
|
225
287
|
}
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
import { z } from 'zod';
|
|
25
25
|
import type { SyndicateYamlConfig } from './loadSyndicate.ts';
|
|
26
26
|
export declare const MEMORY_SYSTEMS: readonly ['internal-only', 'session-only', 'long-term'];
|
|
27
|
+
export declare const REASONING_LEVELS: readonly ['none', 'low', 'medium', 'high'];
|
|
27
28
|
export declare const SKILL_SCRIPT_MODES: readonly ['none', 'local'];
|
|
28
29
|
export declare const agentSchema: z.ZodObject<{
|
|
29
30
|
name: z.ZodString;
|
|
@@ -60,7 +61,16 @@ export declare const agentSchema: z.ZodObject<{
|
|
|
60
61
|
thinkingBudget: z.ZodOptional<z.ZodNumber>;
|
|
61
62
|
includeThoughts: z.ZodOptional<z.ZodBoolean>;
|
|
62
63
|
}, z.core.$loose>>;
|
|
64
|
+
reasoningEffort: z.ZodOptional<z.ZodString>;
|
|
63
65
|
}, z.core.$loose>>;
|
|
66
|
+
reasoning: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
|
|
67
|
+
high: "high";
|
|
68
|
+
low: "low";
|
|
69
|
+
medium: "medium";
|
|
70
|
+
none: "none";
|
|
71
|
+
}>, z.ZodObject<{
|
|
72
|
+
budget_tokens: z.ZodNumber;
|
|
73
|
+
}, z.core.$strict>]>>;
|
|
64
74
|
outputSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
65
75
|
mcp_server_url: z.ZodOptional<z.ZodString>;
|
|
66
76
|
mcp_tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -71,6 +81,8 @@ export declare const agentSchema: z.ZodObject<{
|
|
|
71
81
|
none: "none";
|
|
72
82
|
}>>;
|
|
73
83
|
tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
84
|
+
env: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
85
|
+
secret_env: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
74
86
|
}, z.core.$strict>>;
|
|
75
87
|
openapi: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
76
88
|
spec: z.ZodString;
|
|
@@ -146,7 +158,16 @@ export declare const subagentSchema: z.ZodObject<{
|
|
|
146
158
|
thinkingBudget: z.ZodOptional<z.ZodNumber>;
|
|
147
159
|
includeThoughts: z.ZodOptional<z.ZodBoolean>;
|
|
148
160
|
}, z.core.$loose>>;
|
|
161
|
+
reasoningEffort: z.ZodOptional<z.ZodString>;
|
|
149
162
|
}, z.core.$loose>>;
|
|
163
|
+
reasoning: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
|
|
164
|
+
high: "high";
|
|
165
|
+
low: "low";
|
|
166
|
+
medium: "medium";
|
|
167
|
+
none: "none";
|
|
168
|
+
}>, z.ZodObject<{
|
|
169
|
+
budget_tokens: z.ZodNumber;
|
|
170
|
+
}, z.core.$strict>]>>;
|
|
150
171
|
outputSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
151
172
|
mcp_server_url: z.ZodOptional<z.ZodString>;
|
|
152
173
|
mcp_tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -157,6 +178,8 @@ export declare const subagentSchema: z.ZodObject<{
|
|
|
157
178
|
none: "none";
|
|
158
179
|
}>>;
|
|
159
180
|
tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
181
|
+
env: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
182
|
+
secret_env: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
160
183
|
}, z.core.$strict>>;
|
|
161
184
|
openapi: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
162
185
|
spec: z.ZodString;
|
|
@@ -236,7 +259,16 @@ export declare const syndicateSchema: z.ZodObject<{
|
|
|
236
259
|
thinkingBudget: z.ZodOptional<z.ZodNumber>;
|
|
237
260
|
includeThoughts: z.ZodOptional<z.ZodBoolean>;
|
|
238
261
|
}, z.core.$loose>>;
|
|
262
|
+
reasoningEffort: z.ZodOptional<z.ZodString>;
|
|
239
263
|
}, z.core.$loose>>;
|
|
264
|
+
reasoning: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
|
|
265
|
+
high: "high";
|
|
266
|
+
low: "low";
|
|
267
|
+
medium: "medium";
|
|
268
|
+
none: "none";
|
|
269
|
+
}>, z.ZodObject<{
|
|
270
|
+
budget_tokens: z.ZodNumber;
|
|
271
|
+
}, z.core.$strict>]>>;
|
|
240
272
|
outputSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
241
273
|
mcp_server_url: z.ZodOptional<z.ZodString>;
|
|
242
274
|
mcp_tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -247,6 +279,8 @@ export declare const syndicateSchema: z.ZodObject<{
|
|
|
247
279
|
none: "none";
|
|
248
280
|
}>>;
|
|
249
281
|
tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
282
|
+
env: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
283
|
+
secret_env: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
250
284
|
}, z.core.$strict>>;
|
|
251
285
|
openapi: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
252
286
|
spec: z.ZodString;
|
|
@@ -316,7 +350,16 @@ export declare const syndicateSchema: z.ZodObject<{
|
|
|
316
350
|
thinkingBudget: z.ZodOptional<z.ZodNumber>;
|
|
317
351
|
includeThoughts: z.ZodOptional<z.ZodBoolean>;
|
|
318
352
|
}, z.core.$loose>>;
|
|
353
|
+
reasoningEffort: z.ZodOptional<z.ZodString>;
|
|
319
354
|
}, z.core.$loose>>;
|
|
355
|
+
reasoning: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
|
|
356
|
+
high: "high";
|
|
357
|
+
low: "low";
|
|
358
|
+
medium: "medium";
|
|
359
|
+
none: "none";
|
|
360
|
+
}>, z.ZodObject<{
|
|
361
|
+
budget_tokens: z.ZodNumber;
|
|
362
|
+
}, z.core.$strict>]>>;
|
|
320
363
|
outputSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
321
364
|
mcp_server_url: z.ZodOptional<z.ZodString>;
|
|
322
365
|
mcp_tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -327,6 +370,8 @@ export declare const syndicateSchema: z.ZodObject<{
|
|
|
327
370
|
none: "none";
|
|
328
371
|
}>>;
|
|
329
372
|
tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
373
|
+
env: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
374
|
+
secret_env: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
330
375
|
}, z.core.$strict>>;
|
|
331
376
|
openapi: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
332
377
|
spec: z.ZodString;
|
|
@@ -395,6 +440,8 @@ export declare const syndicateSchema: z.ZodObject<{
|
|
|
395
440
|
initial_delay: z.ZodOptional<z.ZodNumber>;
|
|
396
441
|
max_delay: z.ZodOptional<z.ZodNumber>;
|
|
397
442
|
backoff_factor: z.ZodOptional<z.ZodNumber>;
|
|
443
|
+
jitter: z.ZodOptional<z.ZodNumber>;
|
|
444
|
+
exceptions: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
398
445
|
}, z.core.$strict>>;
|
|
399
446
|
timeout: z.ZodOptional<z.ZodNumber>;
|
|
400
447
|
}, z.core.$strict>>>;
|
|
@@ -423,6 +470,8 @@ export declare const syndicateSchema: z.ZodObject<{
|
|
|
423
470
|
* snake/camel drift (`output_key` for `outputKey`) count as an exact match.
|
|
424
471
|
*/
|
|
425
472
|
export declare function suggest(input: string, candidates: readonly string[]): string | undefined;
|
|
473
|
+
/** The generateContentConfig keys `reasoning` replaces (ADR 0047). */
|
|
474
|
+
export declare const REASONING_OLDER_SPELLING: readonly ['thinkingConfig', 'reasoningEffort'];
|
|
426
475
|
/**
|
|
427
476
|
* Thrown by validateSyndicateConfig. `problems` holds one `<file>: <path> —
|
|
428
477
|
* <what>` line per problem (the message lists them all); `issues` holds the
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import { z } from 'zod';
|
|
25
25
|
import { DEFAULT_ROUTE_KEY, NODE_KINDS, ROUTE_STEP_SUFFIX, START_NAME, elementNames, nodeKind } from './workflowConfig.js';
|
|
26
|
+
import { isSecretShapedEnvName } from './tools/skills/env.js';
|
|
26
27
|
// ── Leaf rules ───────────────────────────────────────────────────────────────
|
|
27
28
|
/**
|
|
28
29
|
* ADK's own rule (validateAgentName in @google/adk base_agent): checked here
|
|
@@ -41,7 +42,25 @@ const thinkingConfig = z
|
|
|
41
42
|
thinkingBudget: z.number().int().optional().describe('Reasoning token budget. 0 = off, -1 = dynamic.'),
|
|
42
43
|
includeThoughts: z.boolean().optional().describe('Stream the thinking trace in the response.'),
|
|
43
44
|
})
|
|
44
|
-
.describe('Gemini
|
|
45
|
+
.describe('Older spelling of `reasoning` for Gemini and Claude (thinkingLevel or thinkingBudget). Cannot be combined with `reasoning`.');
|
|
46
|
+
export const REASONING_LEVELS = ['none', 'low', 'medium', 'high'];
|
|
47
|
+
/**
|
|
48
|
+
* The provider-neutral reasoning key (ADR 0047): a level, or a token budget.
|
|
49
|
+
* The compiler maps it per provider (lib/compile.ts reasoningConfig).
|
|
50
|
+
*/
|
|
51
|
+
const reasoningSchema = z
|
|
52
|
+
.union([
|
|
53
|
+
z.enum(REASONING_LEVELS),
|
|
54
|
+
z.strictObject({
|
|
55
|
+
budget_tokens: z.number().int().min(0).describe('Thinking tokens; 0 = none. Effort-only providers take the nearest level at or above it.'),
|
|
56
|
+
}),
|
|
57
|
+
], {
|
|
58
|
+
error: (iss) => {
|
|
59
|
+
const hint = typeof iss.input === 'string' ? suggest(iss.input, REASONING_LEVELS) : undefined;
|
|
60
|
+
return `must be one of ${REASONING_LEVELS.join(' | ')}, or { budget_tokens: <integer ≥ 0> } (got ${describeValue(iss.input)}${hint ? ` — did you mean "${hint}"?` : ''})`;
|
|
61
|
+
},
|
|
62
|
+
})
|
|
63
|
+
.describe('How hard this agent reasons, on any provider: none | low | medium | high, or { budget_tokens: <int> }. Replaces generateContentConfig.thinkingConfig and reasoningEffort (ADR 0047).');
|
|
45
64
|
/**
|
|
46
65
|
* Loose on purpose: provider-specific fields (e.g. `toolConfig`) pass through
|
|
47
66
|
* to the SDK. Only the documented fields are typed, so `temperature: hot`
|
|
@@ -63,10 +82,15 @@ const generateContentConfig = z
|
|
|
63
82
|
.array(z.looseObject({ category: z.string(), threshold: z.string() }))
|
|
64
83
|
.optional(),
|
|
65
84
|
thinkingConfig: thinkingConfig.optional(),
|
|
85
|
+
reasoningEffort: z
|
|
86
|
+
.string()
|
|
87
|
+
.optional()
|
|
88
|
+
.describe('Older spelling of `reasoning` for the chat-completions and Responses providers. Cannot be combined with `reasoning`.'),
|
|
66
89
|
})
|
|
67
90
|
.describe('Model generation config (@google/genai GenerateContentConfig). Do not set tools here.');
|
|
68
91
|
// ── Skills ───────────────────────────────────────────────────────────────────
|
|
69
92
|
export const SKILL_SCRIPT_MODES = ['none', 'local'];
|
|
93
|
+
const envName = z.string().regex(/^[A-Z_][A-Z0-9_]*$/, 'an environment variable name (A–Z, 0–9, _)');
|
|
70
94
|
/**
|
|
71
95
|
* Agent Skills for one agent (lib/tools/skillToolset.ts): the frontmatter
|
|
72
96
|
* index is injected into the instruction at compile time, a skill is read in
|
|
@@ -87,10 +111,27 @@ const skillsSchema = z
|
|
|
87
111
|
.array(z.string().min(1))
|
|
88
112
|
.optional()
|
|
89
113
|
.describe('Registry tool names a skill may unlock through its `allowed-tools` frontmatter once it is loaded. Omitted, no skill unlocks anything.'),
|
|
114
|
+
env: z
|
|
115
|
+
.array(envName)
|
|
116
|
+
.optional()
|
|
117
|
+
.describe('Environment variable NAMES (never values) a skill script gets, beyond PATH, HOME, the temp directory, the locale and the OS essentials (ADR 0086). A name that looks like a secret (KEY, TOKEN, SECRET, PASSWORD, AUTH, DATABASE, …) belongs under secret_env.'),
|
|
118
|
+
secret_env: z
|
|
119
|
+
.array(envName)
|
|
120
|
+
.optional()
|
|
121
|
+
.describe('Secret-shaped environment variable names a skill script gets, listed here so passing a credential to a script is a deliberate, reviewable act (ADR 0086).'),
|
|
122
|
+
})
|
|
123
|
+
.superRefine((skills, ctx) => {
|
|
124
|
+
(skills.env ?? []).forEach((name, j) => {
|
|
125
|
+
if (isSecretShapedEnvName(name)) {
|
|
126
|
+
ctx.addIssue({ code: 'custom', path: ['env', j], message: `'${name}' looks like it holds a secret; a script gets it only if you list it under skills.secret_env (ADR 0086)` });
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
if ((skills.env?.length || skills.secret_env?.length) && skills.scripts !== 'local') {
|
|
130
|
+
ctx.addIssue({ code: 'custom', path: [skills.env?.length ? 'env' : 'secret_env'], message: 'variables reach skill scripts only, which run only with scripts: "local"' });
|
|
131
|
+
}
|
|
90
132
|
})
|
|
91
133
|
.describe('Agent Skills (SKILL.md directories) this agent reads the way a coding harness does.');
|
|
92
134
|
// ── OpenAPI ──────────────────────────────────────────────────────────────────
|
|
93
|
-
const envName = z.string().regex(/^[A-Z_][A-Z0-9_]*$/, 'an environment variable name (A–Z, 0–9, _)');
|
|
94
135
|
const openapiEntry = z
|
|
95
136
|
.strictObject({
|
|
96
137
|
spec: z.string().min(1).describe('An OpenAPI 3 spec file (.yaml, .yml, .json), relative to this syndicate file. Files only, never URLs.'),
|
|
@@ -151,6 +192,7 @@ const agentFields = {
|
|
|
151
192
|
disallowTransferToPeers: z.boolean().optional(),
|
|
152
193
|
outputKey: z.string().optional().describe('Session-state key the final reply is saved under.'),
|
|
153
194
|
generateContentConfig: generateContentConfig.optional(),
|
|
195
|
+
reasoning: reasoningSchema.optional(),
|
|
154
196
|
outputSchema: z
|
|
155
197
|
.record(z.string(), z.unknown())
|
|
156
198
|
.optional()
|
|
@@ -254,12 +296,20 @@ const edgeElement = z.union([
|
|
|
254
296
|
.record(z.string().min(1), edgeTargets)
|
|
255
297
|
.describe('A routing map after a node: `{ <route>: <node or nodes>, default: <node> }`, matched against that node\'s output.'),
|
|
256
298
|
]);
|
|
299
|
+
/** An error class name, as ADK matches `exceptions` against one: an identifier. */
|
|
300
|
+
const ERROR_NAME = /^[A-Za-z_$][\w$]*$/;
|
|
257
301
|
const retrySchema = z
|
|
258
302
|
.strictObject({
|
|
259
303
|
max_attempts: z.number().int().positive().optional().describe('Attempts including the first; 1 = no retry. ADK default 5.'),
|
|
260
304
|
initial_delay: z.number().nonnegative().optional().describe('Seconds before the first retry.'),
|
|
261
305
|
max_delay: z.number().nonnegative().optional(),
|
|
262
306
|
backoff_factor: z.number().positive().optional(),
|
|
307
|
+
jitter: z.number().nonnegative().optional().describe('Randomness of the backoff; 0 = none. ADK default 1.'),
|
|
308
|
+
exceptions: z
|
|
309
|
+
.array(z.string().regex(ERROR_NAME, 'an error name, such as TypeError or NodeTimeoutError'))
|
|
310
|
+
.min(1)
|
|
311
|
+
.optional()
|
|
312
|
+
.describe('Error names to retry on (the error\'s class or its `name`); every error when absent.'),
|
|
263
313
|
})
|
|
264
314
|
.describe('Retry a node on failure (lib/workflow.ts).');
|
|
265
315
|
const workflowNodeSchema = z
|
|
@@ -271,8 +321,8 @@ const workflowNodeSchema = z
|
|
|
271
321
|
max_parallel: z.number().int().positive().optional().describe('Concurrency of map. Default 8.'),
|
|
272
322
|
tool: z.string().min(1).optional().describe('Run this registry tool with the node input as its arguments.'),
|
|
273
323
|
route_key: z.string().min(1).optional().describe('Property of a JSON output holding the route. Default "route".'),
|
|
274
|
-
retry: retrySchema.optional(),
|
|
275
|
-
timeout: z.number().positive().optional().describe('Seconds this node may run before it fails.'),
|
|
324
|
+
retry: retrySchema.optional().describe('Retry this node on failure. Not on a map node: each item runs under its agent\'s own retry.'),
|
|
325
|
+
timeout: z.number().positive().optional().describe('Seconds this node may run before it fails. Not on a map node: each item runs under its agent\'s own timeout.'),
|
|
276
326
|
})
|
|
277
327
|
.describe('A declared node (exactly one of ask_user, join, map, tool) or modifiers for an agent node (retry, timeout, route_key).');
|
|
278
328
|
const workflowSchema = z
|
|
@@ -479,13 +529,15 @@ function problemsFromIssues(issues, raw) {
|
|
|
479
529
|
continue;
|
|
480
530
|
}
|
|
481
531
|
if (issue.code === 'too_small' && issue.origin === 'number') {
|
|
482
|
-
out.push({ path: p, message: `must be a positive integer (got ${describeValue(got)})` });
|
|
532
|
+
out.push({ path: p, message: `must be ${Number(issue.minimum) === 0 ? '0 or more' : 'a positive integer'} (got ${describeValue(got)})` });
|
|
483
533
|
continue;
|
|
484
534
|
}
|
|
485
535
|
out.push({ path: p, message: issue.message });
|
|
486
536
|
}
|
|
487
537
|
return out;
|
|
488
538
|
}
|
|
539
|
+
/** The generateContentConfig keys `reasoning` replaces (ADR 0047). */
|
|
540
|
+
export const REASONING_OLDER_SPELLING = ['thinkingConfig', 'reasoningEffort'];
|
|
489
541
|
const isObj = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
490
542
|
/** Same normalisation lib/dispatch.ts uses to match a route to a subagent. */
|
|
491
543
|
const routeNorm = (s) => s.replace(/[^a-z0-9]/gi, '').toLowerCase();
|
|
@@ -588,17 +640,45 @@ function crossFieldProblems(raw) {
|
|
|
588
640
|
for (const [i, sub] of (Array.isArray(raw.subagents) ? raw.subagents : []).entries())
|
|
589
641
|
if (isObj(sub))
|
|
590
642
|
executionProblems(sub, ['subagents', i], false);
|
|
643
|
+
// One spelling per agent (ADR 0047): `reasoning` is mapped onto the very
|
|
644
|
+
// fields the older spelling sets, so the two together would leave which
|
|
645
|
+
// one wins to merge order.
|
|
646
|
+
const reasoningProblems = (agent, path) => {
|
|
647
|
+
if (agent.reasoning === undefined)
|
|
648
|
+
return;
|
|
649
|
+
if (typeof agent.yaml_reference === 'string' || typeof agent.a2a_agent_url === 'string') {
|
|
650
|
+
out.push({ path: [...path, 'reasoning'], message: 'applies to an inline agent; a nested syndicate (yaml_reference) or a remote agent (a2a_agent_url) sets its own' });
|
|
651
|
+
}
|
|
652
|
+
if (!isObj(agent.generateContentConfig))
|
|
653
|
+
return;
|
|
654
|
+
for (const key of REASONING_OLDER_SPELLING) {
|
|
655
|
+
if (agent.generateContentConfig[key] === undefined)
|
|
656
|
+
continue;
|
|
657
|
+
out.push({
|
|
658
|
+
path: [...path, 'reasoning'],
|
|
659
|
+
message: `cannot be combined with generateContentConfig.${key}; reasoning replaces it, so keep one (ADR 0047)`,
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
if (isObj(raw.orchestrator))
|
|
664
|
+
reasoningProblems(raw.orchestrator, ['orchestrator']);
|
|
665
|
+
for (const [i, sub] of subs.entries())
|
|
666
|
+
if (isObj(sub))
|
|
667
|
+
reasoningProblems(sub, ['subagents', i]);
|
|
591
668
|
if (isObj(raw.orchestrator)) {
|
|
592
669
|
gateProblems(raw.orchestrator, ['orchestrator'], true);
|
|
593
670
|
skillProblems(raw.orchestrator, ['orchestrator'], true);
|
|
594
671
|
questionProblems(raw.orchestrator, ['orchestrator'], true);
|
|
595
672
|
}
|
|
596
673
|
const dispatching = isObj(raw.dispatch);
|
|
674
|
+
// A workflow's subagents are its nodes, not delegated tools: a gated call pauses its node, and
|
|
675
|
+
// the walk resumes it (ADR 0098). Skill scripts stay refused inside a workflow (workflowProblems).
|
|
676
|
+
const pausing = dispatching || isObj(raw.workflow);
|
|
597
677
|
subs.forEach((sub, i) => {
|
|
598
678
|
if (!isObj(sub))
|
|
599
679
|
return;
|
|
600
|
-
gateProblems(sub, ['subagents', i],
|
|
601
|
-
skillProblems(sub, ['subagents', i],
|
|
680
|
+
gateProblems(sub, ['subagents', i], pausing);
|
|
681
|
+
skillProblems(sub, ['subagents', i], pausing);
|
|
602
682
|
questionProblems(sub, ['subagents', i], dispatching);
|
|
603
683
|
const hasRef = typeof sub.yaml_reference === 'string';
|
|
604
684
|
const hasRemote = typeof sub.a2a_agent_url === 'string';
|
|
@@ -714,6 +794,15 @@ function workflowProblems(raw, subs) {
|
|
|
714
794
|
if (kind !== 'map' && entry.max_parallel !== undefined) {
|
|
715
795
|
out.push({ path: ['workflow', 'nodes', name, 'max_parallel'], message: 'max_parallel applies to map only' });
|
|
716
796
|
}
|
|
797
|
+
// A map item runs under its agent's own modifiers, as on ADK (ADR 0089, ADR 0103): the map entry's would be applied by neither runtime.
|
|
798
|
+
if (kind === 'map') {
|
|
799
|
+
for (const key of ['retry', 'timeout']) {
|
|
800
|
+
if (entry[key] === undefined)
|
|
801
|
+
continue;
|
|
802
|
+
const target = entry.map;
|
|
803
|
+
out.push({ path: ['workflow', 'nodes', name, key], message: `${key} on a map node is not applied: each item runs under its agent's own ${key}; set it on nodes.${target}` });
|
|
804
|
+
}
|
|
805
|
+
}
|
|
717
806
|
}
|
|
718
807
|
for (const name of agentNames) {
|
|
719
808
|
if (name.endsWith(ROUTE_STEP_SUFFIX))
|
|
@@ -785,8 +874,9 @@ function workflowProblems(raw, subs) {
|
|
|
785
874
|
for (const [path, agent] of agents) {
|
|
786
875
|
if (!isObj(agent))
|
|
787
876
|
continue;
|
|
788
|
-
|
|
789
|
-
|
|
877
|
+
// A map item cannot pause: the walk resumes a paused agent node, not an item of a map (ADR 0094, ADR 0098).
|
|
878
|
+
if (typeof agent.name === 'string' && mapped.has(agent.name) && Array.isArray(agent.require_approval) && agent.require_approval.length) {
|
|
879
|
+
out.push({ path: [...path, 'require_approval'], message: 'approval gates are not supported on an agent a map node runs: a map item cannot pause the walk' });
|
|
790
880
|
}
|
|
791
881
|
if (isObj(agent.skills) && agent.skills.scripts === 'local') {
|
|
792
882
|
out.push({ path: [...path, 'skills', 'scripts'], message: 'skill scripts (an approval pause) are not supported inside a workflow yet' });
|