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.
Files changed (43) hide show
  1. package/CHANGELOG.md +108 -0
  2. package/PRIVACY.md +62 -12
  3. package/README.md +50 -20
  4. package/SECURITY.md +47 -6
  5. package/docs/AGENT_CLIENT_CONTRACT.md +201 -16
  6. package/docs/CAPABILITY_FEEDBACK.md +199 -0
  7. package/docs/COMPATIBILITY.md +63 -5
  8. package/docs/CROSS_SYSTEM_CONVERSATIONS.md +138 -0
  9. package/docs/GENERATED_ARTIFACTS.md +77 -0
  10. package/docs/GETTING_STARTED.md +37 -11
  11. package/docs/GETTING_STARTED.zh-CN.md +32 -12
  12. package/docs/INVOCATION_RECOVERY.md +182 -0
  13. package/docs/MIGRATION_VNEXT.md +78 -7
  14. package/docs/PROJECT_BOUNDARIES.md +4 -1
  15. package/docs/README.zh-CN.md +59 -18
  16. package/docs/RELEASE_NOTES_v0.5.0.md +79 -0
  17. package/docs/RELEASE_NOTES_v0.6.0.en.md +39 -0
  18. package/docs/RELEASE_NOTES_v0.6.0.md +39 -0
  19. package/docs/SESSION_TOOL_REUSE.md +213 -0
  20. package/docs/TASK_CONTROL.md +183 -0
  21. package/docs/UPGRADE_v0.6.0.en.md +71 -0
  22. package/docs/UPGRADE_v0.6.0.md +73 -0
  23. package/lib/artifact-store.js +56 -0
  24. package/lib/artifacts.js +132 -0
  25. package/lib/authorizations.js +23 -8
  26. package/lib/capability-feedback.js +138 -0
  27. package/lib/conversation-archive-store.js +2 -2
  28. package/lib/conversation-outbox.js +37 -7
  29. package/lib/index.js +7 -1
  30. package/lib/invocation-journal.js +223 -0
  31. package/lib/invocation-store.js +275 -0
  32. package/lib/runtime.js +1126 -174
  33. package/lib/session-scope-store.js +2 -2
  34. package/lib/session-scope.js +146 -30
  35. package/lib/session-task-store.js +275 -0
  36. package/lib/session-task.js +237 -0
  37. package/lib/session-tool-cache.js +168 -0
  38. package/lib/session-tools.js +240 -0
  39. package/lib/subject-display.js +39 -0
  40. package/lib/system-info.js +76 -0
  41. package/lib/tool-catalog.js +38 -0
  42. package/lib/transport.js +34 -0
  43. package/package.json +14 -3
@@ -0,0 +1,138 @@
1
+ # One conversation across business systems
2
+
3
+ **Release pairing: Core 0.7.0, SDK 0.5.0 and DSH 0.5.0.** The plugin installs its exact SDK
4
+ dependency. Core 0.6.1 / SDK 0.4.0 / DSH 0.4.0 do not include this extension; upgrade the Hub
5
+ before selecting targets from different systems.
6
+
7
+ ## What you can do
8
+
9
+ Select, for example, a shop account and an inventory account already authorized on the same
10
+ BailingHub deployment. Ask the same local Agent to check product stock, then change a corresponding shop product's price and list it using permitted actions. The Agent keeps each system's tools and authorizations distinct; you do not switch
11
+ the global connection between steps. Available actions still depend on each system's declared
12
+ capabilities and the permissions of the selected account.
13
+
14
+ The Agent initially sees the selected target directory. It searches the intended target's
15
+ capabilities when needed, then calls the corresponding typed tool. A capability search opens
16
+ that target's run with the search's minimal task description. Other systems do not automatically
17
+ receive the original user turn or contribute their memory. The local Agent and the independent
18
+ conversation audit still share the visible conversation.
19
+
20
+ ## Start a conversation
21
+
22
+ ### Understand the selected systems before choosing tools
23
+
24
+ An administrator can provide each connected system's name, short purpose, usual business areas
25
+ and boundaries in BailingHub. The Agent reads these descriptions for the selected accounts before
26
+ its first capability search. For example, it can distinguish online selling from inventory
27
+ management without a client-specific product dictionary. Business-supplied subject names identify
28
+ the particular organization, project or other authorized subject for display. They are separate
29
+ from product purpose and do not prove identity or grant permissions.
30
+
31
+ A description says what the product usually does. The actual tools and permissions still come
32
+ from authorized capability discovery. “Not loaded” means the tools have not been requested yet;
33
+ it does not mean the product has no capabilities. Missing descriptions or an older server show
34
+ unknown information and preserve the existing search flow. Reading a description sends no user
35
+ message and creates no business run. Existing same-system first-turn runs are unchanged.
36
+
37
+ If the server explicitly reports that a selected workspace is unavailable, its directory says
38
+ unavailable while retaining the original selection. An uncertain network failure remains unknown;
39
+ neither condition selects another account. A later turn reads the original target again.
40
+
41
+ ### Select the accounts
42
+
43
+ 1. Authorize each target separately and verify the intended subject on the business authorization
44
+ page. With a compatible business backend, the backend supplies its name automatically. A local
45
+ connection selector remains independent; missing names show “Authorization name pending sync”.
46
+ Names do not prove a mapping between two systems or distinguish duplicate-named subjects.
47
+ 2. In a new conversation, use `/bailinghub connections list`, then
48
+ `/bailinghub scope set <shop-connection-key> <inventory-connection-key>`.
49
+ 3. Wait for successful confirmation before sending the first message. Start with a precise
50
+ request identifying the intended systems, objects and allowed action.
51
+ 4. Review the separate results and any approval requests. Use `/bailinghub archive status`
52
+ to check visible-record upload; `/bailinghub archive sync` retries uploads without repeating
53
+ business actions.
54
+
55
+ An empty scope remains ordinary chat. The first user message freezes the selected targets.
56
+ Reopening a conversation restores the original selection after all original members pass
57
+ validation; a draft that never started needs fresh confirmation. A temporary outage can be
58
+ retried in the same runtime. Revoked/replaced members never cause a switch to a default or subset.
59
+
60
+ ## Host integration
61
+
62
+ Keep the existing `setSessionScope`, `getSessionScope`, `restoreSessionScope`,
63
+ `getSessionArchiveStatus` and `syncSessionArchive` flow. The returned scope view retains its v1
64
+ schema and gains `targetMode: "multi_system"`; authorization entries additionally include
65
+ `clientAppId` and `systemRef`. Use those public fields to group the selection UI if helpful.
66
+ Never feed raw keys, credentials or writable scope records to the model.
67
+
68
+ The built-in stores understand both v1 and v2. A custom store must retain the complete v2 record
69
+ without reconstructing it from today's registry, preserve the original host Session identity,
70
+ and provide atomic revision comparison. Persist original DSH events so incomplete capture can
71
+ be detected on reopen. No second body-reporting path is required.
72
+
73
+ Cross-system operation requires the matching SDK's capability negotiation, frozen-binding guard,
74
+ and the Core's target-member archive extension. Missing support reports
75
+ `CROSS_SYSTEM_SCOPE_UNSUPPORTED`; it is not a reason to send the first message with an old scope.
76
+ The matched Core requires an additive database migration through its own deployment process;
77
+ installing this plugin does not migrate or deploy the Hub.
78
+
79
+ ## Limits
80
+
81
+ - One Hub, with independently authorized Sessions for every selected target. Cross-Hub scope
82
+ and multiple selected routes sharing one Agent Session are not supported.
83
+ - Task planning and step ordering are performed by the Agent. This is not a deterministic
84
+ dependency engine, a distributed transaction, automatic rollback, or cross-process task recovery.
85
+ - Original invocation recovery is available in later turns of the same live runtime. The
86
+ unreleased [durable recovery candidate](INVOCATION_RECOVERY.md) adds explicit recovery after
87
+ restart when the host retains its invocation journal. Archive restoration alone does not
88
+ reconstruct pending invocations or approvals.
89
+ - Different systems' object IDs are unrelated unless a verified business mapping establishes
90
+ the relationship. The Agent must clarify an ambiguous mapping.
91
+ - The runtime enforces target and capability boundaries. It does not provide automatic
92
+ sensitive-text redaction or a field-level cross-system data-sharing policy.
93
+ - Complete transcript reading remains an administrator audit operation. An individual business
94
+ authorization does not grant an API to read the entire mixed transcript.
95
+
96
+ ## 简体中文
97
+
98
+ 本版配套 Core 0.7.0、SDK 0.5.0 与 DSH 0.5.0,插件会安装精确 SDK 依赖。
99
+ Core 0.6.1、SDK/DSH 0.4.0 不含本次扩展;选择不同系统前先升级中枢。
100
+
101
+ ### 对使用者有什么变化
102
+
103
+ 你可以在同一个会话里选中“商城”和“库存系统”的授权,让助手先查询保温杯库存,再使用商城允许的
104
+ 改价、上架等动作。每一步都会使用对应系统的工具与授权,仍按该系统的权限和审批规则执行。
105
+
106
+ 助手先看到你选中的目标目录,需要哪个系统时才查找它的能力、加载它的上下文。不会因为选中了两个系统,
107
+ 就把每句话自动发送给两者。某个系统的查询结果进入本地模型后,会成为同一会话的上下文;只有允许这样共享
108
+ 的数据才适合放在同一会话。完整可见沟通通过独立会话账本归档,各系统仍保留自己的操作记录。
109
+
110
+ ### 怎样开始
111
+
112
+ 接入方可以在中枢维护系统名称、简短用途、典型业务方向和边界。助手在首次搜索工具前就能看到本会话所选系统
113
+ 的介绍,例如分清“线上售卖”和“库存管理”,不需要每个客户端分别写死产品词典。同系统的多个账号也会显示
114
+ 相同的系统归属。授权主体展示接口单独提供业务授权主体的名称,用于表示具体的组织、账号、项目等;名称不证明身份,
115
+ 也不增加权限。
116
+
117
+ 系统介绍说明产品通常做什么,实际能做什么仍取决于此授权的能力查询和原有权限规则。“尚未加载”表示还未
118
+ 查询工具,不表示没有能力。介绍缺失、旧版本不支持或介绍请求暂时失败时,可继续使用原有的授权工具搜索。
119
+ 读取介绍本身不发送用户正文、不创建业务运行记录;同系统原有首轮运行机制保持不变。
120
+
121
+ 如果服务端明确报告所选工作空间不可用,目录会显示“不可用”并保留原选择;无法确定的网络失败显示“未知”。
122
+ 两者都不会自动切换授权,后续轮次仍查询原目标。
123
+
124
+ 先分别授权并在业务授权页确认对象。授权主体展示接口由业务后端提供主体名称;名称缺失时明确显示“授权名称待同步”,
125
+ 内部连接标识独立保留。再新建会话,使用上面的 scope 命令选中两份固定授权。
126
+ 等选择成功后,再明确提出操作对象和要做的事。首次消息后范围固定;需要增减系统时新建会话。
127
+
128
+ 如果库存里的商品与商城中的某个商品需要关联,应使用已经确认的业务映射,或由用户明确确认。
129
+ 两个系统的商品名或编号看起来相同,并不能证明它们是同一对象。库存查询不等于锁库存或建立自动库存同步。
130
+
131
+ ### 下游需要配合什么
132
+
133
+ 宿主继续使用现有五个范围与归档接口,不需要另加一条正文上报链路。界面可以按新返回的系统字段分组展示,
134
+ 并继续保持“选择成功才允许发首条消息”。内置持久化已支持新记录;自定义存储需要原样保留 v2 的目标绑定、
135
+ 原会话身份、事件和修订号。Core 0.7.0 需要通过自身升级流程应用未执行的迁移(本次包括 058/059)。
136
+
137
+ 业务后端只需继续声明能力、独立授权并执行原有业务规则;如果用户要做的动作尚未开放,才需要补充该业务能力。
138
+ 本版解决跨系统选择、调用和完整追溯,不包含周年庆等长任务的持久依赖调度、跨进程恢复或自动回滚。
@@ -0,0 +1,77 @@
1
+ # 本地智能体附件空间:DSH 宿主接入
2
+
3
+ 智能体生成或用户明确指定用于业务的图片,可以用于活动海报、内容封面、经营图表,也可以用于商城商品。宿主把当前会话获准使用的图片登记到附件目录,插件提供目录查询和上传工具;模型选择目标授权与附件引用,中枢保存图片并返回 URL,供后续业务工具使用。图片不必在当前轮生成;仅用于聊天理解的附件不会因此自动登记或上传,聊天附件历史存档是另一项独立能力。
4
+
5
+ 首期支持 PNG、JPEG、WebP。附件空间提供会话级目录、上传与上传结果恢复,不把 PDF、Word、视频或完整网盘管理写成已经交付的能力。业务系统需要预先提供相应的图片 URL 接口。
6
+
7
+ 配套版本:Core 0.8.0 / SDK 0.6.0 / DSH 0.6.0。新能力需部署方升级并按宿主契约接入;业务权限、审批和原身份约束保持。
8
+
9
+ ## 宿主需要接哪两处
10
+
11
+ ```js
12
+ import { createAgentClientPlugin, createFileArtifactStore } from 'dsh-bailinghub'
13
+
14
+ const plugin = createAgentClientPlugin({
15
+ // 保留原 SDK、scopeStore、archiveStore 等配置。
16
+ artifactSource: {
17
+ async list({ sessionId }) {
18
+ // 从本会话已获准用于业务的图片中读取;只返回元数据,最多 100 项。
19
+ return [{ artifactRef: 'campaign-banner', name: 'campaign-banner.png',
20
+ mime: 'image/png', bytes: generatedSize, sha256: generatedDigest }]
21
+ },
22
+ async read({ sessionId, artifactRef }) {
23
+ // 宿主按已登记引用读取获准文件,返回 Uint8Array。
24
+ // 检查归属、允许目录、符号链接及读取期间文件变化;不要直接拼接模型给出的路径。
25
+ return generatedBytes
26
+ },
27
+ },
28
+ artifactStore: createFileArtifactStore({ directory: artifactRecoveryDirectory }),
29
+ })
30
+ ```
31
+
32
+ `artifactRef` 是宿主管理的稳定引用,允许字母、数字、下划线和短横线,最多128字符;不可把同一个引用改指另一份文件。同名图片可以用不同引用。目录属于真实 DSH Session,重新打开时保持原归属;后台持久化上传元数据目录同样需要跨重启保留。也可实现自有 `artifactStore.get(uploadId)` / `reserve(record)`:reserve 必须原子、只写一次并返回先前记录或新记录,落盘完成才成功返回。内存 store 仅用于测试或明确的临时会话。
33
+
34
+ 未提供 artifactSource 时不会新增模型工具,已有业务流程不变。缺少持久恢复 store 时上传返回 storage_error,并且不会发送文件。
35
+
36
+ ## 模型实际能用的工具
37
+
38
+ - `list_generated_artifacts`:读取当前会话产物目录,不返回路径或文件正文。
39
+ - `upload_generated_artifacts`:必须明确 `authorization_ref` 与 `artifact_refs` 数组;每批1–8张,单张不超过6MiB,支持PNG/JPEG/WebP。
40
+
41
+ 上传成功后模型直接使用 ready URL,不需要为每次使用重复查询地址或上传。只有不确定的上传结果或会话重开恢复才查询原上传记录。
42
+
43
+ 业务操作独立执行并单独判断结果。例如更新商城轮播图时,所有必需图片 ready 后再提交完整清单,保留未被要求删除的旧图片;业务结果未知时保留原 invocation,不能重发写操作。
44
+
45
+ 模型不选择桶或密钥。中枢管理员在“智能体客户端 → 配置接入 → 工具与审批 → 生成图片上传”选择媒体存储。第一期图片用于公开展示;普通COS/OSS或显式本地存储都由部署方管理保留,不增加文件到期判定。
46
+
47
+ ## 失败和重开
48
+
49
+ ### 跨轮上传与旧错误记录恢复
50
+
51
+ 新上传只关联目标授权**当前轮次的 active run**。跨系统按需启动业务 run;该目标当轮尚未启动时,上传使用原会话与当前轮次、不带可选 runId,既不沿用上轮记录,也不为上传额外创建业务 run。
52
+
53
+ 旧候选可能保存了“本轮上传 + 上轮 run”的记录。只有配套 Core 返回 `artifact_run_turn_mismatch`(同一原身份、路由、会话均一致,仅轮次不同),且再次读取原上传 ID 明确返回 `artifact_not_found`,DSH 才自动纠正。泛化的 `artifact_run_mismatch`、授权失效、网络错误或结果未知都不能触发纠正。Core 的身份和关联检查保持生效。
54
+
55
+ 纠正不覆盖原恢复记录:通过现有 get/reserve 原子保存独立的 `bailing.artifact-run-link-repair.v1` 记录,保留原记录摘要和原上传 ID。修正元数据只删除已证明属于另一轮的 runId,保留原上传会话、轮次、内容及目标;实际 HTTP 始终使用原上传 ID。宿主应完整保存记录中的 `runLinkRepair` 字段,不把这个本地记录键当成新增云端上传或新产物。无需新增 store 方法。
56
+
57
+ 纠正必须落盘后才补传;已 ready 的原回执直接复用,pending 不改绑,确认丢失后读取原 ID。原记录或纠正记录缺失、损坏时返回 storage_error,不根据服务端 URL 猜补本地历史。旧 Core/SDK 没有精确错误码时,旧错链记录继续明确阻断;当前正确链接的新上传仍兼容原附件 API。
58
+
59
+ 两个工具名中的 `generated` 为兼容保留,不限制来源必须是 AI 生成。宿主需要更新自己的目录说明,让用户明确指定业务用途的图片可以受控登记,不新增聊天附件自动同步或用户手动上传入口。
60
+
61
+ 每文件上传身份绑定原真实 Session、连接与产物引用,首发前原子保存原 Hub/client/workspace/Agent Session、内容摘要、名称和原会话/轮次/run关联。重试或重开先读取原中枢记录,成功项直接复用 URL;pending 或尚未写入时补传原文件。即使原文件已不可读,已确认成功的上传仍可恢复。
62
+
63
+ 批量结果包含 `results`、`all_ready` 和 `business_operation_performed=false`。每项为 ready、pending 或 blocked。成功项保留,失败项独立处理;不要因部分上传成功就写入残缺图库。
64
+
65
+ 本地持久化失败返回 storage_error;原记录损坏或丢失须由宿主标记恢复缺口,不能猜补原历史。暂时网络失败可以在同一 runtime 重试,保留原记录。任何原授权成员撤销或改绑时整组阻断,不退回默认、子集或替代 Session。取消后迟到上传不会重新注册业务工具或返回可继续执行的轮次;已经保存的对象及原记录仍可在合法恢复时查询。
66
+
67
+ 旧SDK/Core返回artifact_unsupported,保留原业务工具能力。Lazy SDK transport 已转发新增接口,但宿主仍必须实际提供生成文件来源。测试替身与真实对象存储连通性分开验收。
68
+
69
+ ## 业务后端要不要改
70
+
71
+ 如果原业务能力已经接收图片 URL,不必改授权或审批规则。客户端将 ready URL 作为原业务参数传入即可。需要业务自身素材ID、转存或素材库归属时,另接该系统的业务导入能力。
72
+
73
+ ## 分开记录的已知限制
74
+
75
+ 配套 Core 0.8.0 已提供可配置的中枢工具限额,小时/日额度按原窗口计数,并为新调用保留加密原参数。DSH 读取 `retry_after_ms`,等待后只恢复原 `invocation_id`;长于自动等待预算时返回 `agent_client_wait.state=rate_limited`,保留等待时间和原调用。手动过早恢复也不向中枢反复请求。原权限、审批、取消和授权目标约束继续生效;旧 Core 不返回提示时仍沿原恢复流程。
76
+
77
+ 当前列表工具的源错误可能经过通用分类变成 unknown_failure;宿主应保留自己检查到的 storage_error/recovery_gap,不能将其描述为空目录。上传工具仍返回逐项错误。不要用新业务调用或重新上传已经 ready 的附件掩盖这些问题。
@@ -1,8 +1,10 @@
1
- # Get started with 0.4.0
1
+ # Get started with 0.6.0
2
2
 
3
3
  This guide is for users whose business system is already connected to BailingHub. Your
4
- administrator must prepare Core 0.6.1, a public Client App ID, a workspace, and the business
5
- browser-authorization entry first. You do not need to change business capability declarations.
4
+ administrator must prepare Core 0.8.0, a public Client App ID, a workspace, and the business
5
+ browser-authorization entry first. For multiple systems, prepare one independently authorized
6
+ connection per target on the same Hub and audit domain. Existing business capability declarations
7
+ remain valid; each requested action must already be exposed by its own system.
6
8
 
7
9
  ## 1. Install and configure
8
10
 
@@ -10,10 +12,10 @@ Use Node.js `22.19.0+` or `24+` and the compatible DSH version:
10
12
 
11
13
  ```bash
12
14
  npm install --global pnpm @deepseek-ai/dsh@0.1.1-rc.2
13
- dsh plugin --profile web add dsh-bailinghub@0.4.0
15
+ dsh plugin --profile web add dsh-bailinghub@0.6.0
14
16
  ```
15
17
 
16
- The plugin installs SDK 0.4.0 automatically. Enter the four public values in DSH plugin settings,
18
+ The plugin installs SDK 0.6.0 automatically. Enter the four public values in DSH plugin settings,
17
19
  or use their environment names. The example values below are placeholders:
18
20
 
19
21
  ```bash
@@ -41,7 +43,10 @@ In DSH, authorize the first account:
41
43
 
42
44
  The browser opens the original business authorization page. Sign in or switch accounts there,
43
45
  select the intended store/tenant when asked, and check the actual identity before approving.
44
- The business page determines the identity; the local label `Store A` does not.
46
+ The business page determines the identity; the local label `Store A` does not. A compatible
47
+ backend supplies the approved subject name automatically. The current display name is separate
48
+ from its fixed key and the system description; missing names remain explicit. Duplicate names
49
+ or renames do not recreate authorization or change old conversation records.
45
50
 
46
51
  For another account in the **same system and workspace**, register a clearly named connection,
47
52
  using the same three administrator-provided values, then authorize it separately:
@@ -59,24 +64,45 @@ an available alias. Names such as `default-2` do not prove which store was autho
59
64
  mapping before use. If login reports cleanup required, the new connection is already authorized;
60
65
  inspect and remove the reported old entry rather than authorizing again.
61
66
 
67
+ To add a **different system**, use its administrator-provided Client App and workspace on the
68
+ same Hub, then authorize it independently. For example, an inventory target may use:
69
+
70
+ ```text
71
+ /bailinghub connections add "Inventory" https://hub.example.com inventory-client inventory_assistant
72
+ /bailinghub login
73
+ /bailinghub connections list
74
+ ```
75
+
76
+ These are placeholders, not values to guess from a product name. Each selected target needs its
77
+ own original Agent Session. Keep the existing Store B connection if using the same-system flow.
78
+
62
79
  ## 3. Choose this conversation's business scope
63
80
 
64
81
  Start a **new conversation before sending any message**, then run:
65
82
 
66
83
  ```text
67
84
  /bailinghub connections list
68
- /bailinghub scope set <store-a-connection-key> <store-b-connection-key>
85
+ /bailinghub scope set <shop-connection-key> <inventory-connection-key>
69
86
  /bailinghub scope
70
87
  ```
71
88
 
72
89
  Copy the fixed connection keys from the list; the scope command does not accept names. Select
73
- one key for one account, or several for the same Hub/Client App/workspace. Wait for the successful
74
- confirmation before sending. For example, when the reporting capability is available:
90
+ one key for one account, several for the same system, or several systems on one Hub and audit
91
+ domain. Wait for successful confirmation. With the required inventory/shop capabilities and a
92
+ confirmed product mapping, you can ask:
75
93
 
76
94
  ```text
77
- Compare today's sales at Store A and Store B. Show each store separately.
95
+ Check tumbler stock. If any are available, change the corresponding shop product to 59 and list it.
96
+ Otherwise leave it unlisted.
78
97
  ```
79
98
 
99
+ The system description can explain online selling versus inventory before tool search. “Not loaded”
100
+ means tools have not been queried, not that none exist. A stock read does not reserve or synchronize
101
+ stock. Check price and listing outcomes separately, including any pending approval.
102
+
103
+ For the retained same-system flow, select Store A and Store B instead and compare their sales
104
+ when reporting tools are available. A system that was not used must not be marked as executed.
105
+
80
106
  The Agent chooses which selected authorization to use for each call. The system still decides
81
107
  which data and actions that authorization permits. Test a read first, then a reversible permitted
82
108
  update in a development workspace. Approval-required work follows the original approval flow.
@@ -89,7 +115,7 @@ connection does not change a conversation's scope.
89
115
  ## 4. Check results and the visible conversation
90
116
 
91
117
  Check the actual business result and its original invocation trail in BailingHub. With the matching
92
- Core 0.6.1, you can also follow visible user/assistant messages, turns, and the linked runs as one
118
+ Core 0.8.0, you can also follow visible user/assistant messages, turns, and the linked runs as one
93
119
  conversation record. Multi-authorization runs keep their own call summaries separately.
94
120
 
95
121
  ```text
@@ -1,7 +1,8 @@
1
- # 开始使用 0.4.0
1
+ # 开始使用 0.6.0
2
2
 
3
- 这份指南面向业务系统已经接入 BailingHub 的用户。管理员需要先准备 Core 0.6.1、公开的 Client
4
- App ID、workspace 和浏览器业务授权入口;已有业务能力无需为多账号重新声明。
3
+ 这份指南面向业务系统已经接入 BailingHub 的用户。管理员需要先准备 Core 0.8.0、公开的 Client
4
+ App ID、workspace 和浏览器业务授权入口。多系统时,每个目标分别授权,且属于同一中枢、同一
5
+ 审计域;已有能力无需重新声明,但每项动作必须已由相应业务系统开放。
5
6
 
6
7
  ## 第一步:安装与配置
7
8
 
@@ -9,10 +10,10 @@ App ID、workspace 和浏览器业务授权入口;已有业务能力无需为
9
10
 
10
11
  ```bash
11
12
  npm install --global pnpm @deepseek-ai/dsh@0.1.1-rc.2
12
- dsh plugin --profile web add dsh-bailinghub@0.4.0
13
+ dsh plugin --profile web add dsh-bailinghub@0.6.0
13
14
  ```
14
15
 
15
- 插件会自动安装 SDK 0.4.0。通过插件设置填写四项公开信息,或使用对应环境变量。下面都是
16
+ 插件会自动安装 SDK 0.6.0。通过插件设置填写四项公开信息,或使用对应环境变量。下面都是
16
17
  占位值,需要替换成管理员提供的中枢地址、Client App ID 和 workspace:
17
18
 
18
19
  ```bash
@@ -38,7 +39,9 @@ dsh web
38
39
  ```
39
40
 
40
41
  浏览器会打开原业务授权页面。在那里登录、切换账号,并按业务系统要求选择门店或租户;同意前
41
- 核对实际业务身份。可信身份由业务授权页面决定,本机名称 `A 店` 不证明身份。
42
+ 核对实际业务身份。可信身份由业务授权页面决定,本机名称 `A 店` 不证明身份。配套后端可自动
43
+ 提供授权主体名称,它与固定连接键、系统用途说明独立;缺名会明确提示。同名或改名不重建授权,
44
+ 也不更改原会话记录。
42
45
 
43
46
  如需使用**同一系统、同一 workspace** 的 B 店,用同样的三项公开信息创建清晰命名的新连接,
44
47
  再分别授权:
@@ -54,24 +57,41 @@ dsh web
54
57
  `default-2` 之类的名称不能证明是哪家店,使用前应核对对应关系。若登录提示需要清理,新连接
55
58
  已经授权成功;请检查并删除提示的旧条目,不要重复授权。
56
59
 
60
+ 要接入**另一个系统**,使用管理员提供的该系统 Client App 与 workspace,并在同一中枢上
61
+ 独立授权。例如库存目标可以使用以下占位配置:
62
+
63
+ ```text
64
+ /bailinghub connections add "库存系统" https://hub.example.com inventory-client inventory_assistant
65
+ /bailinghub login
66
+ /bailinghub connections list
67
+ ```
68
+
69
+ 这些配置不能从产品名称推测。每个选中目标都需要独立原 Agent Session;同系统流程中已有的
70
+ B 店连接可以继续保留。
71
+
57
72
  ## 第三步:选择本次会话的业务范围
58
73
 
59
74
  **新建会话,在发送任何消息前**执行:
60
75
 
61
76
  ```text
62
77
  /bailinghub connections list
63
- /bailinghub scope set <A店连接键> <B店连接键>
78
+ /bailinghub scope set <商城连接键> <库存连接键>
64
79
  /bailinghub scope
65
80
  ```
66
81
 
67
- 从列表复制固定连接键替换占位符,不能直接填写连接名称。只操作一家店就选一个键;多份授权
68
- 必须属于相同中枢、Client App 和 workspace。等待设置成功回显后,再发送请求。例如,业务系统
69
- 已开放报表能力时可以说:
82
+ 从列表复制固定连接键替换占位符,不能直接填写名称。可以选单份授权、同系统多份授权,或
83
+ 同一中枢、同一审计域的多个系统。等设置成功回显后再发请求。相应能力已开放、商品关系已确认时:
70
84
 
71
85
  ```text
72
- 对比今天 A 店和 B 店的营业情况,按门店分别说明。
86
+ 先查保温杯库存。有货就把商城对应商品的售价改为 59 元并上架;没货就先不要上架。
73
87
  ```
74
88
 
89
+ 系统说明可在首次查工具前解释“线上售卖”和“库存管理”的区别。“尚未加载”表示还没查询工具,
90
+ 不是没有能力。查库存不等于锁库存或自动同步;改价、上架及审批要分别核对结果。
91
+
92
+ 原同系统流程仍可选择 A 店和 B 店,在报表能力可用时对比营业情况。没有实际调用的系统不能
93
+ 被标为已执行。
94
+
75
95
  智能体自行选择每次调用使用哪份选中授权;实际数据和操作范围仍由业务权限决定。先验证一次
76
96
  查询,再在开发空间尝试一次可回滚且允许的修改。需要审批的操作继续沿用原规则。
77
97
 
@@ -81,7 +101,7 @@ dsh web
81
101
 
82
102
  ## 第四步:核对业务结果与沟通过程
83
103
 
84
- 在业务后台核对最终结果,在 BailingHub 查看原调用轨迹。配合 Core 0.6.1,还能把可见用户消息、
104
+ 在业务后台核对最终结果,在 BailingHub 查看原调用轨迹。配合 Core 0.8.0,还能把可见用户消息、
85
105
  助手回复、轮次与相关执行记录放在同一份沟通记录里查看。多授权的各份执行记录仍分别保留自身
86
106
  调用摘要。
87
107
 
@@ -0,0 +1,182 @@
1
+ # Reopen a conversation and recover its original business actions
2
+
3
+ Available in DSH 0.6.0 with SDK 0.6.0 and Core 0.8.0. Configure a durable invocation store and preserve original Session records.
4
+
5
+ A shop listing may still await approval when a user closes the Agent. An inventory
6
+ adjustment may have reached the server even though its response was lost. Reopening
7
+ the same conversation must keep those actions attached to their original accounts
8
+ and calls. It must not create a second listing or stock adjustment from remembered text.
9
+
10
+ The plugin adds durable **invocation metadata**, separate from the visible conversation
11
+ archive and attachment space. It retains the original fixed scope fingerprint/revision,
12
+ connection key, Hub/client/workspace, Agent Session, authorization reference, run,
13
+ invocation ID, tool name, declaration revision, host call ID and parameter digest.
14
+ It also stores the last received state, receipt digest and retry deadline. It does not
15
+ store credentials, raw tool arguments, receipt bodies or conversation text in this journal.
16
+ Digests support association and change detection; they are not independent tamper-proof evidence.
17
+
18
+ ## What happens before and after reopening
19
+
20
+ 1. Before a new business request leaves the host, save its original binding using
21
+ compare-and-swap (CAS). If that cannot be confirmed, do not send the request.
22
+ 2. After a response, save only its state and digest. A failed save remains
23
+ `storage_error` with an unsaved record. A successful business response does not
24
+ erase the local recovery-storage error.
25
+ 3. Reopen the real DSH Session with its original persisted events and scope. Reuse
26
+ its invocation store. Do not create a replacement Session or replay user messages.
27
+ 4. Inspect or restore local invocation metadata. This does **not** create business
28
+ runs, invoke/resume business actions, or restore old dynamic tools.
29
+ 5. When the user or Agent explicitly requests recovery, call
30
+ `resume_governed_tool_invocation` with only the original `invocation_id`. The plugin
31
+ verifies every original scope member and uses that call's original target. It sends
32
+ `resume` with the original ID and an empty payload, never a replacement `invoke`.
33
+
34
+ `resume` is **not a read-only status operation**: Core may continue an approved or
35
+ otherwise resumable original operation. Unknown dispatched writes remain subject to
36
+ Core's existing reconciliation and approval rules. Reopening alone does not do this.
37
+ This feature does not automatically continue a whole task, DAG or sequence of actions.
38
+ When the user starts a new recovery turn, the existing runtime may create a current-turn
39
+ run for the original selected target to keep the new conversation/audit association.
40
+ That run never replaces the recovered call's original run or invocation. The startup and
41
+ local status/restore stage creates no such run.
42
+
43
+ A local last-known state is not current server proof (`result_verified: false`). After
44
+ full reopening, recovery obtains the original server receipt even if local metadata says
45
+ `executed`. Within a living host, its original confirmed response may be reused. A
46
+ cancelled or ended turn can retain late original receipts without re-enabling its tools.
47
+
48
+ ## Host integration
49
+
50
+ The default plugin uses its protected `invocation-records` directory under the DSH
51
+ plugin data directory. A host supplying a custom `scopeStore` must **explicitly** supply
52
+ an `invocationStore`; omission preserves existing business behavior but does not support
53
+ durable recovery after a full restart. `null` also explicitly disables the new store.
54
+
55
+ ```js
56
+ import { createAgentClientPlugin, createFileInvocationStore } from 'dsh-bailinghub'
57
+
58
+ const plugin = createAgentClientPlugin({
59
+ scopeStore: hostScopeStore,
60
+ archiveStore: hostArchiveStore,
61
+ invocationStore: createFileInvocationStore({ directory: hostInvocationDirectory }),
62
+ })
63
+ ```
64
+
65
+ Place `hostInvocationDirectory` in the same durable, access-controlled host data area as
66
+ the original Session scope. Never derive it from model input. An explicit memory store
67
+ is exported for tests; a new empty memory store is not persistence after process restart.
68
+
69
+ Custom adapters implement the same asynchronous interface:
70
+
71
+ ```js
72
+ await store.load(sessionId) // null or the complete metadata record
73
+ await store.save(sessionId, nextRecord, expectedRevision) // return saved record
74
+ ```
75
+
76
+ The current record schema is `bailing.agent-invocations.v2`; original v1 records
77
+ without task bindings remain readable and do not acquire a task implicitly.
78
+ Its top-level fields are `schema`,
79
+ `sessionId`, `revision`, `entries`. Revisions start at 1; missing expects `null`. Save must
80
+ atomically compare the previous revision, persist the full record, and acknowledge only
81
+ after durable success. A load error must not return `null`. Custom stores must retain
82
+ the strict metadata-only field restrictions and enforce the same 8 MiB record bound.
83
+
84
+ In the standard DSH command interface, `/bailinghub invocations status` reads this
85
+ status and `/bailinghub invocations restore` retries only the local metadata save.
86
+ Neither command calls the business resume endpoint.
87
+
88
+ The runtime exposes:
89
+
90
+ | Interface | Effect |
91
+ | --- | --- |
92
+ | `getSessionInvocationStatus(sessionId)` | Verify original scope and read local journal status. |
93
+ | `restoreSessionInvocations(sessionId)` | Verify scope and flush pending local metadata with original CAS identity. No business invocation or resume. |
94
+ | `inspectSessionInvocation(realSession, invocationId, { signal }?)` | Read the original remote receipt without a model turn or new run. Does not continue an approved action. |
95
+ | `resumeSessionInvocation(realSession, invocationId, { signal }?)` | Explicitly inspect, then continue an eligible original undispatched action at most once. Later observation is read-only. |
96
+
97
+ The first two return `state`, `entries`, `unsavedRecords`, plus `revision`/`reason` when available.
98
+ Each entry has `invocation_id`, `authorization_ref`, `tool`, `original_run_id`,
99
+ `last_known_state`, `retry_at`, and `result_verified: false`. Treat tool names as data.
100
+ Do not turn these entries into new calls or consider the task complete from this list.
101
+
102
+ The two remote actions require the actual Session and return a
103
+ `bailing.agent-session-invocation-action.v1` envelope. `ready` means the interface
104
+ returned normally, not that the business action succeeded. `resume_dispatched`
105
+ only reports whether the runtime submitted resume to the SDK, not whether the
106
+ Hub received it or the business action ran; read the original receipt for
107
+ business state. A missing result remains unconfirmed. Storage or coverage failures
108
+ retain priority, and a failure after sending resume never proves no dispatch occurred.
109
+ See the [idle-panel integration and failure contract](TASK_CONTROL.md).
110
+
111
+ | State | Host action |
112
+ | --- | --- |
113
+ | `ready` | Metadata is readable and saved; explicit original-call recovery is available for known records. This is not a claim that every historical call has a journal record. It does not confirm Core connectivity or the business outcome. |
114
+ | `inactive` | No selected business scope, or the conversation has not started. Ordinary chat makes no Hub request for this journal. |
115
+ | `unsupported` | No invocation store. Existing same-host business/recovery behavior is retained. |
116
+ | `blocked` | Original scope or invocation binding is unavailable/conflicting. Do not switch account or use a surviving subset. |
117
+ | `storage_error` | Restore local persistence first. Keep unsaved records and the original call identity. |
118
+
119
+ Keep conversation archive `storage_error`, `unsavedEvents` and `recovery_gap` visible
120
+ independently. A `ready` invocation journal cannot make an incomplete transcript complete. If local
121
+ metadata is unsaved while authorization is also blocked, preserve `storage_error` and
122
+ `unsavedRecords` as the primary state, with `scope_state: blocked` and `scope_reason`;
123
+ this does not enable business access or discard either failure.
124
+
125
+ ## Failure and compatibility rules
126
+
127
+ - `invocation_store_unavailable` / `invocation_store_conflict`: category `storage_error`,
128
+ next action `restore_invocations`. Do not interpret `not_dispatched` on a failed
129
+ recovery attempt as evidence that the original business action never ran.
130
+ - `invocation_binding_unavailable` / `invocation_binding_conflict`: inspect original
131
+ evidence. Never guess the binding from transcript text or accept model-supplied targets.
132
+ - `invocation_store_unsupported`: the host has not enabled durable invocation storage.
133
+ - An authoritative HTTP 404 with SDK `publicCode=invocation_not_found` stops the
134
+ current recovery poll immediately, even if an older receipt said `awaiting_approval`.
135
+ Both the native model tool and direct `runtime.resume` preserve the original ID in
136
+ `feedback`: `category=invocation_outcome_unknown`, `code=invocation_not_found`,
137
+ `next_action=inspect_original`, `retryable=false`, `original_outcome=unverified`.
138
+ Dispatch remains `attempted` or `unknown`; the original business outcome is not
139
+ declared unexecuted. The local journal, scope and original run are retained.
140
+ Inspect that call's original evidence instead of retrying automatically or
141
+ reconstructing a new write. The existing feedback schema and host APIs are unchanged.
142
+ - An arbitrary 404, a message containing that name, a missing public code or a
143
+ conflicting status is not authoritative not-found evidence. Older SDKs and custom
144
+ transports keep conservative bounded recovery for unconfirmed requests. A timeout
145
+ or temporary network failure does not become a missing-record diagnosis.
146
+ - Existing SDK/Core network, authorization, unsupported and reconciliation feedback
147
+ continues unchanged. No new Core endpoint, SDK method, database migration or business
148
+ backend capability declaration is required for this feature.
149
+
150
+ Whole-scope revalidation includes single-system multiple accounts. Temporary offline
151
+ checks stay closed but can recover in the same runtime. A confirmed revoked or rebound
152
+ member blocks the complete original group. Existing cross-Hub restrictions remain.
153
+
154
+ Concurrent writers cannot overwrite a different stored outcome with a stale receipt.
155
+ On a conflict, preserve both the stored record and unsaved local status for inspection.
156
+ Do not clear an unsaved record simply to make the screen green. File-store locks are
157
+ not automatically treated as stale: after an abnormal host exit while saving, an
158
+ operator must establish that no writer remains, preserve the record/lock evidence and
159
+ resolve storage ownership before retrying. Do not instruct a model to delete locks.
160
+
161
+ The journal closes the host-side identity gap, not every crash gap. If the host saved
162
+ its dispatch fence but crashed before the request reached Core, an explicit resume can
163
+ return `invocation_not_found`. That response does not authorize automatic reconstruction
164
+ or resubmission. Missing/deleted records from older installations cannot be backfilled
165
+ from conversation prose. Old sessions without a journal remain explicitly unsupported
166
+ or unavailable; new calls begin producing records once the host enables the store.
167
+
168
+ ## Acceptance expectations
169
+
170
+ Use synthetic shop/inventory data and real persisted DSH Session events. Cover pending
171
+ approval and lost responses across complete host reconstruction; unchanged original
172
+ invocation ID, authorization and run; zero duplicate `invoke`; no automatic startup
173
+ resume; all-member revalidation; offline recovery; pre/post-dispatch storage failures;
174
+ CAS conflicts and lost save acknowledgements; cancellation before delayed save returns;
175
+ and empty scope with zero Hub requests. Preserve the existing long-task tool retention,
176
+ declaration-change, archive and attachment regressions.
177
+
178
+ For missing-record regressions, use the real SDK and Core's error response shape
179
+ `{"error":"invocation_not_found","message":"..."}` with HTTP 404. Test both recovery
180
+ paths for pending approval and unknown outcomes. Require accurate final model/host
181
+ feedback and no replacement `invoke`, rediscovery, changed target or journal revision.
182
+ A test that only reproduces the old generic error is not a passing recovery test.