Merge branch 'master' into sdk/ts-client-and-subagent

This commit is contained in:
Tianyi Cui
2026-07-27 22:41:23 +08:00
committed by GitHub
386 changed files with 7849 additions and 9915 deletions
+7 -3
View File
@@ -32,13 +32,16 @@ function quote(value: string): string {
/**
* Reduce an exported class to its type shape: drop method/constructor bodies
* and property initializers so the catalog serves member signatures, not
* implementation.
* implementation. An abstract class (e.g. `Agent`) is a public type consumers
* program against, so it belongs in the type closure alongside interfaces.
*/
function classShape(node: ts.ClassDeclaration): ts.ClassDeclaration {
const isNonPublic = (member: ts.ClassElement): boolean =>
(ts.canHaveModifiers(member) ? ts.getModifiers(member) : undefined)?.some(m =>
m.kind === ts.SyntaxKind.PrivateKeyword || m.kind === ts.SyntaxKind.ProtectedKeyword) ?? false
const members = node.members.flatMap((member): ts.ClassElement[] => {
// A model-facing type shape carries only the public surface — drop private,
// protected, and #private members, and strip every kept member's body.
if (isNonPublic(member) || (ts.isPropertyDeclaration(member) && ts.isPrivateIdentifier(member.name))) return []
if (ts.isMethodDeclaration(member)) {
return [ts.factory.updateMethodDeclaration(
@@ -67,8 +70,9 @@ function classShape(node: ts.ClassDeclaration): ts.ClassDeclaration {
}
/**
* Collect exported interface, type-alias, and body-stripped class shapes; omit
* names declared in multiple packages rather than serve the wrong shape.
* Collect exported interface, type-alias, and (body-stripped) class shapes;
* omit names declared in multiple packages rather than risk serving the wrong
* package's shape.
*/
function collectTypeDecls(scanRoot: string = root): Map<string, string> {
const printer = ts.createPrinter({ removeComments: true })
+3 -1
View File
@@ -35,9 +35,11 @@ export const LINK_MAP: Record<string, string> = {
ContinuationDecision: 'core.md',
ContinuationStop: 'core.md',
GenerateOptions: 'core.md',
InboxPlacement: 'core.md',
AgentMessage: 'core.md',
AgentMessageId: 'core.md',
HookContext: 'core.md',
SettleReason: 'core.md',
LlmCallConfig: 'core.md',
LlmModelContext: 'core.md',
LlmModelReasoningInfo: 'core.md',
@@ -48,8 +50,8 @@ export const LINK_MAP: Record<string, string> = {
Message: 'core.md',
MessageSource: 'core.md',
PromptDecision: 'core.md',
RequestErrorAction: 'core.md',
RequestError: 'core.md',
RequestErrorDecision: 'core.md',
PreparedReferencedMessage: 'session-reference.md',
SessionReferenceCandidate: 'session-reference.md',
SessionReferenceInput: 'session-reference.md',
+51 -25
View File
@@ -709,22 +709,31 @@ class EventRelationCollector {
/** Walk one package source file and classify event API calls by receiver type. */
private visitSource(source: PackageSource): void {
const visit = (node: ts.Node): void => {
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
const receiverKind = this.receiverKind(node.expression.expression)
const method = node.expression.name.text
if (receiverKind === 'events-service' && method === 'dispatch') {
const argumentList = node.arguments[1]
if (argumentList) {
for (const event of this.eventNamesFromArgumentList(argumentList, new Set())) {
this.addDispatcher(event, source.pkg, 'events.dispatch')
if (ts.isCallExpression(node)) {
if (this.isAgentEventEmitter(node.expression)) {
const event = node.arguments[2]
if (event) {
for (const name of this.finiteStringValues(event) ?? []) {
this.addDispatcher(name, source.pkg, 'emitAgentEvent')
}
}
} else if (receiverKind === 'context' || receiverKind === 'agent-dispatch') {
const eventNames = this.eventNamesFromCall(node, receiverKind)
if (method === 'on' || method === 'once') {
for (const event of eventNames) this.ensure(event).listeners.add(source.pkg)
} else if (method === 'emit' || method === 'parallel' || method === 'serial' || method === 'waterfall') {
for (const event of eventNames) this.addDispatcher(event, source.pkg, method)
} else if (ts.isPropertyAccessExpression(node.expression)) {
const receiverKind = this.receiverKind(node.expression.expression)
const method = node.expression.name.text
if (receiverKind === 'events-service' && method === 'dispatch') {
const argumentList = node.arguments[1]
if (argumentList) {
for (const event of this.eventNamesFromArgumentList(argumentList, new Set())) {
this.addDispatcher(event, source.pkg, 'events.dispatch')
}
}
} else if (receiverKind === 'context' || receiverKind === 'agent-dispatch') {
const eventNames = this.eventNamesFromCall(node, receiverKind)
if (method === 'on' || method === 'once') {
for (const event of eventNames) this.ensure(event).listeners.add(source.pkg)
} else if (method === 'emit' || method === 'parallel' || method === 'serial' || method === 'waterfall') {
for (const event of eventNames) this.addDispatcher(event, source.pkg, method)
}
}
}
}
@@ -733,6 +742,22 @@ class EventRelationCollector {
visit(source.sourceFile)
}
/** Match the exported contained-notification helper by declaration identity. */
private isAgentEventEmitter(expression: ts.Expression): boolean {
if (!ts.isIdentifier(expression)) return false
const local = this.project.checker.getSymbolAtLocation(expression)
if (!local) return false
const symbol = local.flags & ts.SymbolFlags.Alias
? this.project.checker.getAliasedSymbol(local)
: local
const declarations = symbol.declarations ?? []
return declarations.some((declaration) => {
return ts.isFunctionDeclaration(declaration)
&& declaration.name?.text === 'emitAgentEvent'
&& this.project.relativePath(declaration.getSourceFile()) === 'packages/core/agent/src/dispatch.ts'
})
}
/** Classify a receiver using assignability to the repository's actual event API types. */
private receiverKind(receiver: ts.Expression): EventReceiverKind | undefined {
const type = this.project.checker.getTypeAtLocation(receiver)
@@ -984,18 +1009,21 @@ function renderLifecycle(): string {
' participant LLM as ctx.llm',
' participant Tools as ctx.tools',
' participant Session',
' participant Persistence',
' participant SDK as UI or SDK listener',
' User->>Agent: followup(content)',
` Agent-->>SDK: ${mermaidCode('agent/inbox/enqueue')}`,
' Agent->>Driver: queued work wakes driver',
` Driver-->>SDK: ${mermaidCode('agent/status')} running`,
` Driver->>Session: ${mermaidCode('turn/start')}`,
' Note over Agent,Driver: next-step acceptance window opens',
` Driver->>Hooks: ${mermaidCode('agent/prompt-submit')} waterfall`,
' Hooks-->>Driver: authoritative allow, block, or add context',
` Driver->>Session: ${mermaidCode('user/message')} or rejected ${mermaidCode('turn/end')}`,
' alt prompt blocked or admission failed',
' Driver-->>Driver: append context-only batch or keep steering boundary pending',
' else prompt allowed',
` Driver->>Session: ${mermaidCode('turn/start')}`,
` Driver->>Session: ${mermaidCode('user/message')}`,
` Driver->>Prompt: ${mermaidCode('system-prompt/assemble')} waterfall`,
` Driver-->>Driver: ${mermaidCode('agent/pre-step')} serial checkpoint`,
` Driver-->>Driver: ${mermaidCode('agent/step')} serial checkpoint`,
` Driver->>Session: ${mermaidCode('step/start')}`,
` Driver->>LLM: ${mermaidCode('agent/request')} waterfall, then ${mermaidCode('llm/stream')} waterfall`,
' LLM-->>Driver: StreamChunk*',
@@ -1004,9 +1032,8 @@ function renderLifecycle(): string {
' alt final adapter or terminal in-band request failure',
` Driver->>Session: ${mermaidCode('step/end')}`,
` Driver->>Hooks: ${mermaidCode('agent/request-error')} waterfall`,
' Hooks-->>Driver: retry in a new step or preserve the original error',
' Hooks-->>Driver: return retry action or preserve the original error',
' else model request succeeded',
` Driver->>Hooks: ${mermaidCode('agent/step-result')} waterfall`,
` Driver->>Session: ${mermaidCode('assistant/message')}`,
' Driver->>Tools: classify pending call by executionMode',
' loop barriers and bounded rolling pool, reclassify before start',
@@ -1021,19 +1048,18 @@ function renderLifecycle(): string {
' end',
' end',
' Driver->>Session: post-tool context and steering (no prompt-submit)',
` Driver->>Hooks: ${mermaidCode('agent/post-step')} serial checkpoint`,
` Driver->>Session: ${mermaidCode('step/end')}`,
` Driver->>Hooks: ${mermaidCode('agent/turn-continuation')} waterfall`,
` Driver->>Hooks: ${mermaidCode('agent/turn-stop')} serial terminal checkpoint`,
` Driver->>Hooks: ${mermaidCode('agent/turn-stopping')} serial terminal checkpoint`,
' end',
' Note over Agent,Driver: next-step acceptance window closes',
` Driver->>Session: ${mermaidCode('turn/end')}`,
` Driver->>Persistence: ${mermaidCode('session/flush')} parallel checkpoint`,
' end',
` Driver-->>SDK: ${mermaidCode('agent/status')} idle`,
'```',
'',
'The `assistant/message` edge records every successful provider call, including content-less and `max-tokens` finishes. Empty content stays out of derived history while the durable anchor retains usage and exact chunk provenance, including an explicit empty source set.',
'',
'`dsh-compact-basic` uses `agent/post-step` for pressure after those durable facts and `agent/request-error` only for canonical context overflow. Once either trigger qualifies, optional tool-result pruning runs before summary selection. Recovery works between the closed failed step and a fresh retry step, and returns retry only when pruning or summarization advances the surface replacement generation; otherwise the original request error remains authoritative.',
'`dsh-compact-basic` uses `agent/step` for pressure before request derivation and `agent/request-error` only for canonical context overflow. Once either trigger qualifies, optional tool-result pruning runs before summary selection. Recovery works between the closed failed step and failed turn close, and opens a fresh retry turn only when pruning or summarization advances the surface replacement generation; otherwise the original request error remains authoritative.',
'',
'The returned `agent/prompt-submit` allow is authoritative; listeners wrapping `next()` preserve downstream content and additional contexts unless replacement is intentional. Steering bypasses that waterfall and joins at its durable checkpoint.',
'',
-2
View File
@@ -736,8 +736,6 @@ def scrub_snapshot_header(value: dict[object, object]) -> None:
tool.get("name") if isinstance(tool, dict) else "{{tools}}"
for tool in tools
]
if isinstance(header.get("messagePrefix"), list):
header["messagePrefix"] = ["{{messagePrefix}}" for _ in header["messagePrefix"]]
def render_jsonl(records: list[object]) -> str:
+12 -27
View File
@@ -86,21 +86,21 @@
"symbol": "SessionEvent",
"source": "packages/core/session/src/types.ts"
},
{
"doc": "docs/core-data-structures/core.md",
"symbol": "SendTarget",
"source": "packages/core/agent/src/types.ts"
},
{
"doc": "docs/core-data-structures/core.md",
"symbol": "InboxPlacement",
"source": "packages/core/agent/src/types.ts"
},
{
"doc": "docs/core-data-structures/core.md",
"symbol": "SendOptions",
"source": "packages/core/agent/src/types.ts"
},
{
"doc": "docs/core-data-structures/core.md",
"symbol": "InjectOptions",
"source": "packages/core/agent/src/types.ts"
},
{
"doc": "docs/core-data-structures/core.md",
"symbol": "ResolvedAgentInput",
"source": "packages/core/agent/src/types.ts"
},
{
"doc": "docs/core-data-structures/core.md",
"symbol": "AgentMessageId",
@@ -126,11 +126,6 @@
"symbol": "Agent",
"source": "packages/core/agent/src/types.ts"
},
{
"doc": "docs/core-data-structures/core.md",
"symbol": "HookContext",
"source": "packages/core/agent/src/types.ts"
},
{
"doc": "docs/core-data-structures/core.md",
"symbol": "PromptDecision",
@@ -138,7 +133,7 @@
},
{
"doc": "docs/core-data-structures/core.md",
"symbol": "ContinuationDecision",
"symbol": "RequestErrorAction",
"source": "packages/core/agent/src/types.ts"
},
{
@@ -146,16 +141,6 @@
"symbol": "RequestError",
"source": "packages/core/agent/src/types.ts"
},
{
"doc": "docs/core-data-structures/core.md",
"symbol": "RequestErrorDecision",
"source": "packages/core/agent/src/types.ts"
},
{
"doc": "docs/core-data-structures/core.md",
"symbol": "ContinuationStop",
"source": "packages/core/agent/src/types.ts"
},
{
"doc": "docs/core-data-structures/core.md",
"symbol": "SessionStartSource",
@@ -335,7 +320,7 @@
},
{
"doc": "docs/core-data-structures/session.md",
"symbol": "PromptMessageData",
"symbol": "UserMessageData",
"source": "packages/core/session/src/types.ts"
},
{