dsh-bailinghub 0.4.0 → 0.6.0
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/CHANGELOG.md +108 -0
- package/PRIVACY.md +62 -12
- package/README.md +50 -20
- package/SECURITY.md +47 -6
- package/docs/AGENT_CLIENT_CONTRACT.md +201 -16
- package/docs/CAPABILITY_FEEDBACK.md +199 -0
- package/docs/COMPATIBILITY.md +63 -5
- package/docs/CROSS_SYSTEM_CONVERSATIONS.md +138 -0
- package/docs/GENERATED_ARTIFACTS.md +77 -0
- package/docs/GETTING_STARTED.md +37 -11
- package/docs/GETTING_STARTED.zh-CN.md +32 -12
- package/docs/INVOCATION_RECOVERY.md +182 -0
- package/docs/MIGRATION_VNEXT.md +78 -7
- package/docs/PROJECT_BOUNDARIES.md +4 -1
- package/docs/README.zh-CN.md +59 -18
- package/docs/RELEASE_NOTES_v0.5.0.md +79 -0
- package/docs/RELEASE_NOTES_v0.6.0.en.md +39 -0
- package/docs/RELEASE_NOTES_v0.6.0.md +39 -0
- package/docs/SESSION_TOOL_REUSE.md +213 -0
- package/docs/TASK_CONTROL.md +183 -0
- package/docs/UPGRADE_v0.6.0.en.md +71 -0
- package/docs/UPGRADE_v0.6.0.md +73 -0
- package/lib/artifact-store.js +56 -0
- package/lib/artifacts.js +132 -0
- package/lib/authorizations.js +23 -8
- package/lib/capability-feedback.js +138 -0
- package/lib/conversation-archive-store.js +2 -2
- package/lib/conversation-outbox.js +37 -7
- package/lib/index.js +7 -1
- package/lib/invocation-journal.js +223 -0
- package/lib/invocation-store.js +275 -0
- package/lib/runtime.js +1126 -174
- package/lib/session-scope-store.js +2 -2
- package/lib/session-scope.js +146 -30
- package/lib/session-task-store.js +275 -0
- package/lib/session-task.js +237 -0
- package/lib/session-tool-cache.js +168 -0
- package/lib/session-tools.js +240 -0
- package/lib/subject-display.js +39 -0
- package/lib/system-info.js +76 -0
- package/lib/tool-catalog.js +38 -0
- package/lib/transport.js +34 -0
- package/package.json +14 -3
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Reuse business tools across user turns
|
|
2
|
+
|
|
3
|
+
This feature lets a conversation reuse recently discovered complete tool declarations while the same runtime and Session remain alive. For example, a user can find a shop product and ask for its details in the next message without another capability search.
|
|
4
|
+
|
|
5
|
+
Reusing a declaration does not reuse an earlier execution context or operation.
|
|
6
|
+
The next turn prepares current rules, context, authorization and a new run for
|
|
7
|
+
the intended target before a new business operation can execute.
|
|
8
|
+
|
|
9
|
+
This feature is opt-in with `toolLifecycle: 'session'`; default `active_turn` behavior is unchanged. Use DSH 0.6.0 with SDK 0.6.0 and Core 0.8.0 for the complete release set.
|
|
10
|
+
|
|
11
|
+
## Enable it in the host
|
|
12
|
+
|
|
13
|
+
Pass the lifecycle option when creating the Agent Client plugin, alongside the
|
|
14
|
+
host's existing stores and transport options:
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
import { createAgentClientPlugin } from 'dsh-bailinghub'
|
|
18
|
+
|
|
19
|
+
const plugin = createAgentClientPlugin({
|
|
20
|
+
...existingHostOptions,
|
|
21
|
+
toolLifecycle: 'session',
|
|
22
|
+
})
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Omitting the option, or setting `toolLifecycle: 'active_turn'`, preserves the
|
|
26
|
+
existing lifecycle: tools are retained through searches within one user turn,
|
|
27
|
+
then the next turn discovers its usable tools again. The default plugin entry
|
|
28
|
+
point and existing configuration files keep that behavior. This is a host
|
|
29
|
+
integration option, not an extra setting an ordinary user must manage.
|
|
30
|
+
|
|
31
|
+
The host must keep the same real DSH Session and runtime between messages. A new
|
|
32
|
+
Session, recreated runtime or host restart begins with an empty declaration
|
|
33
|
+
cache. Restoring the persistent original-invocation journal does not restore
|
|
34
|
+
cached tools; the two mechanisms have separate purposes.
|
|
35
|
+
|
|
36
|
+
## Two lifetimes
|
|
37
|
+
|
|
38
|
+
| Item | Lifetime and meaning |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| `toolset.lifetime=active_turn` | Current execution registrations and target preparation belong to this user turn |
|
|
41
|
+
| `toolset.reuse=session_runtime` | This host enabled declaration reuse between turns of the same living Session |
|
|
42
|
+
| `cache.lifetime=session_runtime` | Cached declarations may survive turn completion, but are not current permission or an active business run |
|
|
43
|
+
| Current run and context | Created lazily for the selected target in the current turn; never carried over as authority |
|
|
44
|
+
| Original invocation | Keeps its original authorization, arguments, run and ID for recovery, independently of the tool cache |
|
|
45
|
+
|
|
46
|
+
There is no fixed “rediscover every N messages” rule. Each turn must prepare a
|
|
47
|
+
target when business work is first needed. Existing identity, permission and
|
|
48
|
+
capability-revision checks still apply to execution. An unchanged, valid cached
|
|
49
|
+
declaration can be reused; a changed revision, missing entry or invalidated
|
|
50
|
+
declaration requires the current authoritative declaration.
|
|
51
|
+
|
|
52
|
+
The cache retains at most **64 target-tool pairs across the entire Session** and
|
|
53
|
+
at most **2 MiB of declaration and cache metadata JSON**. The full-schema prompt
|
|
54
|
+
window is **12 tools across targets**. These are cache and presentation budgets,
|
|
55
|
+
not limits on operations, messages, products or task duration. A declaration for
|
|
56
|
+
two separately authorized targets consumes two cache entries, even when their
|
|
57
|
+
parameters are identical. Recent use influences bounded eviction.
|
|
58
|
+
|
|
59
|
+
Only normalized declarations and trusted target/revision bindings are cached.
|
|
60
|
+
Previous run context, credentials, invocation arguments and business results are
|
|
61
|
+
not stored in this cache. All cache entries are memory-only in this phase.
|
|
62
|
+
|
|
63
|
+
## How the model should use tools
|
|
64
|
+
|
|
65
|
+
The existing `search_business_capabilities` tool is also the preparation and
|
|
66
|
+
schema-retrieval entry point in session mode. It accepts an optional exact
|
|
67
|
+
`tool_name`; use the `original_name` from `cached_tools`, not a generated alias,
|
|
68
|
+
an account display name or a guessed capability name.
|
|
69
|
+
|
|
70
|
+
1. Select the intended `authorization_ref` from the current Session's scope.
|
|
71
|
+
It is required for multi-target preparation, including two accounts in the
|
|
72
|
+
same system. A single-target Session can omit it.
|
|
73
|
+
2. Check `targets[].preparation_state`. Use `search_business_capabilities` with
|
|
74
|
+
a short target-specific `query` and `tool_name` when reusing an exact known
|
|
75
|
+
tool. Keep the query within 500 characters and avoid unrelated target data.
|
|
76
|
+
3. Read the returned `contexts` and complete `tool_schemas` before deciding on
|
|
77
|
+
the business call. Preparation explicitly reports
|
|
78
|
+
`business_operation_performed=false`; it is not a business receipt.
|
|
79
|
+
4. Call a ready tool using its current schema and selected target. It can be
|
|
80
|
+
called repeatedly within the turn without repeating capability search.
|
|
81
|
+
5. For a new kind of task, omit `tool_name` and search with a relevant query.
|
|
82
|
+
A broad query still requests ranked discovery; caching does not convert it
|
|
83
|
+
into a promise to enumerate the entire catalog.
|
|
84
|
+
|
|
85
|
+
If a ready tool's full schema is outside the presentation window, use the exact
|
|
86
|
+
`tool_name` to bring its declaration back into the window. The declaration can
|
|
87
|
+
come from local cache, while normal scope validation may still make requests.
|
|
88
|
+
The model must not guess fields or rely on an old schema that it cannot inspect.
|
|
89
|
+
|
|
90
|
+
An exact valid cache hit avoids an additional `searchCapabilities` request. The
|
|
91
|
+
first preparation of a target still calls `startTurn` for current context; Core
|
|
92
|
+
may perform its existing bounded internal discovery there. This is reduced
|
|
93
|
+
duplicate discovery, not a promise of zero network requests or zero server-side
|
|
94
|
+
retrieval.
|
|
95
|
+
|
|
96
|
+
## Reading the returned state
|
|
97
|
+
|
|
98
|
+
Both model-facing preparation results and the read-only host method
|
|
99
|
+
`runtime.getSessionToolState(sessionId)` expose the following distinctions:
|
|
100
|
+
|
|
101
|
+
| Field | Meaning |
|
|
102
|
+
| --- | --- |
|
|
103
|
+
| `targets[].preparation_state=not_prepared` | Selected target has no current-turn run yet; cached tools are not evidence that it is absent or expired |
|
|
104
|
+
| `targets[].preparation_state=ready` | Target has current-turn context and an active run; individual tool validity still matters |
|
|
105
|
+
| `active_tools` | Tools ready for business dispatch with the listed current-turn authorizations |
|
|
106
|
+
| `cached_tools` | Cached names and target bindings; `prepared_authorization_refs` identifies which targets are prepared |
|
|
107
|
+
| `cached_tools[].state=cached_unverified` | At least one associated target still needs current-turn preparation |
|
|
108
|
+
| `visible_tools` | Ready tools in the current full-schema presentation window |
|
|
109
|
+
| `cache.tool_count`, `limit` | Stored target-tool pair count and shared cache ceiling |
|
|
110
|
+
| `cache.bytes`, `byte_limit` | Retained JSON bytes and byte ceiling |
|
|
111
|
+
| `toolset.cached_count` | Shared cached tool-name view; it can differ from the target-tool pair count |
|
|
112
|
+
| `authorizations[].source=cache` | Exact declaration was available after target preparation; no extra search request was needed |
|
|
113
|
+
| `authorizations[].source=discovery` | A current capability search was required |
|
|
114
|
+
| `authorizations[].requested_tool_available=false` | The requested exact tool was not obtained; inspect the returned declarations and feedback instead of calling it blindly |
|
|
115
|
+
|
|
116
|
+
State can be `inactive` or `blocked`; neither permits dispatch. A prepared target
|
|
117
|
+
does not imply that every possible tool in its business system is available.
|
|
118
|
+
Discovery counts keep the separate meanings defined in
|
|
119
|
+
[capability feedback](CAPABILITY_FEEDBACK.md#search-result-meanings).
|
|
120
|
+
|
|
121
|
+
## If the model directly calls an unprepared cached tool
|
|
122
|
+
|
|
123
|
+
The retained name has a preparation guard. If the target is not ready, the guard
|
|
124
|
+
prepares that exact target and declaration, then returns context and schema with
|
|
125
|
+
`business_operation_performed=false`. It does **not** execute the requested
|
|
126
|
+
business arguments. The model must read the result and decide on a new tool call
|
|
127
|
+
using the current instructions and schema.
|
|
128
|
+
|
|
129
|
+
The original guard call ID stays attached to preparation. Replaying that same
|
|
130
|
+
call ID returns its preparation result, rather than turning the old attempt into
|
|
131
|
+
a write after preparation finishes. A later business call needs its own genuine
|
|
132
|
+
model-issued call ID. Hosts must not silently replay or re-label the first call
|
|
133
|
+
as an operation, and must not display a successful preparation as “product
|
|
134
|
+
updated.” A failed preparation remains a failure or unavailable result.
|
|
135
|
+
|
|
136
|
+
This guard is a fallback. Providing the preparation guidance and current state
|
|
137
|
+
to the model before it acts is the normal path and avoids unnecessary surprise.
|
|
138
|
+
Calls that enter concurrently before preparation finishes remain preparation-only.
|
|
139
|
+
The host must return preparation to the model before requesting the next business
|
|
140
|
+
decision. Do not queue a previously planned write behind preparation and silently
|
|
141
|
+
issue it under a new call ID. The runtime cannot establish that a custom host has
|
|
142
|
+
actually shown a response to the model merely by observing a later request.
|
|
143
|
+
|
|
144
|
+
## Example: shop and inventory conversation
|
|
145
|
+
|
|
146
|
+
In the first message, the user asks to find a shop product. The Agent selects the
|
|
147
|
+
shop authorization, searches product capabilities and calls the query tool.
|
|
148
|
+
|
|
149
|
+
In the next message, the user asks for that product's details. The Agent sees
|
|
150
|
+
the cached original tool name, prepares it for the same shop authorization,
|
|
151
|
+
reads current shop context and uses the returned schema. If the declaration is
|
|
152
|
+
still valid, no additional capability-search request is needed. The new query
|
|
153
|
+
uses the new turn's run, not the previous product-query run.
|
|
154
|
+
|
|
155
|
+
The user then asks to check warehouse stock. The Agent selects the separately
|
|
156
|
+
authorized inventory target and discovers its capabilities. Matching tool names
|
|
157
|
+
or product identifiers do not establish a cross-system relationship; the Agent
|
|
158
|
+
must use the business systems' verified identifiers and declared relationships.
|
|
159
|
+
Returning to a still-ready shop tool in the same turn requires no new search.
|
|
160
|
+
|
|
161
|
+
Simply saying “thanks” does not prepare either system or create business runs.
|
|
162
|
+
Scope validation, controlled system metadata or archive negotiation may still
|
|
163
|
+
occur under their existing contracts; no tool cache restores historical writes.
|
|
164
|
+
|
|
165
|
+
## Invalidation and original-operation recovery
|
|
166
|
+
|
|
167
|
+
The original complete selected scope is revalidated. Revocation or a changed
|
|
168
|
+
binding blocks the scope; it never falls back to a default account, another
|
|
169
|
+
Agent Session or the surviving subset. Target keys include the trusted original
|
|
170
|
+
binding, so same-named tools cannot acquire a different target from display text.
|
|
171
|
+
|
|
172
|
+
Current capability revision changes discard the affected target's old cache.
|
|
173
|
+
A same-revision, same-name contradictory declaration is quarantined rather than
|
|
174
|
+
silently replacing its meaning. Known conflicts survive tool eviction and local
|
|
175
|
+
invalidation; an authoritative new revision can clear them. Cache or metadata
|
|
176
|
+
budget pressure cannot expand authorization or establish that a missing tool
|
|
177
|
+
never existed. Re-discover only within the original selected target.
|
|
178
|
+
|
|
179
|
+
An operation already issued is different from a missing declaration. Awaiting
|
|
180
|
+
approval or uncertain outcomes keep the original invocation ID and run link.
|
|
181
|
+
Use `resume_governed_tool_invocation` for the original operation; never create a
|
|
182
|
+
replacement write after preparing tools. `inspect_original` means stop automatic
|
|
183
|
+
retries and inspect the original outcome. See
|
|
184
|
+
[durable invocation recovery](INVOCATION_RECOVERY.md).
|
|
185
|
+
|
|
186
|
+
Cancellation and ended turns continue to reject late tool activation. Cache
|
|
187
|
+
reuse does not weaken local storage errors, archive ACK/CAS, original-run audit
|
|
188
|
+
links, attachment identity or existing business approval rules.
|
|
189
|
+
Temporary target-preparation transport failures can be retried in the same
|
|
190
|
+
runtime and user turn. The retry preserves the original turn ID and initial
|
|
191
|
+
target-specific input, including after a lost response. A cancelled preparation
|
|
192
|
+
keeps its late run link without reopening that target in the ended attempt; a
|
|
193
|
+
later user turn can prepare the original target again.
|
|
194
|
+
|
|
195
|
+
## Host acceptance checklist
|
|
196
|
+
|
|
197
|
+
- Preserve the real Session, fixed scope, original call IDs and existing stores;
|
|
198
|
+
enable session mode explicitly with the exact candidate.
|
|
199
|
+
- Keep preparation state, current context, `tool_schemas`,
|
|
200
|
+
`business_operation_performed`, structured feedback and original invocation IDs
|
|
201
|
+
in the model-visible tool result. A terse success badge alone is insufficient.
|
|
202
|
+
- Dispatch through the native tool registry, including cached preparation guards
|
|
203
|
+
and valid tools outside the schema window. Do not filter by only the latest
|
|
204
|
+
search result or `visible_tools`.
|
|
205
|
+
- Verify consecutive-message reuse, same-system accounts and cross-system
|
|
206
|
+
same-name tools, changed declarations, revocation, cache eviction, ordinary
|
|
207
|
+
chat without business runs, cancellation and preparation call-ID replay.
|
|
208
|
+
- Verify original-operation recovery independently: no repeated business write,
|
|
209
|
+
no target substitution and no loss of the original execution record.
|
|
210
|
+
|
|
211
|
+
The business backend continues to own each capability's parameters, validation,
|
|
212
|
+
permissions and approval requirements. No new business API or consumer UI setting
|
|
213
|
+
is required for this opt-in DSH lifecycle.
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# 宿主任务关联与只读回执
|
|
2
|
+
|
|
3
|
+
配套版本:Core 0.8.0 / SDK 0.6.0 / DSH 0.6.0。新能力需部署方升级并按宿主契约接入;业务权限、审批和原身份约束保持。
|
|
4
|
+
|
|
5
|
+
## 宿主接口
|
|
6
|
+
|
|
7
|
+
任务由受控管理端建立;DSH 和模型不创建任务、扩大预算、暂停、继续或取消任务。
|
|
8
|
+
模型工具不接受 task ID。宿主先按原流程选择完整授权集合,再绑定管理端返回的 task:
|
|
9
|
+
|
|
10
|
+
```js
|
|
11
|
+
import {
|
|
12
|
+
createAgentClientPlugin, createFileSessionScopeStore,
|
|
13
|
+
createFileInvocationStore, createFileSessionTaskStore,
|
|
14
|
+
} from 'dsh-bailinghub'
|
|
15
|
+
|
|
16
|
+
const plugin = createAgentClientPlugin({
|
|
17
|
+
scopeStore: createFileSessionScopeStore({ directory: '/host/session-scopes' }),
|
|
18
|
+
invocationStore: createFileInvocationStore({ directory: '/host/invocations' }),
|
|
19
|
+
taskStore: createFileSessionTaskStore({ directory: '/host/session-tasks' }),
|
|
20
|
+
toolLifecycle: 'session',
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
// runtime 来自 ctx.get('bailingHubAgentClient');session 是真实宿主 Session。
|
|
24
|
+
await runtime.setSessionScope(session.id, { connectionKeys: originalSelectedKeys })
|
|
25
|
+
const coordinates = await runtime.getSessionTaskCoordinates(session)
|
|
26
|
+
// 仅 ready 时,将原成员坐标提供给受控管理端。它们不是执行许可。
|
|
27
|
+
// 管理端建立任务并返回 confirmedTaskId;普通聊天不需要建立任务。
|
|
28
|
+
await runtime.setSessionTaskBinding(session, { taskId: confirmedTaskId })
|
|
29
|
+
const state = await runtime.getSessionTaskState(session)
|
|
30
|
+
// 重开:先恢复原 scope,再恢复 task;这些操作不创建 run 或继续业务调用。
|
|
31
|
+
await runtime.restoreSessionScope(session.id)
|
|
32
|
+
await runtime.restoreSessionTaskBinding(session)
|
|
33
|
+
await runtime.restoreSessionInvocations(session.id)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
三个 task 方法也接受原 Session ID 字符串。set 只接受 `{ taskId }`,不接受本地策略、预算或成员替换。
|
|
37
|
+
默认插件使用 DSH_HOME 下的私有 sidecar;注入自定义 scopeStore 的宿主必须明确注入 taskStore 和 invocationStore。
|
|
38
|
+
内存适配器仅用于测试,不提供跨进程持久化。存储目录 0700、文件 0600,原子写入、锁和 revision CAS 与既有 journal 一致。
|
|
39
|
+
|
|
40
|
+
### 没有进行中的对话轮次,也能核对或继续原调用
|
|
41
|
+
|
|
42
|
+
例如,商城商品上架正在等待审批,用户关闭客户端后重新打开会话。面板可以直接“核对结果”,
|
|
43
|
+
不必伪造一条用户消息或让模型重新执行上架。用户明确点击“继续原调用”时,才考虑继续原操作。
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
// session 必须是真实、保留原 events/unsavedEvents 的宿主 Session,不接受 ID 字符串。
|
|
47
|
+
const inspected = await runtime.inspectSessionInvocation(session, originalInvocationId, { signal })
|
|
48
|
+
// 仅在用户明确要求继续时调用。不要把此方法用于自动轮询。
|
|
49
|
+
const continued = await runtime.resumeSessionInvocation(session, originalInvocationId, { signal })
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
仅接受原 invocation ID 和可选 AbortSignal,不接受新目标、参数、run 或 task。
|
|
53
|
+
原 ID 来自受保护的持久 invocation store,不能从聊天正文补造记录。
|
|
54
|
+
两种动作均重验整组选定身份、固定范围、原 journal、task 与 CAS;不创建业务 run,不注入工具,
|
|
55
|
+
不改变会话范围或原审批规则。没有 task 的旧 v1 记录仍保持无 task,不能据此跳过后来生效的受管要求。
|
|
56
|
+
|
|
57
|
+
| 动作 | 行为 |
|
|
58
|
+
| --- | --- |
|
|
59
|
+
| `inspectSessionInvocation` | 只读原服务端 receipt;即使审批已通过也不执行。暂停或取消的原任务仍可核对原结果。 |
|
|
60
|
+
| `resumeSessionInvocation` | 先核对原 receipt。只有明确 `not_dispatched`、无派发 journal 且状态可继续,才发送最多一次原 resume POST。后续等待使用 GET。 |
|
|
61
|
+
| 已派发、结果未知、缺少结果 | 只核对原结果,不新造调用;未知结果不能因“继续”按钮变成替代写入。 |
|
|
62
|
+
| 当前任务暂停、取消或被阻断 | 核对仍按权限进行;继续受原任务门禁限制。额度与并发限制由 Core 在实际派发时权威判断。 |
|
|
63
|
+
|
|
64
|
+
结果 envelope:`schema=bailing.agent-session-invocation-action.v1`,含 `operation`、`invocation_id`、
|
|
65
|
+
`state`、`resume_dispatched`。`ready` 表示本次核对/继续接口正常返回,**不代表业务成功或整个任务完成**。
|
|
66
|
+
读取 `receipt.result.state` 判断原业务结果;`receipt.result=null` 必须保持结果未确认。
|
|
67
|
+
`resume_dispatched=true` 表示运行时已将原 resume 请求提交给 SDK,不保证 Hub 已接收或业务已执行;
|
|
68
|
+
随后读取的 receipt 本身仍是只读。身份校验可能按 SDK 原规则刷新凭据,这不属于业务派发。
|
|
69
|
+
成功可能含 `result`(原 POST 回应)、`receipt`(其后的核对)、`next_action` 和 `retry_after_ms`。
|
|
70
|
+
未确定结果时继续用 inspect 原 ID;无隐式轮询、定时自动继续或替代 invocation。
|
|
71
|
+
|
|
72
|
+
失败返回 `blocked / unavailable / unsupported / storage_error / recovery_gap / cancelled`,
|
|
73
|
+
附 `reason` 与现有 `bailing.agent-feedback.v1`,不同时返回可操作的成功结果。
|
|
74
|
+
本地保存失败、`unsavedEvents` 和已确认历史缺口优先;已发出请求后出现这些错误,
|
|
75
|
+
`feedback.dispatch=unknown`,不能把它解释为“业务肯定没执行”。宿主显示主错误并保留原调用 ID。
|
|
76
|
+
网络恢复后可在同一 runtime 重试核对;明确原身份改变仍整组阻断,不退默认目标或剩余授权。
|
|
77
|
+
|
|
78
|
+
重开时只读加载原本地 outbox 以判断已保存历史,不创建归档、补原文或同步正文。
|
|
79
|
+
只读 GET 中相同限流结果保留原绝对等待截止;新的 POST 限流结果可以建立新的等待窗口。
|
|
80
|
+
宿主和模型对同一 Session / invocation 共享运行时串行控制;跨进程仍依赖原 journal CAS 和 Core 原 ID 幂等规则。
|
|
81
|
+
面板 AbortSignal 与对话轮次分开,关闭 Session/runtime 会取消相关面板动作。迟到响应至多保存原调用事实,
|
|
82
|
+
不会复活工具或恢复已取消任务。宿主应在面板操作取消时 abort,不能把旧响应写入另一个会话。
|
|
83
|
+
|
|
84
|
+
旧 SDK/Core 不支持只读 receipt 时明确 `unsupported`,不会将读取降级为 resume。
|
|
85
|
+
缺少持久 invocation store 不猜补原绑定。原“填写草稿 → 用户发送 → 模型工具”流程继续可用。
|
|
86
|
+
|
|
87
|
+
### 读取原任务坐标
|
|
88
|
+
|
|
89
|
+
例如,同一会话已选择商城和库存系统的授权,宿主需要让管理端建立一个累计预算任务。
|
|
90
|
+
调用 `getSessionTaskCoordinates(session)` 即可获得原会话、原 Agent Session、接入方和 workspace,
|
|
91
|
+
不必读取 `sessionScopes.snapshot()` / `sessionTasks.conversationId()` 或自己计算会话标识。
|
|
92
|
+
该方法也接受原 Session ID 字符串;推荐传入真实 Session,以同步检查已知的 `unsavedEvents` 和可见历史缺口。
|
|
93
|
+
|
|
94
|
+
成功结果为:
|
|
95
|
+
|
|
96
|
+
```js
|
|
97
|
+
{
|
|
98
|
+
schema: 'bailing.agent-session-task-coordinates.v1',
|
|
99
|
+
state: 'ready', availability: 'supported',
|
|
100
|
+
sessionId: 'original-host-session-id',
|
|
101
|
+
clientConversationId: 'dsh.conversation.…',
|
|
102
|
+
scopeRevision: 2, scopeLocked: false,
|
|
103
|
+
members: [{
|
|
104
|
+
connectionKey: 'conn_…',
|
|
105
|
+
agentSessionId: 'original-agent-session-uuid',
|
|
106
|
+
clientAppId: 'shop_app', workspace: 'shop'
|
|
107
|
+
}],
|
|
108
|
+
snapshot_is_dispatch_permission: false
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
- 单授权、同系统多授权、同 Hub 跨系统使用同一结构;只返回当前已选成员,不含地址、凭据、名称、正文、参数或工具清单。
|
|
113
|
+
- 每次重新核验完整原范围及任务协议;已绑定时同时核对原任务成员。临时失败可在同一 runtime 上重试,不能取剩余子集或默认授权。
|
|
114
|
+
- 读取不保存、锁定或修复 sidecar,不建立任务、run,不搜索、调用、查询回执或继续业务操作。授权探针由 SDK 按已有机制维护凭据;“只读”不禁止正常凭据刷新。
|
|
115
|
+
- 草稿可读但保持未锁定;重开未锁定草稿仍须显式重新确认。已开始且持久保存的原范围按原规则恢复,缺失记录不猜补。异步改选、持久 CAS 变化会使本次读取失败。
|
|
116
|
+
- `scopeRevision` 只描述读取时的范围修订,不能当成 task 的 `scope_hash`。绑定仍通过 `setSessionTaskBinding` 重新验证,不能直接写 sidecar。
|
|
117
|
+
- 暂停或取消任务的坐标仍可能为 ready;它不表示任务可执行,也不表示宿主允许此时绑定。首消息前才允许绑定等产品规则由宿主判断,不由投影隐式改变。
|
|
118
|
+
|
|
119
|
+
非 ready 结果**不含** `sessionId/clientConversationId/scopeRevision/scopeLocked/members`,不能沿用界面缓存的旧坐标:
|
|
120
|
+
|
|
121
|
+
| state | 含义与处理 |
|
|
122
|
+
| --- | --- |
|
|
123
|
+
| `inactive` | 未选或显式空范围,reason 为 `SESSION_SCOPE_UNSELECTED` / `SESSION_SCOPE_CHAT_ONLY`;availability=`not_checked`,零 Hub 请求 |
|
|
124
|
+
| `unsupported` | SDK/Core 明确不支持任务协议;`TASK_UNSUPPORTED` 等,检查配套;不破坏普通未受管多授权流程 |
|
|
125
|
+
| `unavailable` | 临时网络失败、429/5xx 或原身份尚未完成验证;保持原范围,恢复连接后重试只读方法 |
|
|
126
|
+
| `blocked` | 原身份撤销/改绑、范围待确认、CAS 冲突、任务成员不匹配或未分类错误;按 reason/feedback 处理,禁止自动换授权 |
|
|
127
|
+
| `storage_error` | scope/task 持久读取失败或已知未保存消息、invocation、归档;优先解决原存储,不能清队列或创建替代调用 |
|
|
128
|
+
| `recovery_gap` | 当前可见历史尚未被原 outbox 完整覆盖;保留原事件并沿现有恢复流程核对 |
|
|
129
|
+
|
|
130
|
+
错误沿用 `bailing.agent-feedback.v1`,`operation=task_coordinates`、`dispatch=not_dispatched`,
|
|
131
|
+
提供安全的 `code/category/retryable/next_action`,不要求解析错误文本。
|
|
132
|
+
CAS 的 `SCOPE_STORE_CONFLICT` / `TASK_STORE_CONFLICT` 与读盘失败的 `*_STORE_UNAVAILABLE` 分开;
|
|
133
|
+
`SESSION_SCOPE_CONFLICT` 表示读取期间范围变化。已知本地存储错误优先于网络及 unsupported,随后才是 recovery_gap。
|
|
134
|
+
原 journal/outbox 的持久恢复仍需宿主正常调用既有恢复接口;本投影不代替恢复,也不证明尚未装载的本地存储完整。
|
|
135
|
+
|
|
136
|
+
旧版本缺少此方法时,宿主只将任务坐标功能显示为 unsupported,保留普通会话,不能回退读取内部协调器。
|
|
137
|
+
此宿主坐标方法本身不新增业务后端要求;完整版本按配套升级指南执行 Core、SDK 和 DSH 升级。
|
|
138
|
+
它没有新增面板直接 inspect/resume 方法,现有“填入草稿→用户发送→原模型工具”流程继续有效。
|
|
139
|
+
|
|
140
|
+
set 会锁定已选 scope,逐个查询所有原成员,并核对同 Hub、原 Agent Session、client、workspace、conversation、完整 member_count、scope_hash 和不可变工具集合。
|
|
141
|
+
任一成员失效、缺失、换绑或跨 Hub 都不能用剩余子集执行。暂时失败保留原关联,恢复仍验证整组。
|
|
142
|
+
已绑定任务不可切换或删除;已有非受管业务 run 的 Session 不能中途改绑。
|
|
143
|
+
任务确认失败后也不能静默降级成未受管调用。CAS 或保存回执失败保持阻断,restore 只重验或保存同一原关联,不自动创建任务或执行请求。
|
|
144
|
+
|
|
145
|
+
## 执行和恢复
|
|
146
|
+
|
|
147
|
+
受管 Session 对实际使用目标按需 startTurn,宿主 options 注入原 taskBinding,SDK 验证 Core 回显。
|
|
148
|
+
聊天、目录加载、task 查询、重开、缓存恢复都不会自动创建任务或业务 run。
|
|
149
|
+
受管 Session 保留最多 64 条声明与 12 条完整 schema 窗口;下一轮只准备实际目标,声明有效时无需再次远端搜索。
|
|
150
|
+
缓存或 task 快照都不是派发许可,每笔业务仍由 Core 原子裁决。
|
|
151
|
+
同一受管操作合并重复身份探针:每次实际 invoke/resume 前仍验证全部原成员和最新任务状态,GET/restore 也重新验证。
|
|
152
|
+
若继续操作需要等待,等待结束后重新验证;同 runtime 断网后必须重验原组才能恢复,不跨操作缓存授权证明。
|
|
153
|
+
固定原绑定可复用协议支持证明;不缓存任务状态、成员身份、unsupported 或未绑定任务时的 required/optional 判定。
|
|
154
|
+
|
|
155
|
+
- `inspect_governed_tool_invocation` 仅接受原 invocation_id,读取 SDK GET receipt,并与可信 journal 的 run、工具、route、task 和完整原 scope 比对。没有可靠 journal 时不能根据聊天文本猜补身份。
|
|
156
|
+
- 受管后台等待只用 inspect,审批已批准也不会触发 POST resume。
|
|
157
|
+
- `resume_governed_tool_invocation` 表示明确继续原操作;每次显式动作只发一次原 resume,后续等待用 GET。任务暂停、取消、到期或预算拒绝不会给出重新搜索、换授权或换调用 ID 的建议。
|
|
158
|
+
- 取消不回滚已经产生的业务效果。迟到回执仍可保存原事实,但不能恢复已结束轮次的工具或触发后续派发。
|
|
159
|
+
- 旧 Core 的明确 unsupported 且 optional 流程继续原有行为;网络失败不是 unsupported。required 时缺 task 或不兼容 SDK 会阻断,不能降级。
|
|
160
|
+
|
|
161
|
+
`getSessionTaskState` 同时提供 task_state、控制 revision、计量 counters、policy、协议 availability、local 和 unsavedRecords。
|
|
162
|
+
计量是唯一写 invocation,不是成功数、商品数或附件数;object_metering 明确 unsupported。
|
|
163
|
+
`state` 优先保留 storage_error 和 recovery_gap,因此它可能与 task_state=cancelled 并存;local 保留各存储状态、unsavedEvents 和 unsavedInvocations。
|
|
164
|
+
`getSessionToolState` 和工具准备结果包含同一紧凑 task 摘要,不重新下载任务列表。
|
|
165
|
+
|
|
166
|
+
## 升级与回滚
|
|
167
|
+
|
|
168
|
+
既有 journal v1 仍可读取;第一次正常写入升级为 v2。旧条目保持没有 task,不推断归属;新条目的 taskBinding 不可改绑。
|
|
169
|
+
任务 sidecar 使用 `bailing.agent-session-task.v1`,只保存原 scope 指纹、原成员坐标和 task 引用,不保存凭据、聊天或业务参数。
|
|
170
|
+
|
|
171
|
+
运行前备份 task、scope、invocation 和会话归档存储。包含受管任务或 v2 journal 的 Session 不应回退给不识别它们的旧宿主。
|
|
172
|
+
Core 的 managed enrollment 是粘性的;回退客户端、删 sidecar 或换任务都不能用来解除服务端 task 要求。
|
|
173
|
+
未受管旧 Session 不需要自动创建任务或补写旧条目。部署与数据库升级顺序见配套升级指南。
|
|
174
|
+
|
|
175
|
+
## 合成验证
|
|
176
|
+
|
|
177
|
+
```sh
|
|
178
|
+
BAILINGHUB_SDK_DIST=/absolute/path/to/paired-sdk/dist/sdk.js npm run verify
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
集成测试使用真实 DSH Session、真实配套 SDK 和 loopback HTTP,隔离临时文件存储。
|
|
182
|
+
覆盖跨轮缓存、lazy run、整组检查、required/optional、CAS 失败和丢失保存回执、完整重开、暂停/取消、显式继续、只读 receipt、身份串换、迟到响应和存储优先级。
|
|
183
|
+
它证明 DSH/SDK 接线,不替代真实 Core 数据库事务/HTTP 派发验收,也不涉及真实业务写入。
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Paired upgrade guide for 0.6.0: longer tasks and attachment delivery
|
|
2
|
+
|
|
3
|
+
Upgrade Core 0.7.0 / SDK 0.5.0 / DSH 0.5.0 to Core 0.8.0 / SDK 0.6.0 / DSH 0.6.0 using the deployment-specific release procedure. Preserve original configuration and persistent records.
|
|
4
|
+
|
|
5
|
+
## Choose the required features
|
|
6
|
+
|
|
7
|
+
| Need | Core | SDK / DSH and host | Business backend |
|
|
8
|
+
| --- | --- | --- | --- |
|
|
9
|
+
| Configure Hub tool limits | New Core, migration 061 and policies | Paired packages for retry guidance and original-call recovery | Keep APIs/declarations; backend limits remain independent |
|
|
10
|
+
| Use generated or explicitly selected images | New Core, 060, storage and workspace switch | New SDK, controlled source and durable upload store in the DSH host | Existing URL APIs work; asset IDs/imports remain business operations |
|
|
11
|
+
| Switch tools within a turn | Paired Core for full discovery feedback | New DSH merges valid tools | No new requirement |
|
|
12
|
+
| Reuse declarations across messages | Current-turn preparation and permission checks remain | Opt into `toolLifecycle: 'session'` and handle preparation | No new requirement |
|
|
13
|
+
| Inspect/recover after reopening | New read-only receipt API | New SDK/DSH, durable invocation store and panel methods | Original invocation and approval rules remain |
|
|
14
|
+
| Task budgets, concurrency and pause | New Core, 062 and administrator-created tasks | Paired packages, task store and complete original-scope verification | Counts invocations, not products or monetary impact |
|
|
15
|
+
|
|
16
|
+
Other MCP hosts and adapters do not gain these integrations automatically. Ordinary unenrolled authorizations retain their existing flow. Unsupported interfaces must not cause fallback to a broader or default authorization.
|
|
17
|
+
|
|
18
|
+
## 1. Preserve original state
|
|
19
|
+
|
|
20
|
+
Back up the database, configuration, encryption material, storage settings and objects. Original argument snapshots use a key derived from the instance's `server.token`. Do not combine a normal upgrade with an unplanned rotation: an old snapshot that cannot be decrypted must block recovery rather than be reconstructed.
|
|
21
|
+
|
|
22
|
+
Hosts retain secure credentials, connection instance keys, real Sessions, fixed scopes, events, archive outbox/ACK/CAS, upload and invocation journals, and task bindings. Protect these private stores; do not commit or publish them. An upgrade should not recreate authorizations or conversations.
|
|
23
|
+
|
|
24
|
+
## 2. Upgrade Core and apply migrations
|
|
25
|
+
|
|
26
|
+
Use final released artifacts and the deployment-specific upgrade procedure. Check the intended database, then run the official migration runner (`npm run db:init` for a source installation) for all unapplied files before starting the new service. Startup does not migrate implicitly.
|
|
27
|
+
|
|
28
|
+
| New since Core 0.7.0 | Purpose |
|
|
29
|
+
| --- | --- |
|
|
30
|
+
| `060_agent_artifacts.sql` | Original image-upload receipts; binary objects stay in operator storage |
|
|
31
|
+
| `061_tool_rate_limit_policies.sql` | Provider defaults and per-tool overrides; existing null settings inherit declarations |
|
|
32
|
+
| `062_agent_task_control.sql` | Six tables for task bindings, enforcement, invocations and budgets/permits |
|
|
33
|
+
|
|
34
|
+
Verify the migration ledger and readiness. Do not manually replay applied SQL. Older baselines also need their missing earlier migrations. Existing candidate deployments must compare migration records and checksums rather than recreate tables.
|
|
35
|
+
|
|
36
|
+
Persistent task control needs the corresponding MySQL repository. Embedded hosts lacking optional support report unsupported. A configured managed flow must block on missing tables or database failure, not fall back to unrestricted dispatch.
|
|
37
|
+
|
|
38
|
+
## 3. Upgrade the actual SDK, DSH and host
|
|
39
|
+
|
|
40
|
+
The released DSH must depend on the exact paired SDK, with a matching lockfile. Editing the source manifest alone does not upgrade a desktop Profile or packaged application. Verify actual consumer resolution and nested copies. An earlier same-numbered candidate is identified by hash; it is not the public 0.5.0 package.
|
|
41
|
+
|
|
42
|
+
- Cross-turn reuse: enable `toolLifecycle: 'session'`, preserve the real Session/runtime and handle preparation with current schemas. `active_turn` remains the default. Restart empties the declaration cache; the separate invocation journal can still recover original operations.
|
|
43
|
+
- Recovery and panels: persist `invocationStore`; use `inspectSessionInvocation` / `resumeSessionInvocation` with the original real Session, ID and cancellation signal. Reads never invoke resume. Null results stay unconfirmed; storage errors and history gaps cannot display stale success.
|
|
44
|
+
- Tasks: persist `taskStore`, obtain original coordinates with `getSessionTaskCoordinates` and bind the administrator-created task. Restore original scope/task/journal. The model does not choose task IDs.
|
|
45
|
+
- Images: connect a controlled `artifactSource` and durable `artifactStore`. Register only files approved for business use in the current conversation. Ordinary chat attachments are not synchronized automatically. The model chooses neither buckets nor arbitrary local paths.
|
|
46
|
+
|
|
47
|
+
Do not reconstruct missing historical bindings or arguments from text. Old SDK/Core versions without read-only receipts report unsupported; inspection must not fall back to resume.
|
|
48
|
+
|
|
49
|
+
## 4. Enable features with a small validation scope
|
|
50
|
+
|
|
51
|
+
Configure Media Storage, then select it under Agent Clients → Connection Configuration → Tools and Approval → Generated Image Upload. Objects are public in the first phase; use an image suitable for public display.
|
|
52
|
+
|
|
53
|
+
Configure provider-wide, default per-tool and individual override limits under Tool Providers → Edit → Governance. A provider gate value of `0` disables only that layer. Client-app, per-tool and backend gates remain independent. The same provider/tool shares its counter across conversations.
|
|
54
|
+
|
|
55
|
+
Create and bind a task using dedicated test authorizations. **Enrollment persists a requirement on each original Agent Session, including its other conversations and older entry points. Cancellation does not remove the marker.** Check all relevant hosts before enrolling ordinary authorizations. A task write budget of `0` means read-only; `null` means no cumulative write cap.
|
|
56
|
+
|
|
57
|
+
## Observable checks
|
|
58
|
+
|
|
59
|
+
1. Ordinary chat does not create business runs just to restore tools or inspect a panel. Unselected targets receive no requests.
|
|
60
|
+
2. Query a shop product, inspect inventory and return to the product: targets remain correct; valid tools are reused or prepared with complete schemas.
|
|
61
|
+
3. Upload a public test image, obtain a ready URL and recover the same upload. The business action using that URL has its own result or approval.
|
|
62
|
+
4. In a synthetic task, counts survive turn changes; pause stops new dispatch permits while original outcomes remain readable.
|
|
63
|
+
5. Reopen a conversation with trustworthy persisted invocations: inspect sends no business action; explicit continuation retains the original ID. Unknown writes are not replaced. Cancelling a wait is not displayed as withdrawing a business request.
|
|
64
|
+
|
|
65
|
+
Synthetic and automated checks do not replace each deployer's actual client acceptance. Focus on the enabled features and final artifacts rather than repeating unrelated tests.
|
|
66
|
+
|
|
67
|
+
## Rollback and limits
|
|
68
|
+
|
|
69
|
+
Before task enrollment, assess rollback against new persistent records and interfaces and retain migration tables/ledger. **Once enforcement markers exist, do not revert to a Core/host that ignores them or delete markers to remove control.** Pause managed dispatch and inspect in-flight calls first; prefer a forward fix. Restoring a backup requires separately reconciling business effects since that backup. Database restoration is not business rollback.
|
|
70
|
+
|
|
71
|
+
Task quotas exclude host-local tools, model inference, attachment uploads and chat archives. They do not measure product count, money or field impact. Cross-Hub tasks, full-plan automatic restart and compensation are not provided. Some host attachment-source listing errors can still surface as `unknown_failure`; they must not be displayed as an empty list. Per-item upload errors and already-ready URLs remain separate.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# 长任务与附件空间:配套升级指南
|
|
2
|
+
|
|
3
|
+
本指南从 Core 0.7.0 / SDK 0.5.0 / DSH 0.5.0 升级到 Core 0.8.0 / SDK 0.6.0 / DSH 0.6.0。按部署方式使用正式版本,保留原配置和持久记录。
|
|
4
|
+
|
|
5
|
+
## 先确定需要哪部分
|
|
6
|
+
|
|
7
|
+
| 需求 | Core | SDK / DSH 与宿主 | 业务后端 |
|
|
8
|
+
| --- | --- | --- | --- |
|
|
9
|
+
| 调整中枢工具限额 | 新 Core、061;后台设置策略 | 配套版本可展示准确等待与原调用恢复 | 原 API/声明可沿用;业务自有限流不由中枢关闭 |
|
|
10
|
+
| 生成或指定图片后用于业务 | 新 Core、060、存储及工作空间开关 | 新 SDK;DSH 宿主接文件目录和持久上传记录 | 已接收 URL 的能力可沿用;素材 ID/转存由业务系统提供 |
|
|
11
|
+
| 一轮内来回切换工具 | 配套版本用于完整发现反馈 | 新 DSH 自动合并仍有效工具 | 无新增要求 |
|
|
12
|
+
| 跨用户消息复用工具声明 | 沿用现有每轮准备和权限检查 | 新 DSH;宿主显式 `toolLifecycle: 'session'` 并处理准备结果 | 无新增要求 |
|
|
13
|
+
| 重开后恢复原调用及只读面板 | 新 Core 的只读 receipt | 新 SDK/DSH、持久 invocationStore、正式面板接口 | 原调用/审批规则保持 |
|
|
14
|
+
| 任务累计预算、并发和暂停 | 新 Core、062;管理端按需创建任务 | 新 SDK/DSH、taskStore、完整原范围核验与绑定 | 计量为调用次数;不新增商品数/金额保护承诺 |
|
|
15
|
+
|
|
16
|
+
新包不会自动让其他 MCP 客户端、生态适配器或业务后端取得这些宿主能力。普通未纳入任务的授权可继续原流程;新接口不支持时要明确报告,不能扩大授权或换默认连接。
|
|
17
|
+
|
|
18
|
+
## 第一步:保留原状态
|
|
19
|
+
|
|
20
|
+
中枢备份数据库、运行配置与密钥、存储配置和文件对象。限流恢复的原参数快照由实例 `server.token` 派生密钥加密:不要在同一次普通升级中无计划地轮换它。令牌轮换后,旧快照不能解密时应阻断恢复,不能重新猜参数。
|
|
21
|
+
|
|
22
|
+
客户端保留安全凭据、连接实例标识、真实 Session、固定范围、原事件、归档 outbox/ACK/CAS、上传和原调用账本,以及已有任务关联。保护原私有存储位置,不把这些内容提交源码或当作示例数据;升级不应重新授权或重建全部会话。
|
|
23
|
+
|
|
24
|
+
## 第二步:升级中枢并迁移
|
|
25
|
+
|
|
26
|
+
使用最终正式发行制品,按部署方式准备代码和回滚材料。确认目标数据库后,用正式迁移器(源码安装入口为 `npm run db:init`)应用全部未执行迁移,再启动新版本。启动本身不隐式迁移。
|
|
27
|
+
|
|
28
|
+
| 相对 Core 0.7.0 的迁移 | 保存内容 |
|
|
29
|
+
| --- | --- |
|
|
30
|
+
| `060_agent_artifacts.sql` | 图片上传回执与原上传关联;二进制在部署方存储中 |
|
|
31
|
+
| `061_tool_rate_limit_policies.sql` | 工具源默认及单工具覆盖策略;历史空值继承原声明 |
|
|
32
|
+
| `062_agent_task_control.sql` | 六张任务关联、强制标记、调用及额度/许可记录表 |
|
|
33
|
+
|
|
34
|
+
检查迁移账本与就绪状态。已经应用的文件不要手工重跑;从更旧版本升级时还要应用其缺少的前序迁移。已有候选部署应按账本和文件摘要核对,不重复建表。
|
|
35
|
+
|
|
36
|
+
当前任务持久控制需要相应 MySQL 仓储。缺少可选接口的嵌入式 Host 应报告不支持;一旦配置为受管模式,缺表或数据库故障必须阻断,不能静默回到无限制执行。
|
|
37
|
+
|
|
38
|
+
## 第三步:升级实际 SDK / DSH 和宿主
|
|
39
|
+
|
|
40
|
+
DSH 正式依赖必须精确指向同批 SDK,lock 也应匹配。仅改工程 `package.json` 不足以证明桌面客户端、用户 Profile 或打包产物已升级:核对实际解析来源,检查是否存在嵌套旧 SDK。早期同版本候选按哈希识别,不能把候选 0.5.0 当成公开 0.5.0。
|
|
41
|
+
|
|
42
|
+
宿主按需接入:
|
|
43
|
+
|
|
44
|
+
- 跨轮复用:开启 `toolLifecycle: 'session'`,保留真实 Session/runtime,使用当前 schema 并处理“尚未准备”。默认 `active_turn` 保持。工具缓存重启后为空,不影响另一份原调用账本的持久恢复。
|
|
45
|
+
- 恢复与面板:持久 `invocationStore`;使用正式 `inspectSessionInvocation` / `resumeSessionInvocation`,传原 Session、原 ID 和取消信号。读取不会调用 resume。空结果保持未确认,存储错误或历史缺口不能显示旧成功。
|
|
46
|
+
- 任务:持久 `taskStore`,通过 `getSessionTaskCoordinates` 提交原坐标,再绑定管理端返回的任务,恢复时保留原范围、task 和 journal。任务 ID 不交给模型选择。
|
|
47
|
+
- 图片:受控 `artifactSource`、持久 `artifactStore`,只登记当前会话获准用于业务的文件。普通聊天附件不自动同步,模型不选择桶或本机任意路径。
|
|
48
|
+
|
|
49
|
+
旧记录缺少可信原绑定或参数时,不从正文补齐。旧 SDK/Core 明确不支持只读回执时,界面显示 unsupported,不用 resume 代替查询。
|
|
50
|
+
|
|
51
|
+
## 第四步:按需启用,先小范围验证
|
|
52
|
+
|
|
53
|
+
存储:先在“媒体存储”配置,再到“智能体客户端 → 配置接入 → 工具与审批 → 生成图片上传”启用指定存储。首期公开读取;准备一张可公开的测试图片即可。
|
|
54
|
+
|
|
55
|
+
限额:在“工具源 → 编辑 → 治理”设置工具源总闸、单工具默认与覆盖策略。总闸的 `0` 只关闭该层,不会关闭单工具、接入方或业务系统自有限流。相同工具源/工具的不同会话共享计数。
|
|
56
|
+
|
|
57
|
+
任务:用专用测试授权,通过管理端创建任务并绑定。**纳入任务会为原 Agent Session 保存持续强制标记,影响它的其他会话及旧入口;取消任务不会删除标记。** 先确认所有相关宿主兼容,再决定是否纳入日常授权。任务写额度 `0` 表示只读,`null` 表示没有累计写上限,不能混淆。
|
|
58
|
+
|
|
59
|
+
## 如何确认成功
|
|
60
|
+
|
|
61
|
+
1. 只聊天时不因恢复缓存或面板而创建业务 run;未选授权不接收请求。
|
|
62
|
+
2. 查询商品、查看库存,再回到商品查询,目标正确,旧有效工具可复用;需要准备时能取得完整参数说明。
|
|
63
|
+
3. 上传公开用途测试图片后得到 ready URL,恢复时仍是原上传;业务侧使用 URL 的成功、审批或失败单独显示。
|
|
64
|
+
4. 在专用合成任务中核对累计调用数;切换轮次不重置,暂停后不授予新派发许可,原结果仍可查询。
|
|
65
|
+
5. 关闭并重开有可信原调用记录的会话,核对不派发;明确继续仍是同一原调用。结果未知不产生替代写,取消等待不被显示为“已撤回业务”。
|
|
66
|
+
|
|
67
|
+
合成与自动化通过不能替代各部署方的实际客户端验收。无需为了每次升级重新执行无关场景;重点检查本次启用的能力与最终安装包。
|
|
68
|
+
|
|
69
|
+
## 回退与限制
|
|
70
|
+
|
|
71
|
+
尚未启用任务时,仍需根据新增持久记录和接口评估回退,保留新增表与账本,不能简单删表降级。**一旦存在任务强制标记,不应回到不理解它的旧 Core/宿主,也不通过删标记解除控制。** 出现问题先暂停受管派发并核对原在途调用,优先修复前进;完整恢复备份需要单独核对备份后已发生的业务效果,不能把数据库恢复当成业务回滚。
|
|
72
|
+
|
|
73
|
+
任务不包含本地工具、模型推理、图片上传或聊天归档的资源额度,不自动度量商品数、金额或字段影响;没有跨 Hub、整项计划自动恢复或自动补偿。附件列表的某些宿主源错误仍可能显示 `unknown_failure`,宿主不能将其展示为空目录;上传逐项错误和已成功 URL 保持独立。
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto'
|
|
2
|
+
import { constants } from 'node:fs'
|
|
3
|
+
import { lstat, mkdir, open, link, unlink } from 'node:fs/promises'
|
|
4
|
+
import { resolve, join } from 'node:path'
|
|
5
|
+
|
|
6
|
+
const error = () => Object.assign(new Error('Artifact recovery metadata could not be saved or restored.'), { code: 'storage_error' })
|
|
7
|
+
const fileName = id => { if (!/^[a-f0-9]{64}$/.test(id)) throw error(); return `${id}.json` }
|
|
8
|
+
function checked(value, id) {
|
|
9
|
+
const json = JSON.stringify(value)
|
|
10
|
+
if (json.length > 16384 || value?.uploadId !== id || !value.metadata || !value.binding || !value.artifactRef || !value.sessionId) throw error()
|
|
11
|
+
return JSON.parse(json)
|
|
12
|
+
}
|
|
13
|
+
/** Test/ephemeral hosts only. Production hosts must persist the immutable recovery record. */
|
|
14
|
+
export function createMemoryArtifactStore() {
|
|
15
|
+
const records = new Map()
|
|
16
|
+
return { async get(id) { return structuredClone(records.get(id) ?? null) }, async reserve(value) {
|
|
17
|
+
if (!records.has(value.uploadId)) records.set(value.uploadId, checked(value, value.uploadId))
|
|
18
|
+
return structuredClone(records.get(value.uploadId))
|
|
19
|
+
} }
|
|
20
|
+
}
|
|
21
|
+
/** Atomic write-once receipts; no credentials or file bytes. Keep this directory across restarts. */
|
|
22
|
+
export function createFileArtifactStore({ directory } = {}) {
|
|
23
|
+
if (typeof directory !== 'string' || !directory) throw error()
|
|
24
|
+
const root = resolve(directory)
|
|
25
|
+
async function prepare() {
|
|
26
|
+
await mkdir(root, { recursive: true, mode: 0o700 })
|
|
27
|
+
const stat = await lstat(root)
|
|
28
|
+
if (!stat.isDirectory() || (process.platform !== 'win32' && (stat.mode & 0o777) !== 0o700)) throw error()
|
|
29
|
+
}
|
|
30
|
+
async function get(id) {
|
|
31
|
+
try {
|
|
32
|
+
await prepare()
|
|
33
|
+
let handle
|
|
34
|
+
try { handle = await open(join(root, fileName(id)), constants.O_RDONLY | constants.O_NOFOLLOW) }
|
|
35
|
+
catch (e) { if (e.code === 'ENOENT') return null; throw e }
|
|
36
|
+
try {
|
|
37
|
+
const stat = await handle.stat()
|
|
38
|
+
if (!stat.isFile() || stat.size > 16384 || (process.platform !== 'win32' && (stat.mode & 0o777) !== 0o600)) throw error()
|
|
39
|
+
return checked(JSON.parse(await handle.readFile('utf8')), id)
|
|
40
|
+
} finally { await handle.close() }
|
|
41
|
+
} catch { throw error() }
|
|
42
|
+
}
|
|
43
|
+
return { get, async reserve(value) {
|
|
44
|
+
const data = checked(value, value.uploadId)
|
|
45
|
+
let temporary
|
|
46
|
+
try {
|
|
47
|
+
await prepare()
|
|
48
|
+
temporary = join(root, `${randomUUID()}.tmp`)
|
|
49
|
+
const handle = await open(temporary, 'wx', 0o600)
|
|
50
|
+
try { await handle.writeFile(JSON.stringify(data)); await handle.sync() } finally { await handle.close() }
|
|
51
|
+
try { await link(temporary, join(root, fileName(value.uploadId))) } catch (e) { if (e.code !== 'EEXIST') throw e }
|
|
52
|
+
if (process.platform !== 'win32') { const dir = await open(root, 'r'); try { await dir.sync() } finally { await dir.close() } }
|
|
53
|
+
return await get(value.uploadId)
|
|
54
|
+
} catch { throw error() } finally { if (temporary) await unlink(temporary).catch(() => {}) }
|
|
55
|
+
} }
|
|
56
|
+
}
|