Merge branch 'stack/agent-profiles-1-seam' into stack/agent-profiles-3-wire

This commit is contained in:
Yichen Jiang
2026-08-08 22:47:15 +08:00
910 changed files with 17011 additions and 6270 deletions
+2 -2
View File
@@ -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/cordis/tool-cordis/README.md
README.md: eda135d93e2912bbb4e111af40d176409b383b5b
README.zh.md: 773d4100f1be6c54f491838b65205b85ec61cdbc
README.md: 9986310160c2b56126155a4c3ef84d66018d31c2
README.zh.md: d955306e1e5d4154f58c771704782ece44a15c99
+1 -1
View File
@@ -85,5 +85,5 @@ Mounting or unmounting a prompt or tool contribution changes later request prefi
## Known Limitations and Deferred Work
- **The sandbox is containment for honest code, not a security boundary** — host-realm helpers on the sandbox global are reachable, so mount code can reach Node; load this plugin as deliberately as you would grant a bash tool (see § Trust stance).
- **The `ctx` façade exposes no `effect()`** — mount code cannot register a bespoke disposer; `on`/`provide`/`tools.register` cover every mount seen so far, and a guarded `effect` waits on a real need (`FIXME(sandbox-effect)`).
- **The `ctx` façade exposes no `effect()`** — mount code cannot register a bespoke disposer; `on`/`provide`/`tools.register` are the supported cleanup paths.
- **`vmTimeoutMs` bounds only synchronous evaluation** — an async mount body escapes it; there is no async budget on mount code.
+1 -1
View File
@@ -85,5 +85,5 @@ Namespace 插件:命名导出 `name`/`inject`/`Config`/`apply`,无默
## 已知限制与暂缓事项
- **沙箱只用于约束诚实代码,并非安全边界**:可以访问沙箱全局变量上的 host realm helper,因此挂载代码可以触达 Node;加载该插件时,应当像授予 bash 工具一样慎重(见 § 信任立场)。
- **`ctx` façade 不公开 `effect()`**:挂载代码无法注册定制 disposer;`on`/`provide`/`tools.register` 已覆盖目前出现的每项挂载,受保护的 `effect` 会等待真实需求(`FIXME(sandbox-effect)`)。
- **`ctx` façade 不公开 `effect()`**:挂载代码无法注册定制 disposer;`on`/`provide`/`tools.register` 是受支持的清理路径。
- **`vmTimeoutMs` 只限制同步求值**:async 挂载主体可逃出该边界;挂载代码没有 async 预算。
+18 -2
View File
@@ -954,6 +954,10 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
signature: 'async followup( parent: Agent, childId: SessionId, content: ContentBlock[], options: SubagentFollowupOptions, ): Promise<MessageId>',
jsDoc: '/**\n * Deliver one later message to a continuable child as its next FIFO turn. A\n * resident child\'s Agent inbox accepts it directly (waking a `waiting`\n * Activation), while an absent one is cold-resumed from its persisted\n * Session. The Agent inbox is the only queue, so every accepted message has\n * one observable order.\n * @param parent - the exact live direct parent authorizing this delivery.\n * @param childId - durable child session id.\n * @param content - user-role content to deliver.\n * @param options - durable provenance and caller cancellation, which stops the\n * operation only before inbox acceptance.\n * @returns the accepted message\'s inbox id.\n * @throws when continuation services are unavailable, parent authority is\n * rejected, or the message was not admitted.\n */',
},
{
signature: 'interrupt(targetSessionId: SessionId, authority: SubagentInterruptAuthority): void',
jsDoc: '/**\n * Interrupt one live continuable child\'s current turn under a human parent\n * address or an exact live ancestor Agent. Fire-and-return: the cancel\n * signal is issued before this returns, but the target may keep running\n * until it observes the signal. Unclaimed pending inbox work, the Activation,\n * and published descendants are preserved; claimed work is not requeued.\n * Once the interrupted driver is idle, a waking send resumes the parked FIFO\n * queue. An absent target — including a one-shot or unknown id —\n * is an accepted no-op, as is a manager-less composition, which cannot own a\n * live Activation.\n * @param targetSessionId - the durable child session id to interrupt.\n * @param authority - the human parent address or exact live ancestor Agent.\n * @throws {SubagentError} `UNAUTHORIZED` when the authority does not own the\n * live target.\n */',
},
{
signature: 'async reportFrom( child: Agent, content: ContentBlock[], options: SubagentReportOptions, ): Promise<MessageId>',
jsDoc: '/**\n * Deliver selected content from one live continuable child to its durable\n * direct parent. The child is the authority credential; callers cannot name a\n * recipient. Reporting does not conclude the child\'s turn or Activation.\n * @param child - exact live reporting child.\n * @param content - selected model-facing content.\n * @param options - parent scheduling and pre-acceptance cancellation.\n * @returns the stable identity of the parent-accepted message.\n * @throws when continuation services are unavailable, sender authorization\n * fails, or the direct parent is not live.\n */',
@@ -970,6 +974,10 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
signature: 'listChildren(parentSessionId: SessionId, signal?: AbortSignal): Promise<SubagentListEntry[]>',
jsDoc: '/**\n * Enumerate the parent\'s direct session-backed subagents without loading or\n * resuming an Agent and without any query seam: the listing merges the live\n * session store with optional session persistence (live-preferred) and\n * serves each child\'s durable mode/label from the registered `subagent`\n * projection unit down a three-rung ladder — the registry\'s watermark\n * snapshot for a live child; for a cold one, a durable projection-cache\n * row when the optional cache serves an own-suffix identity (its `seq`\n * gate proves the value postdates the fork seed, where a child\'s own\n * descriptor is immutable once appended), else one persistence inspection\n * folded through the registry. The\n * projection fold is the single classification authority; per-child\n * diagnostics relay a fold that served no identity or a failed inspection,\n * never a list-time descriptor parse. Absent persistence, enumeration is\n * live-only (a cold child cannot be resumed then either, so its absence is\n * capability absence, not an error). This service consults no Agent\n * registrations, Activations, or providers.\n *\n * Every persistence read receives `signal`, and the listing rechecks\n * cancellation around each of those awaits. Read rejections that settle\n * after an abort become a stable `SubagentError` with code `CANCELLED`.\n * @param parentSessionId - parent session whose direct children are listed.\n * @param signal - caller-owned cancellation forwarded to persistence reads\n * and observed around every read await.\n * @returns children and per-child diagnostics ordered by `createdAt`, then id.\n * @throws {@link SubagentError} when the projection registry or the session\n * store is not mounted, or the caller cancels the listing.\n */',
},
{
signature: 'listDescendants(rootSessionId: SessionId, signal?: AbortSignal): Promise<SubagentDescendantListEntry[]>',
jsDoc: '/**\n * Enumerate the root\'s complete session-backed subagent tree in stable\n * pre-order from one live-preferred corpus, without loading or resuming an\n * Agent. Ordinary sessions and one-shot children remain traversal nodes so\n * continuable descendants below them are discovered; each returned entry\n * adds its durable `parentId` and root-relative `depth`. Identity resolution,\n * diagnostics, optional persistence, and cancellation follow the same\n * projection-backed contract as {@link listChildren}.\n * @param rootSessionId - session whose complete descendant tree is listed.\n * @param signal - caller-owned cancellation forwarded to persistence reads\n * and observed around every read await.\n * @returns children and per-candidate diagnostics with tree position, in\n * stable pre-order.\n * @throws {@link SubagentError} under the same conditions as {@link listChildren}.\n */',
},
{
signature: 'registerProvider(provider: SubagentProvider): () => void',
jsDoc: '/**\n * Register a provider under its name. Registration is effect-scoped and HMR\n * safe; removing a provider blocks new starts but does not revoke runs that\n * were already returned to their holders.\n * @param provider - the trusted provider implementation.\n * @returns the exact Cordis effect disposer.\n */',
@@ -1200,7 +1208,7 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
},
{
signature: 'async ask(request: AskUserQuestionRequest): Promise<AskUserQuestionAnswer>',
jsDoc: '/**\n * Ask the active UI provider and wait for the user\'s answer.\n *\n * @param request Questions, owner agent, and abort signal.\n * @returns The answer chosen or typed by the human.\n */',
jsDoc: '/**\n * Ask the active UI provider and wait for the user\'s answer.\n *\n * When a caller supplies an agent, human interaction is valid only for the\n * exact live runtime root. Runtime ownership, not durable session lineage,\n * decides this boundary: an owned child has no human answerer and would\n * block forever, while a lineage-bearing session resumed as a new runtime\n * root may ask normally.\n *\n * @param request Questions, owner agent, and abort signal.\n * @returns The answer chosen or typed by the human.\n * @throws {UserInteractionError} code `CALLER_NOT_LIVE` when a supplied\n * agent is not the registry\'s exact live instance, or `DELEGATED_CALLER`\n * when that live agent is owned by another agent.\n */',
},
],
},
@@ -1823,7 +1831,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
},
{
name: 'CommandResult',
declaration: 'export type CommandResult = {\n readonly kind: \'success\';\n readonly text?: string;\n} | {\n readonly kind: \'error\';\n readonly text: string;\n};',
declaration: 'export type CommandResult = {\n readonly kind: \'success\';\n readonly text?: string;\n readonly sourceEventSeq?: number;\n} | {\n readonly kind: \'error\';\n readonly text: string;\n};',
},
{
name: 'CompactAgentContext',
@@ -2813,6 +2821,10 @@ export const TYPE_API: readonly TypeApiEntry[] = [
name: 'SubagentCapabilities',
declaration: 'export interface SubagentCapabilities {\n readonly outputSchema: boolean;\n readonly depthLimit: boolean;\n readonly toolFilter: boolean;\n readonly persona: boolean;\n}',
},
{
name: 'SubagentDescendantListEntry',
declaration: 'export type SubagentDescendantListEntry = SubagentListEntry & {\n readonly parentId: SessionId;\n readonly depth: number;\n};',
},
{
name: 'SubagentDescriptorData',
declaration: 'export type SubagentDescriptorData = OneShotSubagentDescriptorData | ContinuableSubagentDescriptorData;',
@@ -2821,6 +2833,10 @@ export const TYPE_API: readonly TypeApiEntry[] = [
name: 'SubagentFollowupOptions',
declaration: 'export interface SubagentFollowupOptions {\n readonly source: MessageSource;\n readonly signal: AbortSignal;\n}',
},
{
name: 'SubagentInterruptAuthority',
declaration: 'export type SubagentInterruptAuthority = {\n readonly kind: \'user\';\n readonly parentSessionId: SessionId;\n} | {\n readonly kind: \'ancestor\';\n readonly agent: Agent;\n};',
},
{
name: 'SubagentListEntry',
declaration: 'export type SubagentListEntry = {\n readonly kind: \'child\';\n readonly id: SessionId;\n readonly activity: \'running\' | \'inactive\';\n readonly hasChildren: boolean;\n} & ({\n readonly mode: \'one-shot\';\n readonly label?: string;\n} | {\n readonly mode: \'continuable\';\n readonly label: string;\n}) | {\n readonly kind: \'diagnostic\';\n readonly id: SessionId;\n readonly reason: \'corrupt\' | \'unsupported\' | \'unavailable\';\n};',
-1
View File
@@ -749,7 +749,6 @@ export function isPlugin(value: unknown): value is Plugin {
* @param plugin - the plugin the mount code returned.
* @returns an equivalent plugin whose `apply` sees the sandbox context façade.
*/
// FIXME(sandbox-effect): expose guarded custom effects when a mount needs bespoke cleanup.
export function guardedPlugin(plugin: Plugin): Plugin {
if (typeof plugin === 'function') {
const functionPlugin = plugin as (ctx: Context, config?: unknown) => unknown