@qilin-agent/agent 0.0.0-stage → 3.1.4

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kkutysllb
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,91 @@
1
+ # Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
2
+ # section, a hash of its English and Chinese blocks outside code blocks and generated regions.
3
+ # After editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/core/agent/README.md
5
+ /:
6
+ en: b6bcf3c8b941d336
7
+ zh: 28b5d2aa43fdd026
8
+ /qilin-agent-agent:
9
+ en: e32d7a82cb47df5a
10
+ zh: cbcc39e7d3f71b47
11
+ /qilin-agent-agent/summary:
12
+ en: 51b3c852c12e3058
13
+ zh: 0d87a7b345bbb20c
14
+ /qilin-agent-agent/table-of-contents:
15
+ en: d152484eb41ac6b4
16
+ zh: 09388d293f9be9cb
17
+ /qilin-agent-agent/use-this-package:
18
+ en: 7bf226aa10032c65
19
+ zh: 35ca00de642a7084
20
+ /qilin-agent-agent/use-this-package/create-or-resume-an-agent:
21
+ en: e305441a5a4a9b44
22
+ zh: 3e997b12ce0ce04c
23
+ /qilin-agent-agent/use-this-package/drive-an-agent-s-conversation:
24
+ en: 71e258897e2e7d73
25
+ zh: abf0991012faa17e
26
+ /qilin-agent-agent/use-this-package/scope-registrations-to-one-agent:
27
+ en: 2594a1e02adea801
28
+ zh: cae8860369728efb
29
+ /qilin-agent-agent/use-this-package/intercept-or-observe-work-in-flight:
30
+ en: ce0913e000c9f798
31
+ zh: 44f9e10105365f60
32
+ /qilin-agent-agent/understand-the-implementation:
33
+ en: ce7e776e9d516bdb
34
+ zh: 59d2751743db7795
35
+ /qilin-agent-agent/understand-the-implementation/design-concept:
36
+ en: 4e4e4c97ed26af72
37
+ zh: 486998c31b53f782
38
+ /qilin-agent-agent/understand-the-implementation/step-admission:
39
+ en: 348b9ecd0890c585
40
+ zh: d0f73d28d0d2c6f1
41
+ /qilin-agent-agent/understand-the-implementation/durable-inbox:
42
+ en: 3c4d162ea2d9c011
43
+ zh: ed5fcecfb0f9b238
44
+ /qilin-agent-agent/understand-the-implementation/source-map:
45
+ en: 5ca4f560874fb136
46
+ zh: b97e5fd12a76a6bc
47
+ /qilin-agent-agent/understand-the-implementation/registry-and-lifecycle:
48
+ en: 93b3792bd58fedf7
49
+ zh: 5375d1f8d5a12e0d
50
+ /qilin-agent-agent/understand-the-implementation/initiator-scope:
51
+ en: a31cbc3060b8d702
52
+ zh: eb5d3c9741fb5922
53
+ /qilin-agent-agent/understand-the-implementation/ownership-invariants:
54
+ en: e48eae6c7de88cf8
55
+ zh: 1f5b37bdb0e53788
56
+ /qilin-agent-agent/further-exploration:
57
+ en: de3b5dbc8e7f042f
58
+ zh: ffe21b02f4b3711e
59
+ /qilin-agent-agent/model-experience:
60
+ en: 215e7ba838619b7b
61
+ zh: a311da3843709f90
62
+ /qilin-agent-agent/model-experience/user-steering-and-injected-messages:
63
+ en: 962c598672b0eed6
64
+ zh: d05b3bc936685ad3
65
+ /qilin-agent-agent/model-experience/user-steering-and-injected-messages/what-the-model-sees:
66
+ en: 55fcf97dbcb7a41e
67
+ zh: 691c0ab3ea41f6f0
68
+ /qilin-agent-agent/model-experience/user-steering-and-injected-messages/token-effect:
69
+ en: 5f428bfadaf4394b
70
+ zh: 03308df9fd3492a7
71
+ /qilin-agent-agent/model-experience/user-steering-and-injected-messages/kv-cache-effect:
72
+ en: 75dc61fe240860f0
73
+ zh: 621720291d1b4bd3
74
+ /qilin-agent-agent/model-experience/agent-scoped-request-composition:
75
+ en: 215fc35b0f30e38a
76
+ zh: 21788ec980c300ea
77
+ /qilin-agent-agent/model-experience/agent-scoped-request-composition/what-the-model-sees:
78
+ en: 057babd515991736
79
+ zh: acf0c05c490b689c
80
+ /qilin-agent-agent/model-experience/agent-scoped-request-composition/token-effect:
81
+ en: dfebf42b629656d9
82
+ zh: 9369ace52f58a579
83
+ /qilin-agent-agent/model-experience/agent-scoped-request-composition/kv-cache-effect:
84
+ en: 07a408b32e49ecaf
85
+ zh: 9684b80cb3c0e75c
86
+ /qilin-agent-agent/known-limitations-and-deferred-work:
87
+ en: 00a2d12980ffe756
88
+ zh: c8bf69d06d165b36
89
+ /qilin-agent-agent/known-limitations-and-deferred-work/dev-note:
90
+ en: 217405c92be96543
91
+ zh: 7bc21e6842079a57
package/README.md CHANGED
@@ -1,3 +1,185 @@
1
- # Temporary Holding Version
1
+ ---
2
+ description: "The Agent handle, live registry, process-local initiator scope, and agent/* event vocabulary for plugins, UI, and orchestrators building or extending agents."
3
+ kind: "package-reference"
4
+ ---
2
5
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
6
+ # @qilin-agent/agent
7
+
8
+ English | [中文](README.zh.md)
9
+
10
+ ## Summary
11
+
12
+ Use `qilin-agent` to create or resume live agents, send follow-up or steering input, inject model-facing context, cancel work, and wait for idle completion. Plugins, UI, hooks, and orchestrators can also observe or intercept agent activity and apply capabilities to one agent without affecting others. Choose it when code needs to control or extend live agents through the public `Agent` API. Pair it with an agent driver such as `qilin-agent-loop`; this package does not create model requests by itself. Initiator attribution is process-local and must be carried explicitly across workers, processes, durable queues, and restarts.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
27
+
28
+ Mount `qilin-agent` wherever live agents exist: it provides `ctx.agents` and the `Agent` handle that plugins, UI, hooks, and orchestrators work against. The service is inert until a driver registers a factory — the shipped driver is `qilin-agent-loop`, so the smallest useful composition loads both.
29
+
30
+ ### Create or resume an agent
31
+
32
+ `ctx.agents.create()` builds a fresh agent and session under one identity; `ctx.agents.resume()` loads a persisted session and rebuilds the agent on it. Both delegate to the registered factory and return an `AgentHandle` — the only object that can tear that agent down. Set `parentAgent` in either operation's options to make the result a runtime child; omit it for a runtime root. `get(id)`, `list()`, and `roots()` find live agents, and `isOwnedBy(id, parent)` tests that exact live relation.
33
+
34
+ ```text
35
+ const handle = await ctx.agents.create({
36
+ sessionId,
37
+ agentOptions: { provider: 'deepseek', model: 'deepseek-chat' },
38
+ })
39
+ // later:
40
+ await handle.dispose() // stops the loop, unregisters, removes the session, unwinds the scope
41
+ ```
42
+
43
+ `AgentOptions` supplies the initial provider/model route, optional adapter-owned `reasoningEffort`, and optional positive `maxTokens` output cap. The loop validates exact-model reasoning support, resolves adapter defaults, records the effective values in the request header, and applies them to each conversation request. An optional `setup(agentCtx, agent)` callback composes the agent's scoped world before it is published: `agentCtx` owns registrations, while the explicit unpublished Agent provides its Session; the Context has no reverse Agent property. Scoped tools, prompt sections, and listeners exist before any creation announcement. Setup is composition-only: drive the agent only after creation resolves.
44
+
45
+ ### Drive an agent's conversation
46
+
47
+ The handle's methods route identified user-role messages into the agent's inbox. `followup()` queues an ordinary next-turn prompt and wakes the driver; `steer()` submits next-step input and wakes it; `inject()` adds model-facing context without waking the driver, so it lands in the next admitted step. `cancel(cause)` aborts the active activity and, unless `keepInbox` is set, clears pending work; `whenIdle()` resolves after the whole agent reaches quiescence.
48
+
49
+ ```text
50
+ handle.agent.followup({
51
+ content: [{ type: 'text', text: 'Summarize this workspace.' }],
52
+ source: { kind: 'user' },
53
+ })
54
+ handle.agent.steer({
55
+ content: [{ type: 'text', text: 'Focus on the tests.' }],
56
+ source: { kind: 'plugin', plugin: 'my-plugin' },
57
+ })
58
+ await handle.agent.whenIdle()
59
+ ```
60
+
61
+ ### Scope registrations to one agent
62
+
63
+ `Agent.ctx` is the agent's scoped context: registrations made through it (tools, prompt sections, variables, event listeners, restrictions) apply to that agent alone and unwind on disposal. The same mechanism is what agent presets use to give one session a different capability set without affecting its neighbors.
64
+
65
+ ### Intercept or observe work in flight
66
+
67
+ The `agent/*` events let plugins act on live work without depending on the loop package. `agent/pre-step` can reject a proposed step or replace the messages entering it; `agent/request-error` lets a listener retry a failed model request; `agent/turn-stopping` runs before an otherwise completed turn closes and can steer to keep it open. `agent/assistant-stream` carries one process-local Assistant attempt's ordered start, transient chunk, and end frames. Start names the attempt's turn and step, chunk indexes are dense from zero, and `end.index` is the next chunk position. The loop commits the complete compact stream as one `assistant/message` or `assistant/attempt` before a committed end frame, so the live event remains presentation data rather than the replay source. `agent/status`, `agent/created`, and `agent/disposed` drive UI and coordination state, and the per-message `agent/inbox/*` notifications keep inbox projections in sync. Exact signatures, dispatch modes, and payload contracts live in the generated region of the [core subsystem page](../../../docs/subsystems/core.md#kylin-surface).
68
+
69
+ -----
70
+
71
+ <a id="understand-the-implementation"></a>
72
+ ## Understand the implementation
73
+
74
+ <details>
75
+ <summary>Implementation internals — click to expand</summary>
76
+
77
+ This section explains how the package realizes the behavior above; the observable contract is covered in [Use this package](#use-this-package).
78
+
79
+ ### Design concept
80
+
81
+ The package is built on one separation: the public `Agent` surface and registry live here, while construction and driving live in the loop package behind a registered factory. Consumers therefore depend on `qilin-agent` and never on `qilin-agent-loop`, keeping the driver swappable. The second idea is the initiator scope: an `AsyncLocalStorage` chain that carries the exact live `Agent` through the asynchronous driver work it starts, so helpers below a driver can attribute their work without forwarding the agent through every call.
82
+
83
+ ### Step admission
84
+
85
+ `PreStepDecision` is either `{ kind: 'reject' }` or `{ kind: 'enter', messages, startsRequestSeries? }`. The enter branch contains the complete identified, frozen message batch. Acceptance does not commit it: after assembly and `step/start`, `agent/request` and `prepareCall()` resolve the route before the loop commits the system prompt and user batch. Cancellation during either async phase commits neither. `startsRequestSeries: true` declares a distinct model-message series; a wrapping listener preserves that declaration and the batch unless it intentionally replaces either one. Claiming removes offered messages from the inbox, while messages inserted after the claim remain pending for a later boundary.
86
+
87
+ ### Durable inbox
88
+
89
+ `Agent.inbox` exposes only the structural `Inbox` interface and the projection vocabulary stays in this package. qilin-agent-loop owns the package-internal `ReactLoopInbox` and the standard `inbox` projection; constructing its concrete inbox ensures that the projection registry owns one registration for the durable `agent/inbox/spliced` fold. The registry remains the sole owner of the live `{ 'next-turn', 'next-step' }` state. Reconstruction rejects unsafe or out-of-range splice coordinates and duplicate `MessageId` values across both pending lists and reports the offending event seq.
90
+
91
+ `Inbox` exposes pending `nextTurn` and `nextStep` messages and mutates them through `append`, `prepend`, `replace`, `remove`, `clear`, and `splice`. Ordinary removals and `clear()` are durable cancellations. At a step boundary, the loop's internal implementation claims pending input through pure deletion splices. Live notifications are deliberately per-message and minimal: `agent/inbox/inserted { message }`, `agent/inbox/claimed { message, turn }`, and `agent/inbox/discarded { message }`.
92
+
93
+ ### Source map
94
+
95
+ | File | Role |
96
+ |---|---|
97
+ | [`src/index.ts`](src/index.ts) | Plugin entry: `AgentRegistry`, factory slot, initiator scope, `CreateAgentOptions`/`ResumeAgentOptions` |
98
+ | [`src/runtime-types.ts`](src/runtime-types.ts) | `Agent`, structural `Inbox`, `AgentStatus`, and the `agent/*` event declarations |
99
+ | [`src/types.ts`](src/types.ts) | `AgentOptions`, cancellation causes, and inbox projection vocabulary |
100
+ | [`src/dispatch.ts`](src/dispatch.ts) | `agentEvents` fused dispatcher and `assembleContextFor(agent)` |
101
+ | [`src/consumed-work.ts`](src/consumed-work.ts) | `foldConsumedWork(events)`: what the log's consumed work became |
102
+ | [`src/model-selection.ts`](src/model-selection.ts) | `installModelSelection`: coupling one selection to assembly and routing |
103
+
104
+ ### Registry and lifecycle
105
+
106
+ `AgentRegistry` keeps one entry per live agent with its carrier and creator relation. Await `register()` to finish serial creation listeners with source `startup` before using an already-constructed agent; the async factory uses the split `enter()`/`announce()` pair so setup and initialization stay rollback-covered. A detach requested during creation waits for every awaited listener to settle, and each detach is bound to the exact entry, so a stale disposer cannot remove a later same-id replacement. Teardown stops and drains the loop, unwinds the scope, detaches the agent, then detaches the session; the id becomes reusable after private cleanup.
107
+
108
+ ### Initiator scope
109
+
110
+ Each driver runs its complete lifetime inside `ctx.agents.withInitiator(agent, ...)`, so inherited async chains observe that agent; `withoutInitiator()` hides it for unrelated process-local work such as shared timers. The boundary is process-local attribution only — ambient presence is neither liveness proof nor authorization, and explicit identity stays authoritative at worker, process, persistence, and wire boundaries. Teardown rejects new boundaries, lets returned-Promise boundaries drain, then disables the underlying storage. The [initiator-scope decision](../../../.agents/notes/implemented/architecture/2026-07-15-agent-initiator-scope.md) owns the detailed contract.
111
+
112
+ ### Ownership invariants
113
+
114
+ The `AgentHandle` disposer is a capability: among consumers, only its holder can tear the agent down. The registered factory provider is a structural co-owner, because scoped agents depend on that provider's service API; provider unload stops and drains every live handle it made. `ctx.agents.get(id)` still returns a bare `Agent` — the handle is exposed only to the consumer that created it.
115
+
116
+ </details>
117
+
118
+ -----
119
+
120
+ <a id="further-exploration"></a>
121
+ ## Further Exploration
122
+
123
+ The package-level contract is enough for most consumers; read these when you need the surrounding domain and the design rationale.
124
+
125
+ - [Core subsystem](../../../docs/subsystems/core.md) — the loop map, `Agent` handle, interception decisions, and generated service API.
126
+ - [agent-loop package](../agent-loop/README.md) — the default driver that creates, drives, and disposes agents.
127
+ - [Session subsystem](../../../docs/subsystems/session.md) — the durable log and derived history behind the handle.
128
+ - [Initiator-scope Agent Note](../../../.agents/notes/implemented/architecture/2026-07-15-agent-initiator-scope.md) — the boundary and teardown contract.
129
+ - [Core group map](../README.md) — how the core packages compose.
130
+
131
+ -----
132
+
133
+ <a id="model-experience"></a>
134
+ ## Model Experience
135
+
136
+ ### User, steering, and injected messages
137
+
138
+ #### What the model sees
139
+
140
+ `followup`, `steer`, and `inject` feed the owning session as identified user-role messages; accepted content becomes part of the derived history the model reads on later steps. `agent/pre-step` and the other declared events let plugins reject a proposed step or add durable request material. `installModelSelection` adds `[model changed: assistant turns above this point were generated by <previous>; the session continues with <next>]` to the first step assembled for a different provider/model route that would send a model request; provider names appear only when the switch crosses providers, and reasoning-effort-only changes add nothing. An empty first decision and a decision that removes offered messages remain no-request results. If a request step fails before logging its header, the next request step receives the notice again because the durable previous route has not changed.
141
+
142
+ #### Token effect
143
+
144
+ Accepted content becomes retained history or a repeated session prefix; blocked content contributes no request tokens. Each emitted model-switch notice adds its text to retained history. Size is caller- and plugin-dependent.
145
+
146
+ #### KV Cache effect
147
+
148
+ Accepted history and steering are append-only; a blocked submission sends no request. A session prefix remains stable within its loop instance, while a new or resumed instance may establish a different prefix.
149
+
150
+ ### Agent-scoped request composition
151
+
152
+ #### What the model sees
153
+
154
+ Registrations through `agent.ctx` can shadow prompt sections or tools and can install agent-only interceptors during unpublished setup, so one agent sees a different prompt and tool set than its neighbors. Model selection captures one provider/model/effort value before prompt assembly and applies it to the same step's request; a later concurrent change waits for another step.
155
+
156
+ #### Token effect
157
+
158
+ Each provider/model switch adds one short retained user-role notice. Other scoped contributions affect only that agent and disappear on disposal.
159
+
160
+ #### KV Cache effect
161
+
162
+ The switch notice appends after the previous history, preserving that prefix, while the route change can prevent the new provider or model from reusing it. Setup or reload that changes prompt sections, tool definitions, or request listeners may invalidate reuse from the first affected request token.
163
+
164
+ ## Known Limitations and Deferred Work
165
+
166
+ <a id="known-limitations-and-deferred-work"></a>
167
+
168
+
169
+ These limits define when this package needs special care. They are current package constraints, not a task backlog.
170
+
171
+ - **Initiator scope is process-local** — workers, child processes, HTTP, durable queues, and restarts must materialize any required identity explicitly.
172
+ - **Ambient identity may outlive liveness** — consumers still check `agent.status`, cancellation, and the owning capability contract before lifecycle-sensitive work.
173
+ - **Creation listeners share the initialization lifetime.** An `agent/created` listener must not await `agent.whenIdle()` or its own owner's disposal: those operations wait for creation to finish. Return only after required asynchronous tool and prompt installation completes.
174
+ - **`cancel()` clears the inbox by default** — it aborts the in-flight turn plus queued and steering work; `cancel(cause, { keepInbox: true })` aborts only the turn and preserves pending items, and there is no step-only abort that keeps the turn running.
175
+ - **Each additional `UserMessage` carries exactly one `MessageSource`** — contributions from several plugins merged onto one message collapse under one source, so the message cannot name several producers.
176
+
177
+ <a id="dev-note"></a>
178
+ ### Dev Note
179
+
180
+ <details>
181
+ <summary>Working context for maintainers — click to expand</summary>
182
+
183
+ This Dev Note is working context for maintainers; it is explicitly non-authoritative. Open, undecided directions: inter-agent channels beyond delegation — shared state, streaming child output, and background or poll semantics remain outside the current delegation seam; and the `SessionStartSource` values `'clear'`/`'compact'` are reserved with no emitter yet, pending the driving subsystems.
184
+
185
+ </details>
package/README.zh.md ADDED
@@ -0,0 +1,185 @@
1
+ ---
2
+ description: "面向插件、UI 与编排器的 Agent 句柄、实时注册表、进程本地发起方作用域,以及 agent/* 事件词汇。"
3
+ kind: "package-reference"
4
+ ---
5
+
6
+ # @qilin-agent/agent
7
+
8
+ [English](README.md) | 中文
9
+
10
+ ## 概述
11
+
12
+ 使用 `qilin-agent` 创建或恢复实时 agent(智能体)、发送后续或 steering(中途引导)输入、注入面向模型的上下文、取消工作,并等待 agent 进入空闲状态。插件、UI、钩子与编排器还可以观察或拦截 agent 活动,并仅为一个 agent 应用能力而不影响其他 agent。当代码需要通过公开 `Agent` API 控制或扩展实时 agent 时,请选择本包。请将它与 `qilin-agent-loop` 等 agent 驱动器配合使用;本包本身不会创建模型请求。发起方归因仅存在于进程内,跨 worker、进程、持久队列与重启时必须显式传递。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [进一步探索](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与延期工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
22
+
23
+ -----
24
+
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
27
+
28
+ 在存在实时 agent 的任何地方挂载 `qilin-agent`:它提供 `ctx.agents` 以及插件、UI、钩子和编排器所面向编程的 `Agent` 句柄。在没有驱动器注册工厂之前,该服务保持惰性——随附驱动器是 `qilin-agent-loop`,因此最小的可用组合需要同时加载两者。
29
+
30
+ ### 创建或恢复 agent
31
+
32
+ `ctx.agents.create()` 在一个身份下构建全新 agent 与会话;`ctx.agents.resume()` 加载持久化会话并在此基础上重建 agent。两者都委托给已注册工厂,并返回 `AgentHandle`——唯一能拆除该 agent 的对象。在任一操作的 options 中设置 `parentAgent`,可使结果成为运行时子级;省略它则得到运行时根级。`get(id)`、`list()` 与 `roots()` 用于查找实时 agent;`isOwnedBy(id, parent)` 用于检验这项确切的实时所有权关系。
33
+
34
+ ```text
35
+ const handle = await ctx.agents.create({
36
+ sessionId,
37
+ agentOptions: { provider: 'deepseek', model: 'deepseek-chat' },
38
+ })
39
+ // later:
40
+ await handle.dispose() // stops the loop, unregisters, removes the session, unwinds the scope
41
+ ```
42
+
43
+ `AgentOptions` 提供初始提供方/模型路由、可选的由适配器定义的 `reasoningEffort`,以及可选的正数 `maxTokens` 输出上限。循环会校验确切模型的推理(reasoning)支持、解析适配器默认值、把生效值记录在请求头中,并将它们应用到每个对话请求。可选的 `setup(agentCtx, agent)` 回调会在 agent 发布之前组合其作用域世界:`agentCtx` 拥有注册,显式的未发布 Agent 则提供其 Session;Context 不含反向 Agent 属性。作用域工具、提示词段与监听器在任何创建公告之前就已存在。Setup 只做组合:创建完成后才能驱动 agent。
44
+
45
+ ### 驱动 agent 的对话
46
+
47
+ 句柄的方法把带标识的 user 角色消息路由进 agent 的收件箱。`followup()` 排队一条普通的下一个轮次提示词并唤醒驱动器;`steer()` 提交下一步输入并唤醒它;`inject()` 添加面向模型的上下文但不唤醒驱动器,因此它落在下一个被接纳的步骤中。`cancel(cause)` 中止当前活动,并在未设置 `keepInbox` 时清除待处理工作;`whenIdle()` 会在整个 agent 达到完全停稳后完成。
48
+
49
+ ```text
50
+ handle.agent.followup({
51
+ content: [{ type: 'text', text: 'Summarize this workspace.' }],
52
+ source: { kind: 'user' },
53
+ })
54
+ handle.agent.steer({
55
+ content: [{ type: 'text', text: 'Focus on the tests.' }],
56
+ source: { kind: 'plugin', plugin: 'my-plugin' },
57
+ })
58
+ await handle.agent.whenIdle()
59
+ ```
60
+
61
+ ### 将注册限定到单个 agent
62
+
63
+ `Agent.ctx` 是该 agent 的作用域上下文:通过它进行的注册(工具、提示词段、变量、事件监听器、限制)只对该 agent 生效,并在 dispose(资源释放)时全部撤销。同一机制也是 agent preset 用来让一个会话获得不同能力集、同时不影响其邻居的方式。
64
+
65
+ ### 拦截或观察进行中的工作
66
+
67
+ `agent/*` 事件让插件无需依赖循环包即可作用于实时工作。`agent/pre-step` 可以拒绝拟进入的步骤或替换进入它的消息;`agent/request-error` 让监听器重试失败的模型请求;`agent/turn-stopping` 在本可完成的轮次关闭前运行,并可通过 steer 使其保持打开。`agent/assistant-stream` 携带一个进程本地 Assistant attempt 的有序 start、瞬态分片与 end frame。start 给出该 attempt 的轮次与步骤,分片索引从零开始密集递增,`end.index` 则是下一个分片位置。loop 会在 committed end frame 前把完整紧凑流提交为一个 `assistant/message` 或 `assistant/attempt`,因此实时事件仍是呈现数据而非回放来源。`agent/status`、`agent/created` 与 `agent/disposed` 驱动 UI 与协调状态,逐消息的 `agent/inbox/*` 通知则让收件箱投影保持同步。确切签名、分发 mode 与 payload 约定见 [core 子系统页](../../../docs/subsystems/core.zh.md#kylin-surface) 的生成区块。
68
+
69
+ -----
70
+
71
+ <a id="understand-the-implementation"></a>
72
+ ## 理解实现
73
+
74
+ <details>
75
+ <summary>实现细节——点击展开</summary>
76
+
77
+ 本节解释该包如何实现上述行为;可观察约定已在[使用本包](#use-this-package)中完整说明。
78
+
79
+ ### 设计理念
80
+
81
+ 该包建立在一项职责分离之上:公开的 `Agent` 接口与注册表位于本包,构造与驱动则位于循环包,并通过已注册工厂提供。消费方因此依赖 `qilin-agent` 而不依赖 `qilin-agent-loop`,从而保持驱动器可替换。第二个理念是发起方作用域:一条 `AsyncLocalStorage` 链把确切的实时 `Agent` 携带经过它启动的异步驱动器工作,使驱动器之下的辅助函数无需逐调用转发 agent 即可归因自己的工作。
82
+
83
+ ### 步骤准入
84
+
85
+ `PreStepDecision` 要么是 `{ kind: 'reject' }`,要么是 `{ kind: 'enter', messages, startsRequestSeries? }`。enter 分支包含完整、带标识且冻结的消息批次。接纳不等于提交:组装与 `step/start` 之后,`agent/request` 和 `prepareCall()` 先解析路由,循环随后才提交系统提示词与用户批次。在任一异步阶段取消都不会提交这两者。`startsRequestSeries: true` 声明一个独立的模型消息序列;包装下游 enter 的监听器会保留该声明与批次,除非有意替换其中一项。领取会从 inbox 移除候选消息,领取后插入的消息则等待后续边界。
86
+
87
+ ### 持久 inbox
88
+
89
+ `Agent.inbox` 只暴露结构型 `Inbox` 接口,投影词汇仍位于本包。qilin-agent-loop 持有包内部的 `ReactLoopInbox` 与标准 `inbox` 投影;构造具体 inbox 时会确保投影注册表为持久 `agent/inbox/spliced` fold 持有一份注册。注册表继续作为实时 `{ 'next-turn', 'next-step' }` 状态的唯一所有者。重建过程会拒绝不安全或越界的 splice 坐标,以及跨两份待处理列表重复的 `MessageId`,并报告出错事件的 seq。
90
+
91
+ `Inbox` 暴露待处理的 `nextTurn` 与 `nextStep` 消息,并通过 `append`、`prepend`、`replace`、`remove`、`clear` 与 `splice` 变更它们。普通删除和 `clear()` 都是持久取消。在步骤边界,循环的内部实现会通过纯删除 splice 领取待处理输入。实时通知刻意采用逐消息的最小载荷:`agent/inbox/inserted { message }`、`agent/inbox/claimed { message, turn }` 与 `agent/inbox/discarded { message }`。
92
+
93
+ ### 源码地图
94
+
95
+ | 文件 | 职责 |
96
+ |---|---|
97
+ | [`src/index.ts`](src/index.ts) | 插件入口:`AgentRegistry`、工厂槽位、发起方作用域、`CreateAgentOptions`/`ResumeAgentOptions` |
98
+ | [`src/runtime-types.ts`](src/runtime-types.ts) | `Agent`、结构化 `Inbox`、`AgentStatus` 与 `agent/*` 事件声明 |
99
+ | [`src/types.ts`](src/types.ts) | `AgentOptions`、取消原因与收件箱投影词汇 |
100
+ | [`src/dispatch.ts`](src/dispatch.ts) | `agentEvents` 融合分发器与 `assembleContextFor(agent)` |
101
+ | [`src/consumed-work.ts`](src/consumed-work.ts) | `foldConsumedWork(events)`:日志消费掉的工作最终怎样了 |
102
+ | [`src/model-selection.ts`](src/model-selection.ts) | `installModelSelection`:把一个选择耦合到组装与路由 |
103
+
104
+ ### 注册表与生命周期
105
+
106
+ `AgentRegistry` 为每个实时 agent 保留一个条目,含其载体与创建者关系。使用已构造的 agent 前,等待 `register()` 以 `startup` 来源完成串行创建监听器;异步工厂使用拆分的 `enter()`/`announce()` 对,使 setup 与初始化始终受回滚保护。创建期间请求的 detach 会等待所有已调用的异步监听器结算,且每次 detach 都绑定到确切条目,因此陈旧 disposer 无法移除之后出现的同 id 替代项。Teardown 停止并排空循环、撤销作用域、detach agent,再 detach 会话;私有清理完成后该 id 即可复用。
107
+
108
+ ### 发起方作用域
109
+
110
+ 每个驱动器在 `ctx.agents.withInitiator(agent, ...)` 内运行其完整生命周期,因此继承的异步链会观察到该 agent;`withoutInitiator()` 为共享定时器等无关的进程本地工作隐藏它。该边界只是进程本地归因——环境中的身份既不是存活证明,也不是授权,显式身份在 worker、进程、持久化与 wire 边界保持权威。Teardown 拒绝新边界,让返回 Promise 的边界排空,然后禁用底层存储。[发起方作用域决策](../../../.agents/notes/implemented/architecture/2026-07-15-agent-initiator-scope.zh.md) 拥有详细约定。
111
+
112
+ ### 所有权不变式
113
+
114
+ `AgentHandle` disposer 是一项能力:在消费方中,只有其持有者能拆除该 agent。已注册的工厂提供方是结构化共同拥有者,因为作用域 agent 依赖该提供方的服务 API;提供方卸载会停止并排空它创建的每个实时句柄。`ctx.agents.get(id)` 仍返回裸 `Agent`——句柄只暴露给创建它的消费方。
115
+
116
+ </details>
117
+
118
+ -----
119
+
120
+ <a id="further-exploration"></a>
121
+ ## 进一步探索
122
+
123
+ 包级约定对大多数消费方已经足够;需要周边领域与设计原理时再阅读以下页面。
124
+
125
+ - [Core 子系统](../../../docs/subsystems/core.zh.md)——循环图、`Agent` 句柄、拦截决策与生成的服务 API。
126
+ - [agent-loop 包](../agent-loop/README.zh.md)——创建、驱动并拆除 agent 的默认驱动器。
127
+ - [会话子系统](../../../docs/subsystems/session.zh.md)——句柄背后的持久日志与派生历史。
128
+ - [发起方作用域 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-15-agent-initiator-scope.zh.md)——边界与 teardown 约定。
129
+ - [core 分组地图](../README.zh.md)——core 各包如何组合。
130
+
131
+ -----
132
+
133
+ <a id="model-experience"></a>
134
+ ## 模型体验
135
+
136
+ ### 用户、steering 与注入消息
137
+
138
+ #### 模型看到什么
139
+
140
+ `followup`、`steer` 与 `inject` 以带标识的 user 角色消息馈送所属会话;被接纳的内容成为模型在后续步骤中读取的派生历史的一部分。`agent/pre-step` 与其他已声明事件让插件能够拒绝拟进入的步骤或添加持久请求材料。`installModelSelection` 会在首次为不同提供方/模型路由组装且原本会发出模型请求的步骤中加入 `[model changed: assistant turns above this point were generated by <previous>; the session continues with <next>]`;仅跨提供方切换时显示提供方名称,只改变推理强度时不添加消息。第一个决策为空时,以及某个决策移除候选消息后为空时,都不会产生请求。如果请求步骤在记录请求头前失败,持久记录中的先前路由没有变化,所以下一个请求步骤会再次收到提示。
141
+
142
+ #### Token 影响
143
+
144
+ 被接纳内容成为保留历史,或成为每次请求重复的会话前缀;被阻止内容不贡献请求 token。每条实际发出的模型切换提示都会把对应文本加入保留历史。大小取决于调用方与插件。
145
+
146
+ #### KV Cache 影响
147
+
148
+ 被接纳历史与 steering 只追加;被阻止的提交不发送请求。会话前缀在循环实例内保持稳定,而新建或恢复的实例可能建立不同前缀。
149
+
150
+ ### Agent 作用域的请求组合
151
+
152
+ #### 模型看到什么
153
+
154
+ 通过 `agent.ctx` 进行的注册可以遮蔽提示词段或工具,也可以在未发布 setup 期间安装仅适用于该 agent 的拦截器,因此一个 agent 看到的提示词与工具集会与其邻居不同。模型选择会在提示词组装前捕获一次提供方/模型/推理强度值,并将其应用到同一步骤的请求;之后发生的并发变更等待下一个步骤。
155
+
156
+ #### Token 影响
157
+
158
+ 每次提供方/模型切换会增加一条简短且保留在历史中的 user 角色提示。其他带作用域贡献只影响该 agent,并在 dispose 时消失。
159
+
160
+ #### KV Cache 影响
161
+
162
+ 切换提示追加在先前历史之后,因此保留该前缀;路由变更可能使新的提供方或模型无法复用此前缀。改变提示词段、工具定义或请求监听器的 setup 或 reload,可能从第一个受影响的请求 token 起使复用失效。
163
+
164
+ ## 已知限制与延期工作
165
+
166
+ <a id="known-limitations-and-deferred-work"></a>
167
+
168
+
169
+ 这些限制说明本包何时需要特别留意。它们是当前包约束,不是任务积压。
170
+
171
+ - **发起方作用域只存在于进程内**:worker、子进程、HTTP、持久队列和重启必须显式传递所需身份。
172
+ - **环境身份可能比存活状态更久**:消费方在生命周期敏感工作前,仍要检查 `agent.status`、取消状态和所属能力约定。
173
+ - **创建监听器共享初始化生命周期。** `agent/created` 监听器不得等待 `agent.whenIdle()` 或自身所有者的 dispose:这些操作要等待创建完成。所需的异步工具与提示词安装完成后,监听器才可返回。
174
+ - **`cancel()` 默认清空收件箱**:它会中止正在处理的轮次以及排队和 steering 工作;`cancel(cause, { keepInbox: true })` 只中止轮次并保留待处理项,且不存在让轮次继续运行、只中止步骤的操作。
175
+ - **每条附加 `UserMessage` 恰好携带一个 `MessageSource`**:多个插件合并到一条消息上的贡献会归入同一来源,因此该消息无法列出多个生产者。
176
+
177
+ <a id="dev-note"></a>
178
+ ### 开发备注
179
+
180
+ <details>
181
+ <summary>维护者的工作上下文——点击展开</summary>
182
+
183
+ 本开发备注是维护者的工作上下文;它明确不具权威性。尚未决定的开放方向:委派之外的 agent 间通道——共享状态、流式子输出以及后台或轮询语义仍不在当前委派 seam 之内;以及 `SessionStartSource` 的 `'clear'`/`'compact'` 值已保留但尚无发出方,待驱动子系统落地。
184
+
185
+ </details>