Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ WeChat original voice can be prepared for playback, native quotes and retained l

### Added

- Paired Lark users with **Answer** capability can answer native Human questions through a DM form or an explicit single-question reference; Web and IM share the original owner and one accepted answer, with uncertain submission requiring explicit reconciliation rather than replay ([#1031](https://github.com/BotHarness/BotHarness/issues/1031), [guide](docs/lark-connection.md#answer-native-questions-in-the-management-dm)).

- A reviewed Lark operator can receive tool approvals in a named management DM and use **Allow once** or **Reject**; callbacks recheck the actual actor and exact native call, while the Profile distinguishes delivery, accepted decision and native execution result. Known-unsent retries are bounded; uncertain delivery and old cards after restart cannot authorize execution ([#1029](https://github.com/BotHarness/BotHarness/issues/1029), [ADR-0141](docs/adr/0141-lark-private-approvals-rejoin-the-native-owner-through-checked-controls.md)).

- AX isolated Profiles can reuse a machine-local OpenCode Go credential and native default model after one hidden-input setup, preserving independent Profile configuration; the qualified DSH 0.2.0 RC1 adapter supplies the actual Session header required for real Go replies ([#1079](https://github.com/BotHarness/BotHarness/issues/1079), [guide](docs/agents/ax-model.md)).
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

### Added

- 已配对且具有「回答」能力的 Lark 用户可通过私聊表单或明确的单问题编号回答原生 Human 提问;Web 与 IM 共用原提问者,只接受一次答案,结果不确定时必须显式核对而不重放答案([#1031](https://github.com/BotHarness/BotHarness/issues/1031),[指南](docs/lark-connection.md#answer-native-questions-in-the-management-dm))。

- 经 Web 审核的 Lark 用户可按名称选择管理私聊,接收工具审批并点击「允许一次/拒绝」;回调复核实际点击者和准确的原生调用,Profile 分开展示通知投递、决定接受及原生执行结果。确定未发送的重试有上限,未知投递及重启后的旧卡片不能授权执行([#1029](https://github.com/BotHarness/BotHarness/issues/1029)、[ADR-0141](docs/adr/0141-lark-private-approvals-rejoin-the-native-owner-through-checked-controls.md))。

- AX 隔离 Profile 可在一次隐藏输入设置后复用本机 OpenCode Go 凭证和内置默认模型,保留各 Profile 的独立配置;经核验的 DSH 0.2.0 RC1 适配器补齐真实 Go 回复要求的实际 Session 请求头([#1079](https://github.com/BotHarness/BotHarness/issues/1079), [指南](docs/agents/ax-model.md))。
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,15 @@ The released dsh-im package version alone does not prove these capabilities. Use
Real platform testing requires one exclusive app receiver. As in ADR-0136, the supervisor can reconnect a disconnected account, so either use a dedicated test application or explicitly authorize a bounded Host shutdown. Restore the original receiver after testing. Local SDK fixtures and native Web proof are distinct from real Lark callback/execution proof.

Computer/Browser first-use approval installs native Session-wide authority. The broker marks these requests Web-only and sends a non-actionable management notification. They cannot be granted by an external one-operation decision. This preserves existing Web behavior while keeping IM Allow once limited to the exact operation. The per-token guard captures the canonical call and survives pre-execution tracking disposal; the native execution guard consumes it once and rechecks actual name/arguments, ownership, scope and current actor capability.

## Native question extension: #1031

The private question slice reuses the revisioned management route and exclusive Consumer fanout while keeping a separate `answer` capability and fixed question namespace. `ChannelUserQuestions` remains the only owner of the native `user-questions/request` wait. A committed request notification carries canonical Channel/Session references; the Messaging question projection cannot settle a native request itself. A second durable answer queue was rejected because it could revive an old native owner or race the existing Web answer.

Generation 61 adds only `messaging_question_deliveries`: canonical request/Session locators, original-questions hash, management route revision, checked card receipt, delivery/submission/update outcomes and bounded send count. Question content and accepted answers remain in the canonical Channel. The current SDK actor/account, paired private conversation, exact receipt, current Answer capability and original offered values are checked; a synchronous Channel commit fence rechecks the live owner and current authorization after queued writes. One native owner accepts one answer from either Web or IM. Answer-only pairings do not receive tool approval requests.

The checked Provider renders the original questions as a Card 2.0 form with opaque option indices and one explicit submit button; it normalizes SDK `action.form_value` alongside actual operator/context. Its private checked send/update pipeline verifies native source and own-card receipt before each write. Plain text is an answer only when it quotes the original single-question card or explicitly selects its 12-character reference. Several questions, malformed references and a reference contradicting a quoted card require explicit selection. Ordinary clarification retains its source conversation; answer text is never an approval or a saved rule. See the [official Lark callback reference](https://github.com/larksuite/cli/blob/main/skills/lark-im/references/lark-im-card-action-reply.md) for transport payload facts; that external reference is not application authority.

A failed or uncertain submission retains the original live wait. An uncertain post-commit answer refuses blind duplicate submission; authenticated `questionRetry` explicitly reconciles the same original owner with its existing canonical answer, or confirms that it still waits before allowing a new submission. It never replays stored answer text. Known-unsent sends and known-failed updates allow explicit bounded repair; unknown sends are not retried. Restart expires pending notification controls, refreshes the original receipt when its checked route is still available and never restores the native Promise. The native wait remains blocking in this slice.

Local native Windows/model proof and checked SDK fixtures remain distinct from real Lark form/callback qualification. A new Human-approved exclusive receiver window is required for #1031; the completed #1029 approval test does not authorize a second outage or qualify question forms. Generation 61 is another one-way writer boundary: older Bundles require their matching pre-upgrade Profile snapshot.
4 changes: 3 additions & 1 deletion docs/architecture/botharness-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,10 +247,12 @@ PersonaBot Profile 的 IM 连接经现有 Typert/API Gateway 选择账号和已

首次能力只能由认证 Web Human 显式勾选并按当前申请 revision 审核;定位码不能兑换权限,首位申请者不会自动成为管理员。能力仅覆盖当前 Bot 的批准、拒绝、回答及保存规则资格,不扩展 VPS、原生 DSH API、其他 Bot 或审批人管理。每次使用重查当前 Binding、Bot、真实 actor、能力和状态;暂停期间不可用,永久撤销后重新配对必须重新审核。该切片交付配对与 Web 审核,IM 审批/提问控件仍由后续切片接入原生权威。参数、恢复及审计边界见 [ADR-0136](../adr/0136-lark-pairing-is-reviewed-bot-scoped-operational-authority.md)。

### Lark 私聊审批(ADR-0141)
### Lark 私聊审批与原生提问(ADR-0141)

[#1029](https://github.com/BotHarness/BotHarness/issues/1029) 将已提交的工具审批发往 Web 显式选择的已配对管理私聊;通知路由及回执由 Messaging owner 在现有 Operational Database generation 60 保存,完整请求/决定仍归 canonical Channel 与 native Session。通知、决定接受、原生执行结果和卡片更新分别投影。现有独占 Consumer fanout 单独处理官方 SDK 卡片操作,不建立普通 Source Event、Inbox 或 Memory。公开的受检查 Provider capability 负责原始私聊/实际发送人、自己的卡片回执和平台写入前 fence;Host 校验实际点击者的当前 Bot/Binding/fingerprint/配对能力与 revision、目的地、request↔receipt、完整操作及原生执行者。允许一次/拒绝重入现有工具审批 broker,Web 与 IM 共用一位胜出者,执行前再次核验撤权与完整参数。回调确认不等于批准,批准不等于成功执行;重启使旧卡片失效,未知发送结果不重发。该切片不保存规则、不处理群审批/原生问题,也不释放 native wait。见 [ADR-0141](../adr/0141-lark-private-approvals-rejoin-the-native-owner-through-checked-controls.md)。

[#1031](https://github.com/BotHarness/BotHarness/issues/1031) 另以 `answer` 能力与固定 question namespace 接入同一管理私聊。原生 `user-questions/request` 的 application-defined owner 仍为 `ChannelUserQuestions`:仅发布已提交请求,IM 回调校验真实 SDK actor、当前路由/配对、原卡片回执与原始选项,再以同步 commit fence 复核实时 owner 和能力。Web/IM 只能写入一次 canonical Channel 回答并恢复同一 native wait;答案文字不能产生工具批准。Generation 61 的 `messaging_question_deliveries` 只保存请求 hash、路由/回执及投递/提交/更新结果,不建立第二份问题或答案权威。只有明确编号或引用原单问题卡片的文字进入回答控制路径;歧义必须显式选择,普通澄清保持原来源。结果不确定时,认证 `questionRetry` 核对原 owner 与已有 canonical 回答,不重放答案;重启只更新失效投影,不恢复旧 Promise。该切片仍使用原生等待;真实 Lark 表单/回调需独立真人窗口核验。

### Bot 之间的 Channel 协作(ADR-0065)

Orchestrator Session 中注册的 `group_attention_get` 与 `group_attention_set` 是 DSH model-facing Tool,消费 BotHarness Host 的 application-defined Channel capability;Host 从该 Session 所属 PersonaBot 确定 actor,并在读写时复核当前 Group membership。未保存的 Group 偏好只读为 digest 5 条/30 秒、revision 0;Human Bridge 写入和 Bot Tool 写入共用 Channel record 中的 `wakePolicies`,每次实质变更在同一 SQLite 事务里追加不可改写的 actor/时间/revision 审计事实。审计表只存历史,不作为第二套当前偏好;Admission 在消息提交时固定有效策略与 revision,不会随之后的编辑回写。
Expand Down
57 changes: 57 additions & 0 deletions docs/evidence/issue-1031/native-shell-measure.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
[
{
"kind": "SECTION",
"label": "Lark 管理通知",
"width": 804,
"height": 269,
"padding": "16px 0px",
"fontSize": "13px",
"color": "rgb(249, 250, 251)",
"background": "rgba(0, 0, 0, 0)",
"borderRadius": "0px"
},
{
"kind": "BUTTON",
"label": null,
"width": 72,
"height": 28,
"padding": "0px 10px",
"fontSize": "13px",
"color": "rgb(249, 250, 251)",
"background": "rgba(84, 85, 87, 0.5)",
"borderRadius": "8px"
},
{
"kind": "BUTTON",
"label": null,
"width": 85,
"height": 28,
"padding": "0px 10px",
"fontSize": "13px",
"color": "rgb(249, 250, 251)",
"background": "rgba(0, 0, 0, 0)",
"borderRadius": "8px"
},
{
"kind": "BUTTON",
"label": null,
"width": 98,
"height": 28,
"padding": "0px 10px",
"fontSize": "13px",
"color": "rgb(249, 250, 251)",
"background": "rgba(84, 85, 87, 0.5)",
"borderRadius": "8px"
},
{
"kind": "SECTION",
"label": "Lark 提问通知",
"width": 804,
"height": 115,
"padding": "16px 0px",
"fontSize": "13px",
"color": "rgb(249, 250, 251)",
"background": "rgba(0, 0, 0, 0)",
"borderRadius": "0px"
}
]
72 changes: 72 additions & 0 deletions docs/evidence/issue-1031/windows-local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
{
"issue": 1031,
"date": "2026-10-07",
"environment": {
"os": "native Windows",
"node": "24.21.0",
"dsh": "0.2.0-rc.1",
"baseline": "009e020becc360763a8506fa1d95024b847e9c72"
},
"provider": {
"source": "8f1c67b65ee6d112e068babe945037f5ddea54a6",
"pr": "https://github.com/DoodleBears/dsh-im/pull/14",
"runtimeFiles": 400,
"runtimeSha256": "b9c66ec730b5755fd406aba05fa58c1643b596904aba64639c2839f35c0fab0f",
"packageSha256": "89e5f1a5f7a31a33927d07870338dfebd50ab2ad0d65cde08ad81d0ce55af4c1"
},
"checks": {
"focusedBotHarness": {
"uniqueFiles": 10,
"uniqueTestsPassed": 194
},
"checkedProvider": {
"testsPassed": 791,
"existingPlatformSkips": 5,
"failures": 0
},
"lint": true,
"format": true,
"typecheck": true,
"build": true,
"fullRepositorySuitesClaimedGreen": false
},
"nativeWeb": {
"questionCount": 2,
"canonicalResolutionCount": 1,
"selectedOption": "Main",
"customAnswer": "Remember the Windows question tracer",
"questionToolCalls": 1,
"nativeAnswerError": false,
"originalSessionContinued": true,
"turnEnded": true
},
"receiverProtection": {
"exercisedWhileImDisabled": true,
"typedUtcTimestampValidated": true,
"exactQaHostStopped": true,
"disabledStatePersisted": true,
"candidateRestartAuthenticated": true
},
"realLarkQuestionsQualified": false,
"productionOutageStarted": false,
"sourceSha256": {
"packages/core/src/channels/user-questions.ts": "fd5617d92b1ec0ab771c2ab2fc748d5868e620b12657053e05720088a134224b",
"packages/core/src/messaging/question-messaging.ts": "095ec5f79237c07543bbe730a98018cf8c99fd0d860d7ee8f5452a20738a5178",
"packages/core/src/messaging/dsh-im.ts": "60059fe5e1ae9a9ec9bfc9a31813a41d71fdf39afbe14a4742b4cdd99cd20c79",
"packages/client/src/client/question-notifications.tsx": "f1d5ad47b7aec124e663f10db5431ed682a5339d0b179e93a54b951231da82e6"
},
"screenshotState": {
"viewport": "1440x960",
"locale": "zh",
"themes": ["light", "dark"],
"managementRoute": null,
"imBindings": 0,
"olderQaSidebarHistoryDiffers": true
},
"limitations": [
"Real Lark form submission, SDK callback and final card review require a fresh Human-authorized exclusive window.",
"The broader Windows suites have pre-existing cleanup/updater/POSIX failures; focused results do not claim full-suite success.",
"Question notification metadata is schema generation 61; an older schema-60 binary is not a safe database downgrade.",
"Native waits remain blocking; no Inbox continuation qualification is claimed."
]
}
53 changes: 53 additions & 0 deletions docs/evidence/issue-1031/windows-platform-attempt-1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"issue": 1031,
"attempt": 1,
"rootSource": "9717d6f460fe41c5598d49d4b90e462cb0a9496b",
"providerSource": "8f1c67b65ee6d112e068babe945037f5ddea54a6",
"nativeWindows": true,
"humanAuthorized": true,
"serverRestoreSeconds": 540,
"localCutoffSeconds": 480,
"windowArmedAtUTC": "2026-10-07T07:57:45.88Z",
"productionRestorationVerifiedAtUTC": "2026-10-07T08:05:36.385Z",
"outageUpperBoundSeconds": 471,
"freshPairingVerified": true,
"capabilities": ["answer"],
"nativeQuestionCount": 2,
"platformResult": {
"code": 11310,
"errorPath": "ROOT -> body -> elements -> [1](tag: form)",
"reason": "max_length 2000 exceeds the default maximum 1000",
"cardCreationAccepted": false
},
"deliveryProjection": "unknown-outcome",
"deliveryReplay": false,
"canonicalAnswers": 0,
"afterRestartNativeQuestion": "expired",
"qaCleanup": {
"routeDisabled": true,
"pairingsRevoked": 1,
"identitiesUnbound": 1,
"providerDisabled": true,
"errors": [],
"localReceiverStopped": true
},
"production": {
"serviceRestored": true,
"withinTenMinutes": true,
"humanConfirmedLarkAndDiscord": true
},
"followup": {
"providerSource": "a9f6cbc72369df7b522658158dc102ad66826ebe",
"formInputMaximum": 1000,
"sdkFormCustomMaximum": 1000,
"explicitTextNativeMaximumUnchanged": 2000,
"checkedProviderTestsPassed": 791,
"existingPlatformSkips": 5,
"packageVerified": true,
"realLarkSubmissionQualified": false,
"freshWindowRequired": true,
"runtimeFiles": 400,
"runtimeSha256": "0432ab4075100b87bb2d383b7d232fd059790dcda6f63ac9ddf890a5b93f3039",
"packageSha256": "675d82faf0f92f8da7cf82bc15d4d09334dc76252c3bfff9f43a388856d251f5"
}
}
Loading
Loading