Merge remote-tracking branch 'origin/master' into xtr/identified-immutable-messages
# Conflicts: # docs/cordis-catalog/events.md # docs/core-data-structures/core.i18n.yaml # docs/event-producer-consumer.md # examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/session.jsonl # packages/core/agent-loop/README.i18n.yaml # packages/core/agent/README.i18n.yaml
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/subagent/subagent-dsh-sdk/README.md
|
||||
README.md: 95ddd154c8262e8854280e74618bdd9be9c938c0
|
||||
README.zh.md: c10145f34cd785d10f4b660a5f6414455ad42464
|
||||
README.md: e904ce3c09a1b44f8f5a0072b9ca85812898e74f
|
||||
README.zh.md: b11cb9c8e0bf2577be71230292d73878b22896a0
|
||||
|
||||
@@ -6,7 +6,7 @@ The SDK provider runs each subagent as a complete DeepSeek Harness runtime in a
|
||||
|
||||
## Start and ownership
|
||||
|
||||
`start(request)` resolves the child's working directory, spawns the runtime through `DeepSeekHarness`, and completes the `initialize` handshake (with the configured `provider`/`model` route) before it fulfills. Fulfillment therefore means the child runtime is ready and ownership has transferred to the caller. A spawn, handshake, or pre-publication cancellation failure rejects only after the subprocess has been reaped; a working-directory resolution failure rejects before anything is spawned.
|
||||
`start(request)` resolves the child's working directory, spawns the runtime through `DeepSeekHarness`, and completes the `initialize` handshake (with the configured `provider`/`model` route and optional `maxTokens` output cap) before it fulfills. Fulfillment therefore means the child runtime is ready and ownership has transferred to the caller. A spawn, handshake, or pre-publication cancellation failure rejects only after the subprocess has been reaped; a working-directory resolution failure rejects before anything is spawned.
|
||||
|
||||
The working directory resolves exactly like the ACP backend, through the seam's shared out-of-process helpers ([`dsh-subagent`](../subagent/README.md)): the configured `cwd` override when set (validated once at load), else the delegating parent session's cwd — never the server process's own cwd. The resolved path becomes the child process cwd and the workspace cwd of its SDK session.
|
||||
|
||||
@@ -32,6 +32,7 @@ The provider advertises no start-time capabilities (`outputSchema`/`depthLimit`/
|
||||
| `cwd` | parent session cwd | Working-directory override; same validation as [`subagent-acp`](../subagent-acp/README.md). |
|
||||
| `provider` | `deepseek` | Provider route sent in the child's `initialize`. |
|
||||
| `model` | `deepseek-v4-flash` | Model sent in the child's `initialize`. |
|
||||
| `maxTokens` | provider default | Per-request output-token cap sent in the child's `initialize`; it applies to the child root agent and its in-process descendants. |
|
||||
| `env` | `{}` | Explicit child environment layered over a credential-scrubbed parent environment (e.g. the child's own `DEEPSEEK_API_KEY`, or `DSH_CORDIS_CONFIG`). |
|
||||
| `shutdownTimeoutMs` | `1000` | Bound on the protocol `shutdown` exchange during dispose. |
|
||||
| `disposeEofGraceMs` | `6000` | Grace after stdin EOF before platform termination. |
|
||||
@@ -44,6 +45,7 @@ The provider advertises no start-time capabilities (`outputSchema`/`depthLimit`/
|
||||
providerName: dsh-sdk
|
||||
command: node
|
||||
args: ['./packages/examples/jsonrpc-demo/lib/bin.js', './examples/jsonrpc-agent/cordis.yml']
|
||||
maxTokens: 49152
|
||||
env:
|
||||
DEEPSEEK_API_KEY: !!js process.env.DEEPSEEK_API_KEY
|
||||
- id: tool-subagent
|
||||
|
||||
@@ -6,7 +6,7 @@ SDK provider 把每个子代理作为一个完整的 DeepSeek Harness 运行时
|
||||
|
||||
## 启动与所有权
|
||||
|
||||
`start(request)` 先解析子进程工作目录,经 `DeepSeekHarness` 生成运行时,并在履行前完成 `initialize` 握手(携带配置的 `provider`/`model` 路由)。因此履行意味着子运行时已就绪、所有权已移交调用方。生成、握手或发布前取消的失败只在子进程被收割之后拒绝;工作目录解析失败在生成任何东西之前拒绝。
|
||||
`start(request)` 先解析子进程工作目录,经 `DeepSeekHarness` 生成运行时,并在履行前完成 `initialize` 握手(携带配置的 `provider`/`model` 路由及可选的 `maxTokens` 输出上限)。因此履行意味着子运行时已就绪、所有权已移交调用方。生成、握手或发布前取消的失败只在子进程被收割之后拒绝;工作目录解析失败在生成任何东西之前拒绝。
|
||||
|
||||
工作目录的解析与 ACP 后端完全一致,经由接缝共享的进程外助手([`dsh-subagent`](../subagent/README.md)):设置了 `cwd` 覆盖则用之(加载时校验一次),否则用发起委托的父会话 cwd——绝不用服务器进程自己的 cwd。解析出的路径同时成为子进程 cwd 与其 SDK 会话的工作区 cwd。
|
||||
|
||||
@@ -32,6 +32,7 @@ Provider 不宣告任何启动期能力(`outputSchema`/`depthLimit`/`toolFilte
|
||||
| `cwd` | 父会话 cwd | 工作目录覆盖;校验规则与 [`subagent-acp`](../subagent-acp/README.md) 相同。 |
|
||||
| `provider` | `deepseek` | 写入子进程 `initialize` 的 provider 路由。 |
|
||||
| `model` | `deepseek-v4-flash` | 写入子进程 `initialize` 的模型。 |
|
||||
| `maxTokens` | provider 默认值 | 写入子进程 `initialize` 的单次请求输出 token 上限;对子根 Agent 及其进程内后代生效。 |
|
||||
| `env` | `{}` | 在凭据擦除后的父环境之上叠加的显式子环境(例如子进程自己的 `DEEPSEEK_API_KEY`,或 `DSH_CORDIS_CONFIG`)。 |
|
||||
| `shutdownTimeoutMs` | `1000` | 处置期间协议 `shutdown` 交换的时限。 |
|
||||
| `disposeEofGraceMs` | `6000` | stdin EOF 之后、平台终止之前的宽限。 |
|
||||
@@ -44,6 +45,7 @@ Provider 不宣告任何启动期能力(`outputSchema`/`depthLimit`/`toolFilte
|
||||
providerName: dsh-sdk
|
||||
command: node
|
||||
args: ['./packages/examples/jsonrpc-demo/lib/bin.js', './examples/jsonrpc-agent/cordis.yml']
|
||||
maxTokens: 49152
|
||||
env:
|
||||
DEEPSEEK_API_KEY: !!js process.env.DEEPSEEK_API_KEY
|
||||
- id: tool-subagent
|
||||
|
||||
@@ -46,6 +46,8 @@ export interface Config {
|
||||
provider: string
|
||||
/** Model the child runtime initializes with (default `deepseek-v4-flash`). */
|
||||
model: string
|
||||
/** Optional per-request output-token cap for the child runtime. */
|
||||
maxTokens?: number
|
||||
/**
|
||||
* Extra environment variables for the child process — e.g. the child
|
||||
* runtime's own `DEEPSEEK_API_KEY`, or `DSH_CORDIS_CONFIG` naming its
|
||||
@@ -73,14 +75,15 @@ export const Config: z<Config> = z.object({
|
||||
cwd: z.string(),
|
||||
provider: z.string().default('deepseek'),
|
||||
model: z.string().default('deepseek-v4-flash'),
|
||||
maxTokens: z.number().step(1).min(1).max(Number.MAX_SAFE_INTEGER),
|
||||
env: z.dict(z.string()).default({}),
|
||||
shutdownTimeoutMs: z.number().default(DEFAULT_SHUTDOWN_TIMEOUT_MS),
|
||||
disposeEofGraceMs: z.number().default(DEFAULT_DISPOSE_EOF_GRACE_MS),
|
||||
disposeGraceMs: z.number().default(DEFAULT_DISPOSE_GRACE_MS),
|
||||
})
|
||||
|
||||
/** The shape after schemastery applied the defaults (cwd has none). */
|
||||
type ResolvedConfig = Required<Omit<Config, 'cwd'>> & Pick<Config, 'cwd'>
|
||||
/** The shape after schemastery applied the defaults (`cwd` and `maxTokens` have none). */
|
||||
type ResolvedConfig = Required<Omit<Config, 'cwd' | 'maxTokens'>> & Pick<Config, 'cwd' | 'maxTokens'>
|
||||
|
||||
/**
|
||||
* The SDK provider. Advertises NO start-time capabilities: an out-of-process
|
||||
@@ -101,6 +104,7 @@ class SdkProvider implements SubagentProvider {
|
||||
cwd: resolveChildCwd('subagent-dsh-sdk', this.config.cwd, request.parent.session.header.cwd),
|
||||
provider: this.config.provider,
|
||||
model: this.config.model,
|
||||
...this.config.maxTokens === undefined ? {} : { maxTokens: this.config.maxTokens },
|
||||
env: this.config.env,
|
||||
shutdownTimeoutMs: this.config.shutdownTimeoutMs,
|
||||
disposeEofGraceMs: this.config.disposeEofGraceMs,
|
||||
@@ -121,6 +125,9 @@ export function apply(ctx: Context, config: Config): void {
|
||||
assertPositiveFinite('subagent-dsh-sdk', 'shutdownTimeoutMs', resolved.shutdownTimeoutMs)
|
||||
assertPositiveFinite('subagent-dsh-sdk', 'disposeEofGraceMs', resolved.disposeEofGraceMs)
|
||||
assertPositiveFinite('subagent-dsh-sdk', 'disposeGraceMs', resolved.disposeGraceMs)
|
||||
if (resolved.maxTokens !== undefined && (!Number.isSafeInteger(resolved.maxTokens) || resolved.maxTokens <= 0)) {
|
||||
throw new TypeError('subagent-dsh-sdk maxTokens must be a positive safe integer')
|
||||
}
|
||||
// Interpret a relative configured cwd against the harness launch directory
|
||||
// ONCE, at load, and fail a misconfigured directory here — not per start.
|
||||
const configuredCwd = validateConfiguredCwd('subagent-dsh-sdk', resolved.cwd)
|
||||
|
||||
@@ -35,6 +35,8 @@ export interface SdkRunSpec {
|
||||
provider: string
|
||||
/** Model the child runtime initializes with. */
|
||||
model: string
|
||||
/** Optional per-request output-token cap sent in the child runtime's initialize handshake. */
|
||||
maxTokens?: number
|
||||
/**
|
||||
* Extra environment variables to ADD for the child (e.g. the child
|
||||
* runtime's own `DEEPSEEK_API_KEY`, or `DSH_CORDIS_CONFIG`). Merged after
|
||||
@@ -126,6 +128,7 @@ export async function startSdkRun(request: SubagentStartRequest, spec: SdkRunSpe
|
||||
cwd: spec.cwd,
|
||||
provider: spec.provider,
|
||||
model: spec.model,
|
||||
...spec.maxTokens === undefined ? {} : { maxTokens: spec.maxTokens },
|
||||
})
|
||||
|
||||
// Cancellation settles the result without waiting for a cooperative child.
|
||||
|
||||
@@ -105,17 +105,22 @@ describe('dsh-subagent-dsh-sdk provider', () => {
|
||||
await ctx.fiber.dispose()
|
||||
})
|
||||
|
||||
it('initializes the child with the configured provider/model and the parent cwd', async () => {
|
||||
it('initializes the child with the configured provider/model/maxTokens and the parent cwd', async () => {
|
||||
const tmp = mkdtempSync(join(tmpdir(), 'subagent-dsh-sdk-init-'))
|
||||
const recordFile = join(tmp, 'init.jsonl')
|
||||
try {
|
||||
const ctx = await setup({ FAKE_RECORD_INIT: recordFile })
|
||||
const ctx = await setup({ FAKE_RECORD_INIT: recordFile }, { maxTokens: 4096 })
|
||||
const run = await ctx.subagents.start('dsh-sdk', request())
|
||||
await run.result
|
||||
await run.dispose()
|
||||
const { readFileSync } = await import('node:fs')
|
||||
const records = readFileSync(recordFile, 'utf8').trim().split('\n').map(line => JSON.parse(line) as Record<string, unknown>)
|
||||
expect(records).toEqual([{ cwd: process.cwd(), provider: 'fake-provider', model: 'fake-model' }])
|
||||
expect(records).toEqual([{
|
||||
cwd: process.cwd(),
|
||||
provider: 'fake-provider',
|
||||
model: 'fake-model',
|
||||
maxTokens: 4096,
|
||||
}])
|
||||
await ctx.fiber.dispose()
|
||||
} finally {
|
||||
rmSync(tmp, { recursive: true, force: true })
|
||||
@@ -364,6 +369,45 @@ describe('dsh-subagent-dsh-sdk provider', () => {
|
||||
await ctx.fiber.dispose()
|
||||
})
|
||||
|
||||
it.each([0, -1, 1.5, Number.NaN, Number.MAX_SAFE_INTEGER + 1])(
|
||||
'rejects invalid maxTokens %s at load',
|
||||
async (maxTokens) => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SubagentService)
|
||||
await expect(ctx.plugin(sdk, {
|
||||
providerName: 'sdk',
|
||||
command: 'true',
|
||||
args: [],
|
||||
provider: 'p',
|
||||
model: 'm',
|
||||
maxTokens,
|
||||
env: {},
|
||||
})).rejects.toThrow('maxTokens')
|
||||
await ctx.fiber.dispose()
|
||||
},
|
||||
)
|
||||
|
||||
it.each([0, 1.5])(
|
||||
'defensively rejects invalid maxTokens %s when apply is called directly',
|
||||
async (maxTokens) => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SubagentService)
|
||||
expect(() => { sdk.apply(ctx, {
|
||||
providerName: 'sdk',
|
||||
command: 'true',
|
||||
args: [],
|
||||
provider: 'p',
|
||||
model: 'm',
|
||||
maxTokens,
|
||||
env: {},
|
||||
shutdownTimeoutMs: DEFAULT_SHUTDOWN_TIMEOUT_MS,
|
||||
disposeEofGraceMs: DEFAULT_DISPOSE_EOF_GRACE_MS,
|
||||
disposeGraceMs: DEFAULT_DISPOSE_GRACE_MS,
|
||||
}) }).toThrow('maxTokens must be a positive safe integer')
|
||||
await ctx.fiber.dispose()
|
||||
},
|
||||
)
|
||||
|
||||
it('rejects an empty config cwd at load', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SubagentService)
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/subagent/subagent-inprocess/README.md
|
||||
README.md: 220a3f44d0eb14dfed3241c9561800f86a90aecf
|
||||
README.zh.md: cd03f01cff19c42b8dc91a906ebb89779c494a24
|
||||
README.md: 3606799e6d16e80473006f82b834a10953270914
|
||||
README.zh.md: 7f52d3699d1240f960e437d12bc48a152658cd15
|
||||
|
||||
@@ -16,7 +16,7 @@ The driver follows this sequence:
|
||||
4. Publish the child, retain the returned `AgentHandle`, and drive one task with `child.followup(prompt)` followed by `child.whenIdle()`.
|
||||
5. Read the child's own last assistant message and latest message-triggered turn reason, excluding any fork seed and later between-turn records.
|
||||
|
||||
The child gets the parent's working-directory/session lineage and inherits the parent model unless `request.agentOptions` overrides it. It gets a fresh flat registration scope: parent ownership does not import parent tool restrictions or establish an authority subset.
|
||||
The child gets the parent's working-directory/session lineage and inherits the parent provider, model, and output-token cap unless `request.agentOptions` overrides them. It gets a fresh flat registration scope: parent ownership does not import parent tool restrictions or establish an authority subset.
|
||||
|
||||
## Cancellation and ownership
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
4. 发布子 agent,保留返回的 `AgentHandle`,并通过先调用 `child.followup(prompt)`、再调用 `child.whenIdle()` 来驱动一项任务。
|
||||
5. 读取子 agent 自身最后一条 assistant 消息,以及由消息触发的最新轮次原因;排除任何 fork 初始内容和后续轮次间记录。
|
||||
|
||||
子 agent 会获得父 agent 的工作目录/会话谱系;除非 `request.agentOptions` 覆盖,否则还会继承父 agent 模型。它获得全新的扁平注册作用域:父级所有权不会导入父 agent 的工具限制,也不会建立权限子集。
|
||||
子 agent 会获得父 agent 的工作目录/会话谱系;除非 `request.agentOptions` 覆盖,否则还会继承父 agent 的提供方、模型和输出 token 上限。它获得全新的扁平注册作用域:父级所有权不会导入父 agent 的工具限制,也不会建立权限子集。
|
||||
|
||||
## 取消与所有权
|
||||
|
||||
|
||||
@@ -88,9 +88,11 @@ export async function startInProcessRun(
|
||||
const parentHeader = parent.session.header
|
||||
const parentProvider = parent.options.provider
|
||||
const parentModel = parent.options.model
|
||||
const parentMaxTokens = parent.options.maxTokens
|
||||
const agentOptions: AgentOptions = {
|
||||
...parentProvider !== undefined ? { provider: parentProvider } : {},
|
||||
...parentModel !== undefined ? { model: parentModel } : {},
|
||||
...parentMaxTokens !== undefined ? { maxTokens: parentMaxTokens } : {},
|
||||
...request.agentOptions,
|
||||
subagentDepth: childDepth,
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createUserMessage } from '@deepseek-ai/dsh-llm'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import { type Agent } from '@deepseek-ai/dsh-agent'
|
||||
import { type Agent, type AgentOptions } from '@deepseek-ai/dsh-agent'
|
||||
import { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
|
||||
import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit'
|
||||
@@ -22,7 +22,7 @@ async function mountInvariants(ctx: Context): Promise<void> {
|
||||
await ctx.plugin(AgentLoopInvariant)
|
||||
}
|
||||
|
||||
async function setup(script: Script) {
|
||||
async function setup(script: Script, parentOptions: Partial<AgentOptions> = {}) {
|
||||
const ctx = new Context()
|
||||
await mountAgentLoopTestDependencies(ctx)
|
||||
await mountInvariants(ctx)
|
||||
@@ -30,7 +30,7 @@ async function setup(script: Script) {
|
||||
await ctx.plugin(SubagentService)
|
||||
const adapter = new MockAdapter(script)
|
||||
ctx.llm.registerAdapter(['mock'], adapter)
|
||||
const parent = ctx.agentLoop.create(SessionId('parent'), { provider: 'mock', model: 'mock' })
|
||||
const parent = ctx.agentLoop.create(SessionId('parent'), { provider: 'mock', model: 'mock', ...parentOptions })
|
||||
return { ctx, parent, adapter }
|
||||
}
|
||||
|
||||
@@ -111,6 +111,27 @@ describe('startInProcessRun', () => {
|
||||
await run.dispose()
|
||||
})
|
||||
|
||||
it('inherits the parent output-token cap and accepts an explicit child override', async () => {
|
||||
const { ctx, parent, adapter } = await setup(
|
||||
[textResponse('inherited'), textResponse('overridden')],
|
||||
{ maxTokens: 111 },
|
||||
)
|
||||
const inherited = await startInProcessRun(request(parent), {})
|
||||
await inherited.result
|
||||
expect(adapter.requests[0]?.maxTokens).toBe(111)
|
||||
expect(ctx.agents.get(inherited.id)?.options.maxTokens).toBe(111)
|
||||
await inherited.dispose()
|
||||
|
||||
const overridden = await startInProcessRun({
|
||||
...request(parent),
|
||||
agentOptions: { maxTokens: 222 },
|
||||
}, {})
|
||||
await overridden.result
|
||||
expect(adapter.requests[1]?.maxTokens).toBe(222)
|
||||
expect(ctx.agents.get(overridden.id)?.options.maxTokens).toBe(222)
|
||||
await overridden.dispose()
|
||||
})
|
||||
|
||||
it('counts a RESUMED child by its persisted header depth, not the absent runtime depth', async () => {
|
||||
// Resume rebuilds runtime options, so the durable header must keep this
|
||||
// depth-1 child from delegating as though it were top-level.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write
|
||||
README.md: 0f1bc6eae00dce7b1832f76d0267edd2c6ef2a93
|
||||
README.zh.md: 49fca6af9fd039d4436f8c209a328d2fb0efcf07
|
||||
# pnpm run verify-translation-pairing --write packages/subagent/tool-subagent/README.md
|
||||
README.md: 20bb6b9c59a13f23301368faefe18849ccc0b1e9
|
||||
README.zh.md: 9435ff638b0d021507af487a54fabac3018ca400
|
||||
|
||||
@@ -21,7 +21,7 @@ With `run_in_background: true`, the tool registers the parent-owned task before
|
||||
| `provider` (required) | Provider name (`spawn`, `fork`, `acp`, ...). |
|
||||
| `toolName` | Model-facing name, default `subagent`; distinct for every loaded instance. |
|
||||
| `enableRunInBackground` | Exposes background mode, default `true`; disabling also rejects forced background calls. |
|
||||
| `agentOptions` | Default child options, currently including `model`. |
|
||||
| `agentOptions` | Provider-specific child `provider`, `model`, and positive `maxTokens`; the in-process provider treats explicit values as overrides of inherited parent options. |
|
||||
| `persona` | Per-child persona; requires provider `persona` capability. |
|
||||
| `toolFilter` | Per-child global-tool restriction; requires `toolFilter` capability. |
|
||||
| `maxDepth` | Absolute delegation-depth cap, default `3` (`0` forbids delegation); a numeric cap requires the `depthLimit` capability and fails the mount without it. `'provider-managed'` sends no cap for an out-of-process provider whose budget belongs to the child harness. The tool stays visible at the cap; each attempted start checks the calling agent's current depth and returns an errored tool result when rejected. |
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
| `provider`(必填) | 提供方名称(`spawn`、`fork`、`acp` 等)。 |
|
||||
| `toolName` | 面向模型的名称,默认 `subagent`;每个已加载实例必须不同。 |
|
||||
| `enableRunInBackground` | 公开后台模式,默认 `true`;禁用时也会拒绝强制后台调用。 |
|
||||
| `agentOptions` | 默认子 agent 选项,目前包括 `model`。 |
|
||||
| `agentOptions` | 传给具体 provider 的子 agent `provider`、`model` 和正整数 `maxTokens`;进程内 provider 会用显式值覆盖继承的父级选项。 |
|
||||
| `persona` | 每个子 agent 独立的 persona;要求提供方具备 `persona` 能力。 |
|
||||
| `toolFilter` | 每个子 agent 独立的全局工具限制;要求提供方具备 `toolFilter` 能力。 |
|
||||
| `maxDepth` | 绝对委派深度上限,默认 `3`(`0` 禁止委派);数值上限要求 `depthLimit` 能力,缺失时挂载失败。对于预算由子 harness 拥有的进程外提供方,`'provider-managed'` 不发送上限。工具在达到上限时仍然可见;每次尝试启动都会检查调用 agent 的当前深度,被拒绝时返回出错的工具结果。 |
|
||||
|
||||
@@ -74,7 +74,8 @@ export const Config: z<Config> = z.object({
|
||||
agentOptions: z.object({
|
||||
provider: z.string(),
|
||||
model: z.string(),
|
||||
}).default(undefined as unknown as { provider: string; model: string }),
|
||||
maxTokens: z.number().step(1).min(1).max(Number.MAX_SAFE_INTEGER),
|
||||
}).default(undefined as unknown as { provider: string; model: string; maxTokens: number }),
|
||||
persona: z.string(),
|
||||
// Preserve omission; Schemastery's `{ allow: [] }` default would deny every tool.
|
||||
toolFilter: z.object({
|
||||
|
||||
Reference in New Issue
Block a user