feat(agent): Pi subagents via agent.spawn / agent.await
- agent_run gains depth / spawn_reason (0029); agent_run_event types
subagent_spawned / subagent_event / subagent_completed
- agent.spawn (propose, medium, replay never): allowlist ⊆ parent, mode not
above parent, budget carved from parent's remainder, idempotent per run+input;
settings.pi.subagents {enabled, maxConcurrent, maxDepth} (subagentsEnabled kept as alias)
- child runs claim a concurrency slot under an advisory lock (waitForFact while
queued); agent.await polls business tables without calling the model
- parent abort cascades to all descendants; parent runtime budget counts
descendant usage; child events mirrored into the parent stream by parent seq
- authorize caps every child call by all ancestors' allowlists/kinds
- session page shows parent link, inherited info and the subagent tree
- e2e E11; runbook section on subagents
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
daa85a8bb4
commit
459991421e
27 files changed
+1720
-32
No files matched your search
@@ -75,7 +75,17 @@ export const PiSettings = z.object({
|
||||
/** 达到上下文预算的该比例时触发压缩 */
|
||||
thresholdRatio: z.number().min(0.3).max(0.95),
|
||||
}),
|
||||
/** @deprecated 同 subagents.enabled(合并设置时两者保持一致);新代码读 subagents.enabled */
|
||||
subagentsEnabled: z.boolean(),
|
||||
/**
|
||||
* 子代理(agent.spawn,W7-B):关闭时不向模型提供派生能力、服务端拒绝派生;
|
||||
* maxConcurrent = 一个会话同时在跑的子会话上限,maxDepth = 派生层数上限(1 = 子会话不能再派生)。
|
||||
*/
|
||||
subagents: z.object({
|
||||
enabled: z.boolean(),
|
||||
maxConcurrent: z.number().int().min(1).max(8),
|
||||
maxDepth: z.number().int().min(1).max(3),
|
||||
}),
|
||||
/** @deprecated */
|
||||
apiKeySecretRef: Id.nullable(),
|
||||
});
|
||||
@@ -182,6 +192,7 @@ export const DEFAULT_SETTINGS: GlobalSettings = {
|
||||
contextBudget: 128_000,
|
||||
compaction: { enabled: true, thresholdRatio: 0.8 },
|
||||
subagentsEnabled: false,
|
||||
subagents: { enabled: false, maxConcurrent: 2, maxDepth: 1 },
|
||||
apiKeySecretRef: null,
|
||||
},
|
||||
budget: {
|
||||
@@ -220,6 +231,7 @@ export const GlobalSettingsUpdate = z.object({
|
||||
.partial()
|
||||
.extend({
|
||||
compaction: PiSettings.shape.compaction.partial().optional(),
|
||||
subagents: PiSettings.shape.subagents.partial().optional(),
|
||||
/** 只写:明文上游密钥,服务端存为 secret 并回填 apiKeySecretRef;传 null 表示清除 */
|
||||
apiKey: z.string().min(1).max(4096).nullable().optional(),
|
||||
})
|
||||
@@ -238,20 +250,38 @@ export type GlobalSettingsUpdate = z.infer<typeof GlobalSettingsUpdate>;
|
||||
|
||||
/** 合并部分更新(纯函数,web 乐观更新与 server 共用) */
|
||||
export function mergeSettings(current: GlobalSettings, patch: GlobalSettingsUpdate): GlobalSettings {
|
||||
const { apiKey: _apiKey, compaction, ...pi } = patch.pi ?? {};
|
||||
const { apiKey: _apiKey, compaction, subagents, ...pi } = patch.pi ?? {};
|
||||
const { tmdbApiKey: _tmdbApiKey, ...metadata } = patch.metadata ?? {};
|
||||
return {
|
||||
automation: { ...current.automation, ...patch.automation },
|
||||
cache: { ...current.cache, ...patch.cache },
|
||||
organize: { ...current.organize, ...patch.organize },
|
||||
retention: { ...current.retention, ...patch.retention },
|
||||
pi: { ...current.pi, ...pi, compaction: { ...current.pi.compaction, ...compaction } },
|
||||
pi: mergePi(current.pi, pi, compaction, subagents),
|
||||
budget: { ...current.budget, ...patch.budget },
|
||||
agent: { ...current.agent, ...patch.agent },
|
||||
metadata: { ...current.metadata, ...metadata },
|
||||
};
|
||||
}
|
||||
|
||||
/** 子代理开关:subagents.enabled 优先,旧字段 subagentsEnabled 作别名,合并后两者一致 */
|
||||
function mergePi(
|
||||
current: PiSettings,
|
||||
pi: Partial<Omit<PiSettings, "compaction" | "subagents">>,
|
||||
compaction: Partial<PiSettings["compaction"]> | undefined,
|
||||
subagents: Partial<PiSettings["subagents"]> | undefined,
|
||||
): PiSettings {
|
||||
const base = current.subagents ?? DEFAULT_SETTINGS.pi.subagents;
|
||||
const enabled = subagents?.enabled ?? pi.subagentsEnabled ?? base.enabled;
|
||||
return {
|
||||
...current,
|
||||
...pi,
|
||||
compaction: { ...current.compaction, ...compaction },
|
||||
subagents: { ...base, ...subagents, enabled },
|
||||
subagentsEnabled: enabled,
|
||||
};
|
||||
}
|
||||
|
||||
/** settings 表行 */
|
||||
export const SettingEntry = z.object({
|
||||
key: SettingsKey,
|
||||
|
||||
Reference in new issue
Block a user