work-ally 0.2.0-alpha.1
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/AGENTS.md +110 -0
- package/DASHBOARD.md +160 -0
- package/PRODUCT.md +113 -0
- package/README.md +403 -0
- package/ally.sh +171 -0
- package/bridge/src/approval-rules.ts +360 -0
- package/bridge/src/channel-delivery.ts +207 -0
- package/bridge/src/channel-types.ts +22 -0
- package/bridge/src/channels/fake/adapter.ts +31 -0
- package/bridge/src/channels/feishu/adapter.ts +411 -0
- package/bridge/src/channels/feishu/approvals.ts +6 -0
- package/bridge/src/channels/feishu/formatter.ts +276 -0
- package/bridge/src/channels/feishu/normalize.ts +368 -0
- package/bridge/src/codex-config.ts +52 -0
- package/bridge/src/config.ts +240 -0
- package/bridge/src/fake-runtime-client.ts +505 -0
- package/bridge/src/handoff-service.ts +494 -0
- package/bridge/src/logger.ts +194 -0
- package/bridge/src/memory-digest.ts +186 -0
- package/bridge/src/receiver-approval-autonomy.ts +158 -0
- package/bridge/src/receiver-control-core.ts +140 -0
- package/bridge/src/receiver-control-work-session.ts +218 -0
- package/bridge/src/receiver-control.ts +83 -0
- package/bridge/src/receiver-delivery.ts +136 -0
- package/bridge/src/receiver-helpers.ts +96 -0
- package/bridge/src/receiver-human-gate.ts +333 -0
- package/bridge/src/receiver-inbound-preflight.ts +162 -0
- package/bridge/src/receiver-recovery.ts +236 -0
- package/bridge/src/receiver-runtime-callbacks.ts +367 -0
- package/bridge/src/receiver-runtime-policy.ts +132 -0
- package/bridge/src/receiver-runtime-state.ts +124 -0
- package/bridge/src/receiver-support-actions.ts +189 -0
- package/bridge/src/receiver-thread-start.ts +57 -0
- package/bridge/src/receiver-turn-coordination.ts +94 -0
- package/bridge/src/receiver-turn-execution.ts +257 -0
- package/bridge/src/receiver-turn-failure.ts +143 -0
- package/bridge/src/receiver-turn-result.ts +185 -0
- package/bridge/src/receiver-turn-steer.ts +70 -0
- package/bridge/src/receiver-work-session.ts +76 -0
- package/bridge/src/receiver.ts +329 -0
- package/bridge/src/router.ts +62 -0
- package/bridge/src/runtime-client-agent-messages.ts +150 -0
- package/bridge/src/runtime-client-message-dispatch.ts +176 -0
- package/bridge/src/runtime-client-protocol.ts +411 -0
- package/bridge/src/runtime-client-request-ops.ts +56 -0
- package/bridge/src/runtime-client-run-turn.ts +158 -0
- package/bridge/src/runtime-client-thread-ops.ts +270 -0
- package/bridge/src/runtime-client-transport.ts +309 -0
- package/bridge/src/runtime-client-turn-poll.ts +224 -0
- package/bridge/src/runtime-client-turn-read.ts +185 -0
- package/bridge/src/runtime-client-turn-state.ts +105 -0
- package/bridge/src/runtime-client.ts +344 -0
- package/bridge/src/runtime-user-input.ts +403 -0
- package/bridge/src/scheduler.ts +239 -0
- package/bridge/src/server-handoff-command.ts +364 -0
- package/bridge/src/server-main.ts +80 -0
- package/bridge/src/server-routine-command.ts +60 -0
- package/bridge/src/server-routine-execution.ts +222 -0
- package/bridge/src/server-runtime-app-support.ts +107 -0
- package/bridge/src/server-runtime-app.ts +238 -0
- package/bridge/src/server-thread-sync-command.ts +63 -0
- package/bridge/src/server.ts +17 -0
- package/bridge/src/session-store-delivery.ts +220 -0
- package/bridge/src/session-store-human-gate.ts +380 -0
- package/bridge/src/session-store-inbound-acceptance.ts +66 -0
- package/bridge/src/session-store-meta.ts +134 -0
- package/bridge/src/session-store-turn-ledger.ts +272 -0
- package/bridge/src/session-store.ts +380 -0
- package/bridge/src/system-notify.ts +220 -0
- package/bridge/src/thread-sync.ts +200 -0
- package/bridge/src/translator.ts +494 -0
- package/bridge/src/types.ts +289 -0
- package/bridge/src/utils.ts +104 -0
- package/bridge/src/work-session-store.ts +471 -0
- package/docs/.gitkeep +0 -0
- package/docs/architecture/codex-feishu-bridge-proposal.md +2742 -0
- package/docs/completed/FEATURE-feishu-markdown-and-reply-support.md +327 -0
- package/docs/completed/README.md +21 -0
- package/docs/completed/SPEC-approval-autonomy-and-safe-defaults.md +205 -0
- package/docs/completed/SPEC-approval-batch-and-strict-reply-shortcuts.md +153 -0
- package/docs/completed/SPEC-conversation-noise-reduction-and-busy-input-gate.md +538 -0
- package/docs/completed/SPEC-engineering-sop-skillization.md +190 -0
- package/docs/completed/SPEC-faithful-bridge-core-thinning-v2.md +376 -0
- package/docs/completed/SPEC-faithful-bridge-core-thinning.md +1071 -0
- package/docs/completed/SPEC-group-chat-sender-identity.md +301 -0
- package/docs/completed/SPEC-middleware-exception-visibility.md +227 -0
- package/docs/completed/SPEC-nightly-memory-digest-visibility.md +121 -0
- package/docs/completed/SPEC-project-group-chat-human-centered-conversation-mapping.md +326 -0
- package/docs/completed/SPEC-remove-cli-persona-bootstrap.md +201 -0
- package/docs/developer-workflow.md +49 -0
- package/docs/implementation/SPEC-codex-same-machine-session-handoff-implementation.md +239 -0
- package/docs/implementation/test-coverage-map.md +363 -0
- package/docs/implementation/work-ally-implementation-guide.md +790 -0
- package/docs/issues/README.md +10 -0
- package/docs/issues/pending/ANALYSIS-ally-premature-recovery-notice-and-task-state-semantics-2026-03-18.md +295 -0
- package/docs/issues/resolved/ANALYSIS-approval-waiting-visible-but-approval-artifact-missing-2026-03-16.md +466 -0
- package/docs/issues/resolved/ANALYSIS-blocking-state-visible-without-user-actionable-artifact-2026-03-16.md +261 -0
- package/docs/issues/resolved/ANALYSIS-codex-app-server-transport-disconnect-semantics-2026-03-14.md +606 -0
- package/docs/issues/resolved/ANALYSIS-premature-terminalization-on-fresh-thread-poll-and-object-error-leak-2026-03-16.md +348 -0
- package/docs/issues/resolved/ANALYSIS-runtime-turn-delivery-and-recovery-2026-03-14.md +603 -0
- package/docs/issues/resolved/ANALYSIS-self-test-gap-approval-waiting-visible-but-approval-artifact-missing-2026-03-16.md +166 -0
- package/docs/issues/resolved/ANALYSIS-self-test-gap-blocking-state-visible-without-user-actionable-artifact-2026-03-16.md +186 -0
- package/docs/issues/resolved/ANALYSIS-self-test-gap-premature-terminalization-on-fresh-thread-poll-and-object-error-leak-2026-03-16.md +166 -0
- package/docs/issues/resolved/REPORT-ally-runtime-turn-delivery-3b42fb8-2026-03-15.md +373 -0
- package/docs/manual-acceptance.md +127 -0
- package/docs/ops-runbook.md +44 -0
- package/docs/planning/FEATURE-memory-system.md +748 -0
- package/docs/planning/SPEC-active-turn-steer-and-context-compaction-visibility.md +269 -0
- package/docs/planning/SPEC-approval-rules-inheritance-and-local-validation-lane.md +450 -0
- package/docs/planning/SPEC-assistant-persona-bootstrap.md +199 -0
- package/docs/planning/SPEC-assistant-rename.md +610 -0
- package/docs/planning/SPEC-bridge-app-server-protocol-alignment.md +667 -0
- package/docs/planning/SPEC-claude-runtime-host-for-work-ally.md +434 -0
- package/docs/planning/SPEC-cli-feishu-codex-session-unification.md +236 -0
- package/docs/planning/SPEC-codex-same-machine-session-handoff.md +873 -0
- package/docs/planning/SPEC-feishu-reaction-shortcuts.md +282 -0
- package/docs/planning/SPEC-local-stable-release-boundary.md +166 -0
- package/docs/planning/SPEC-managed-thread-entry-and-surface-mobility.md +862 -0
- package/docs/planning/SPEC-minimal-bridge-semantics-and-user-visible-surface.md +362 -0
- package/docs/planning/SPEC-npm-alpha-distribution-and-install-first-release.md +222 -0
- package/docs/planning/SPEC-remove-websocket-runtime-transport.md +364 -0
- package/docs/planning/SPEC-runtime-abstraction-phase-1.md +424 -0
- package/docs/planning/SPEC-runtime-connection-and-turn-recovery-semantics.md +274 -0
- package/docs/planning/SPEC-session-presence-and-state-visibility.md +397 -0
- package/docs/planning/SPEC-skill-first-capability-packaging.md +338 -0
- package/docs/planning/SPEC-stable-archive-contract.md +456 -0
- package/docs/planning/SPEC-supervised-start-boundary.md +127 -0
- package/docs/planning/SPEC-user-barrier-reduction-and-activation.md +832 -0
- package/docs/planning/ally-next.md +1278 -0
- package/docs/planning/assistant-workbench-spec.md +725 -0
- package/docs/planning/product-workbench.md +283 -0
- package/docs/product-onboarding.md +227 -0
- package/docs/product-spec-standard.md +528 -0
- package/docs/troubleshooting.md +45 -0
- package/docs/user-quickstart.md +46 -0
- package/internal/dispatch.sh +95 -0
- package/internal/lib/common.sh +1450 -0
- package/internal/modules/assistant/manage.sh +1312 -0
- package/internal/modules/bootstrap/setup.sh +144 -0
- package/internal/modules/config/init-env.sh +10 -0
- package/internal/modules/global/manage.sh +154 -0
- package/internal/modules/handoff/manage.sh +54 -0
- package/internal/modules/mcp/manage.sh +83 -0
- package/internal/modules/ops/logs.sh +76 -0
- package/internal/modules/routines/manage.sh +55 -0
- package/internal/modules/runtime/assistant-autosave.sh +26 -0
- package/internal/modules/runtime/restart.sh +6 -0
- package/internal/modules/runtime/start.sh +283 -0
- package/internal/modules/runtime/status.sh +194 -0
- package/internal/modules/runtime/stop.sh +55 -0
- package/internal/modules/runtime/supervisor.sh +216 -0
- package/internal/modules/runtime/update.sh +26 -0
- package/package.json +41 -0
- package/runtime/config/.gitkeep +0 -0
- package/runtime/host/.gitkeep +0 -0
- package/runtime/host/healthcheck-codex-app-server.ts +22 -0
- package/runtime/host/ping-pong-codex-app-server.ts +66 -0
- package/runtime/host/probe-codex-app-server.ts +115 -0
- package/skills/archive-reader/SKILL.md +9 -0
- package/skills/feishu-production-debug/SKILL.md +37 -0
- package/skills/feishu-production-debug/references/feishu-debug-order.md +49 -0
- package/skills/feishu-production-debug/references/platform-permission-baseline.md +23 -0
- package/skills/issue-to-spec-triage/SKILL.md +44 -0
- package/skills/issue-to-spec-triage/references/triage-rules.md +66 -0
- package/skills/memory-digest/SKILL.md +9 -0
- package/skills/post-implementation-closure/SKILL.md +39 -0
- package/skills/post-implementation-closure/references/closure-checklist.md +45 -0
- package/skills/post-implementation-closure/references/doc-drift-map.md +49 -0
- package/skills/product-spec/SKILL.md +244 -0
- package/templates/env.example +5 -0
- package/templates/routines/nightly-memory-digest.yaml +10 -0
- package/templates/workspace/AGENTS.md +26 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
# Feishu Markdown Rendering And Reply Context
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
- Target project: `work-ally`
|
|
6
|
+
- Audience: implementation engineer
|
|
7
|
+
- Scope: Feishu channel only
|
|
8
|
+
- Status: implemented in current branch
|
|
9
|
+
- Goal: define two concrete features for the next implementation pass
|
|
10
|
+
|
|
11
|
+
## Summary
|
|
12
|
+
|
|
13
|
+
This document defines two Feishu-facing features for `work-ally`:
|
|
14
|
+
|
|
15
|
+
1. Markdown-first outbound rendering for AI replies
|
|
16
|
+
2. Reply-context extraction for inbound Feishu replies
|
|
17
|
+
|
|
18
|
+
Both features are motivated by a gap between the current `work-ally` implementation and the newer Feishu integration patterns already proven in OpenClaw.
|
|
19
|
+
|
|
20
|
+
The key conclusion is:
|
|
21
|
+
|
|
22
|
+
- Feishu output should no longer default to plain text for normal model replies.
|
|
23
|
+
- When a user replies to a previous Feishu message, `work-ally` should fetch the replied message and pass its plain-text body into the Codex prompt context.
|
|
24
|
+
|
|
25
|
+
## Background
|
|
26
|
+
|
|
27
|
+
Historically we treated Feishu bot messages as if they could not reliably render Markdown, so `work-ally` built a downgrade layer that converts model output into plain text and uses `interactive` cards only for a few special cases.
|
|
28
|
+
|
|
29
|
+
After inspecting OpenClaw, that assumption is no longer sufficient.
|
|
30
|
+
|
|
31
|
+
OpenClaw demonstrates that Feishu-side Markdown rendering is practical by using Feishu-supported rich message types instead of raw text messages:
|
|
32
|
+
|
|
33
|
+
- `post` message payloads with `tag: "md"`
|
|
34
|
+
- `interactive` card payloads with `tag: "markdown"`
|
|
35
|
+
|
|
36
|
+
OpenClaw also demonstrates that reply context is recoverable by fetching the replied message via the Feishu message API and extracting plain text from the original message body.
|
|
37
|
+
|
|
38
|
+
## Current `work-ally` State
|
|
39
|
+
|
|
40
|
+
### Outbound rendering
|
|
41
|
+
|
|
42
|
+
Current rendering is centered in `bridge/src/channels/feishu/formatter.ts`.
|
|
43
|
+
|
|
44
|
+
Today the implementation behaves like this:
|
|
45
|
+
|
|
46
|
+
- plain replies are normalized and sent as `text`
|
|
47
|
+
- headings, fenced code, and inline code are downgraded to readable plain text
|
|
48
|
+
- tables and system notices are routed to `interactive` cards
|
|
49
|
+
|
|
50
|
+
Key references:
|
|
51
|
+
|
|
52
|
+
- `work-ally/bridge/src/channels/feishu/formatter.ts`
|
|
53
|
+
- `work-ally/bridge/src/channels/feishu/adapter.ts`
|
|
54
|
+
- `work-ally/tests/unit/channel/formatter.test.mjs`
|
|
55
|
+
- `work-ally/tests/unit/channel/feishu-adapter-outbound.test.mjs`
|
|
56
|
+
|
|
57
|
+
This means the current system already has a unified rendering entrypoint, but the default output target is still plain text rather than Feishu Markdown-capable rich messages.
|
|
58
|
+
|
|
59
|
+
### Inbound reply handling
|
|
60
|
+
|
|
61
|
+
Current inbound normalization only extracts the current message body and does not elevate Feishu reply metadata into the normalized message contract.
|
|
62
|
+
|
|
63
|
+
Key references:
|
|
64
|
+
|
|
65
|
+
- `work-ally/bridge/src/channels/feishu/normalize.ts`
|
|
66
|
+
- `work-ally/bridge/src/types.ts`
|
|
67
|
+
- `work-ally/bridge/src/receiver.ts`
|
|
68
|
+
|
|
69
|
+
Today `work-ally` does not:
|
|
70
|
+
|
|
71
|
+
- preserve `parent_id`
|
|
72
|
+
- preserve `root_id`
|
|
73
|
+
- fetch the replied message body
|
|
74
|
+
- append reply-body context into the prompt sent to Codex
|
|
75
|
+
|
|
76
|
+
## OpenClaw Reference Findings
|
|
77
|
+
|
|
78
|
+
### A. Markdown rendering
|
|
79
|
+
|
|
80
|
+
OpenClaw uses two outbound forms.
|
|
81
|
+
|
|
82
|
+
#### 1. `post` rich-text message with Markdown node
|
|
83
|
+
|
|
84
|
+
Reference:
|
|
85
|
+
|
|
86
|
+
- `.cache/openclaw/extensions/feishu/src/send.ts`
|
|
87
|
+
|
|
88
|
+
Relevant logic:
|
|
89
|
+
|
|
90
|
+
- `buildFeishuPostMessagePayload(...)` builds `msg_type: "post"`
|
|
91
|
+
- payload contains `zh_cn.content` with `{ tag: "md", text: messageText }`
|
|
92
|
+
|
|
93
|
+
This is the important correction to the old assumption: OpenClaw is not relying on plain `text` messages for Markdown rendering.
|
|
94
|
+
|
|
95
|
+
#### 2. `interactive` card with Markdown element
|
|
96
|
+
|
|
97
|
+
Reference:
|
|
98
|
+
|
|
99
|
+
- `.cache/openclaw/extensions/feishu/src/send.ts`
|
|
100
|
+
- `.cache/openclaw/extensions/feishu/src/outbound.ts`
|
|
101
|
+
- `.cache/openclaw/extensions/feishu/src/reply-dispatcher.ts`
|
|
102
|
+
- `.cache/openclaw/extensions/feishu/src/streaming-card.ts`
|
|
103
|
+
|
|
104
|
+
Relevant logic:
|
|
105
|
+
|
|
106
|
+
- `buildMarkdownCard(...)` builds card schema with `tag: "markdown"`
|
|
107
|
+
- outbound routing chooses card rendering for explicit card mode or markdown-heavy content such as tables/code blocks
|
|
108
|
+
- streaming replies also use card-based markdown updates
|
|
109
|
+
|
|
110
|
+
For `work-ally`, streaming is not required for this feature set. The main takeaway is that `interactive` cards are a valid, proven Feishu Markdown carrier.
|
|
111
|
+
|
|
112
|
+
### B. Reply-context extraction
|
|
113
|
+
|
|
114
|
+
Reference:
|
|
115
|
+
|
|
116
|
+
- `.cache/openclaw/extensions/feishu/src/send.ts`
|
|
117
|
+
- `.cache/openclaw/extensions/feishu/src/post.ts`
|
|
118
|
+
- `.cache/openclaw/extensions/feishu/src/bot.ts`
|
|
119
|
+
|
|
120
|
+
Relevant logic:
|
|
121
|
+
|
|
122
|
+
- `getMessageFeishu(...)` fetches message details through `client.im.message.get`
|
|
123
|
+
- `parseQuotedMessageContent(...)` extracts plain text from `text`, `post`, and `interactive`
|
|
124
|
+
- inbound bot logic reads `parent_id`, fetches the quoted message, and injects the extracted body into prompt context as `ReplyToBody`
|
|
125
|
+
|
|
126
|
+
This is the exact pattern we want to reuse conceptually in `work-ally`.
|
|
127
|
+
|
|
128
|
+
## Feature 1: Markdown-First Feishu Rendering
|
|
129
|
+
|
|
130
|
+
## Problem
|
|
131
|
+
|
|
132
|
+
Codex model replies are already Markdown-shaped. The current `work-ally` implementation strips or downgrades much of that structure before delivery, which reduces readability and wastes Feishu’s richer message capabilities.
|
|
133
|
+
|
|
134
|
+
## Goal
|
|
135
|
+
|
|
136
|
+
Treat model output as Markdown by default and render it in Feishu through rich message types instead of downgrading normal replies to plain text.
|
|
137
|
+
|
|
138
|
+
## Desired UX
|
|
139
|
+
|
|
140
|
+
- headings should remain visually structured
|
|
141
|
+
- lists should stay lists
|
|
142
|
+
- links should remain links
|
|
143
|
+
- code blocks should render as code blocks when practical
|
|
144
|
+
- tables should render as tables or as the best supported rich equivalent
|
|
145
|
+
|
|
146
|
+
## Recommended design
|
|
147
|
+
|
|
148
|
+
Keep one outbound rendering entrypoint, but make it Markdown-first.
|
|
149
|
+
|
|
150
|
+
Suggested model:
|
|
151
|
+
|
|
152
|
+
1. Keep a single renderer entry such as `renderFeishuMessages(markdown)`.
|
|
153
|
+
2. Stop treating plain `text` as the default reply container for normal model output.
|
|
154
|
+
3. Internally choose the Feishu carrier based on content shape:
|
|
155
|
+
- normal Markdown: use `post` with `tag: "md"`
|
|
156
|
+
- tables, approval/system cards, or content requiring structured blocks: use `interactive`
|
|
157
|
+
4. Keep the adapter thin. It should send already-rendered payloads, not interpret Markdown itself.
|
|
158
|
+
|
|
159
|
+
## Important implementation note
|
|
160
|
+
|
|
161
|
+
Do not collapse everything into one single Feishu carrier type.
|
|
162
|
+
|
|
163
|
+
We want one rendering pipeline, not one message type.
|
|
164
|
+
|
|
165
|
+
The correct simplification is:
|
|
166
|
+
|
|
167
|
+
- one Markdown-first rendering contract
|
|
168
|
+
- two internal outbound carriers when needed
|
|
169
|
+
|
|
170
|
+
This preserves simplicity without forcing all traffic through `interactive` cards.
|
|
171
|
+
|
|
172
|
+
## Suggested contract changes
|
|
173
|
+
|
|
174
|
+
`FeishuRenderedMessage` should likely evolve from:
|
|
175
|
+
|
|
176
|
+
- `text`
|
|
177
|
+
- `interactive`
|
|
178
|
+
|
|
179
|
+
to something closer to:
|
|
180
|
+
|
|
181
|
+
- `post`
|
|
182
|
+
- `interactive`
|
|
183
|
+
- optional retained `text` only for true low-format cases if still needed
|
|
184
|
+
|
|
185
|
+
## Acceptance criteria
|
|
186
|
+
|
|
187
|
+
- a normal multi-paragraph Markdown reply is no longer downgraded to plain `text`
|
|
188
|
+
- headings and links survive as Feishu-rendered rich content
|
|
189
|
+
- fenced code blocks render through a Feishu rich-message path instead of plain-text degradation when supported by the chosen carrier
|
|
190
|
+
- markdown tables no longer require text fallback as the primary path
|
|
191
|
+
- approval/system messages continue to work
|
|
192
|
+
- long replies still chunk safely
|
|
193
|
+
|
|
194
|
+
## Feature 2: Reply-Context Support For Feishu Replies
|
|
195
|
+
|
|
196
|
+
## Problem
|
|
197
|
+
|
|
198
|
+
When a user replies in Feishu to a previous AI message, `work-ally` currently does not carry the replied message body into the prompt context. The model therefore loses an important local reference and may not understand which prior point the user is asking about.
|
|
199
|
+
|
|
200
|
+
## Goal
|
|
201
|
+
|
|
202
|
+
When an inbound Feishu message is a reply to an earlier message, fetch the replied message, extract its plain-text content, and include that content in the prompt sent to Codex.
|
|
203
|
+
|
|
204
|
+
## Non-goal
|
|
205
|
+
|
|
206
|
+
Do not reconstruct the original rich structure.
|
|
207
|
+
|
|
208
|
+
We only need:
|
|
209
|
+
|
|
210
|
+
- replied message id
|
|
211
|
+
- optional topic/root id
|
|
212
|
+
- extracted plain-text body
|
|
213
|
+
|
|
214
|
+
This is enough to support the user experience of “I am asking about that earlier reply.”
|
|
215
|
+
|
|
216
|
+
## Recommended design
|
|
217
|
+
|
|
218
|
+
### Step 1. Extend normalized inbound contract
|
|
219
|
+
|
|
220
|
+
Add Feishu-relevant reply fields to `InboundMessage`, for example:
|
|
221
|
+
|
|
222
|
+
- `replyToMessageId?: string | null`
|
|
223
|
+
- `rootMessageId?: string | null`
|
|
224
|
+
- `replyToText?: string | null`
|
|
225
|
+
|
|
226
|
+
Exact field names can vary, but reply metadata must become part of the normalized bridge contract rather than remaining buried in raw channel payloads.
|
|
227
|
+
|
|
228
|
+
### Step 2. Preserve reply metadata during normalization
|
|
229
|
+
|
|
230
|
+
In `bridge/src/channels/feishu/normalize.ts`, read reply metadata from the Feishu event, especially:
|
|
231
|
+
|
|
232
|
+
- `parent_id`
|
|
233
|
+
- `root_id`
|
|
234
|
+
|
|
235
|
+
These should be lifted into the normalized message object.
|
|
236
|
+
|
|
237
|
+
### Step 3. Add Feishu-side message fetch helper
|
|
238
|
+
|
|
239
|
+
Add channel-local helper logic in the Feishu adapter layer to fetch a message by message id using the Feishu SDK.
|
|
240
|
+
|
|
241
|
+
This helper should extract plain text from at least:
|
|
242
|
+
|
|
243
|
+
- `text`
|
|
244
|
+
- `post`
|
|
245
|
+
- `interactive`
|
|
246
|
+
|
|
247
|
+
The extraction goal is readability, not exact reconstruction.
|
|
248
|
+
|
|
249
|
+
### Step 4. Enrich prompt context before runtime dispatch
|
|
250
|
+
|
|
251
|
+
Before sending the final prompt to Codex, append reply context in a stable, explicit way, for example:
|
|
252
|
+
|
|
253
|
+
```text
|
|
254
|
+
User is replying to a previous Feishu message.
|
|
255
|
+
|
|
256
|
+
Replied message body:
|
|
257
|
+
<plain text extracted from parent message>
|
|
258
|
+
|
|
259
|
+
Current user message:
|
|
260
|
+
<current inbound text>
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
The exact envelope can vary, but the prompt should make the relationship explicit.
|
|
264
|
+
|
|
265
|
+
## Acceptance criteria
|
|
266
|
+
|
|
267
|
+
- if a user replies to an earlier Feishu message, the normalized inbound message includes the reply target id
|
|
268
|
+
- `work-ally` fetches the replied message body before runtime dispatch when possible
|
|
269
|
+
- if the replied message is `text`, `post`, or `interactive`, the fetched body is converted to readable plain text
|
|
270
|
+
- the prompt sent to Codex includes the replied message body in a stable format
|
|
271
|
+
- if fetch fails, the current user message still proceeds normally without breaking the turn
|
|
272
|
+
|
|
273
|
+
## Recommended Implementation Order
|
|
274
|
+
|
|
275
|
+
1. Land reply metadata in normalized inbound contracts.
|
|
276
|
+
2. Land message-fetch helper plus text extraction.
|
|
277
|
+
3. Plumb reply-body context into runtime prompt construction.
|
|
278
|
+
4. Replace Markdown downgrade-first rendering with Markdown-first rich rendering.
|
|
279
|
+
5. Update adapter and unit tests.
|
|
280
|
+
|
|
281
|
+
Reply-context support is lower risk and directly improves conversation quality, so it is a good first implementation step.
|
|
282
|
+
|
|
283
|
+
## Testing Guidance
|
|
284
|
+
|
|
285
|
+
### Unit tests to add or revise
|
|
286
|
+
|
|
287
|
+
- `work-ally/tests/unit/channel/formatter.test.mjs`
|
|
288
|
+
- assert normal Markdown now renders to rich outbound payloads rather than downgraded plain text
|
|
289
|
+
- assert table/code/link cases route to the expected Feishu carrier
|
|
290
|
+
|
|
291
|
+
- `work-ally/tests/unit/channel/feishu-adapter-outbound.test.mjs`
|
|
292
|
+
- assert adapter sends `post` or `interactive` for normal final replies under the new renderer
|
|
293
|
+
- keep approval/system-card coverage intact
|
|
294
|
+
|
|
295
|
+
- new Feishu reply parsing tests
|
|
296
|
+
- normalize `parent_id` and `root_id`
|
|
297
|
+
- verify replied-message fetch is triggered
|
|
298
|
+
- verify fetched `text/post/interactive` content becomes readable plain text
|
|
299
|
+
|
|
300
|
+
- receiver or prompt-construction tests
|
|
301
|
+
- verify reply-body context is appended to the final runtime input
|
|
302
|
+
|
|
303
|
+
### Manual verification checklist
|
|
304
|
+
|
|
305
|
+
- ask for a normal Markdown answer with headings and links
|
|
306
|
+
- ask for a code-heavy answer with fenced code blocks
|
|
307
|
+
- ask for a table-heavy answer
|
|
308
|
+
- in Feishu, reply to an earlier AI answer with “你说的第二点再展开一下”
|
|
309
|
+
- verify the model correctly interprets which prior message is being referenced
|
|
310
|
+
|
|
311
|
+
## Risks And Constraints
|
|
312
|
+
|
|
313
|
+
- Feishu rich message capabilities differ by carrier type, so the renderer must choose the carrier intentionally.
|
|
314
|
+
- Reply-body fetch must be best-effort. Failure to fetch must not fail the turn.
|
|
315
|
+
- Quoted-message support should stay content-first. Do not add structure-preservation scope unless later demanded by product needs.
|
|
316
|
+
- Chunking logic must be revalidated after the outbound rendering change.
|
|
317
|
+
|
|
318
|
+
## Final Recommendation
|
|
319
|
+
|
|
320
|
+
Implement both features, with these guiding rules:
|
|
321
|
+
|
|
322
|
+
- use one Markdown-first rendering pipeline for all model output
|
|
323
|
+
- allow that pipeline to choose between Feishu rich carriers internally
|
|
324
|
+
- support Feishu reply context by fetching the parent message and extracting plain text only
|
|
325
|
+
- do not add streaming or edit support as part of this scope
|
|
326
|
+
|
|
327
|
+
That gives `work-ally` a cleaner delivery path and a better multi-turn remote conversation experience without turning the Feishu bridge into a second agent system.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Completed Topics
|
|
2
|
+
|
|
3
|
+
这里收已经完成、已落地、已不应再按“待实现 spec”理解的专题文档。
|
|
4
|
+
|
|
5
|
+
放进这个目录,表示至少满足下面两条:
|
|
6
|
+
|
|
7
|
+
1. 用户路径或 shipped model 已经成立
|
|
8
|
+
2. 当前继续讨论时,应默认把它当成已交付基线,而不是待做方案
|
|
9
|
+
|
|
10
|
+
这个目录不是历史归档仓库。
|
|
11
|
+
|
|
12
|
+
它的作用是避免两种错误:
|
|
13
|
+
|
|
14
|
+
- 已做完的功能还继续被当成 planning
|
|
15
|
+
- 新 feature 讨论时重复推翻已落地能力
|
|
16
|
+
|
|
17
|
+
当前这里主要放:
|
|
18
|
+
|
|
19
|
+
- 已完成专题 spec
|
|
20
|
+
- 已落地功能的专题回写
|
|
21
|
+
- 产品负责人确认已收口的专题说明
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# Approval Autonomy And Safe Defaults
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
- Target project: `work-ally`
|
|
6
|
+
- Audience: product owner / implementation engineer
|
|
7
|
+
- Scope: runtime approval boundary, self-validation flow, human-in-the-loop contract
|
|
8
|
+
- Status: shipped baseline / 2026-03-15
|
|
9
|
+
- Goal: stop routing low-risk local self-validation through repeated user approvals while keeping real-risk actions under explicit human control
|
|
10
|
+
|
|
11
|
+
## Summary
|
|
12
|
+
|
|
13
|
+
当前审批机制的根问题不是“卡片文案不够好”,而是**审批边界定错了**。
|
|
14
|
+
|
|
15
|
+
对 `work-ally` 这种由 assistant 兼任产品和开发的长期协作场景,很多动作本质上属于:
|
|
16
|
+
|
|
17
|
+
- 本地阅读
|
|
18
|
+
- 本地测试
|
|
19
|
+
- 当前仓库内改动
|
|
20
|
+
- 当前仓库内 commit
|
|
21
|
+
- assistant desk 根目录内的记忆/文档维护
|
|
22
|
+
|
|
23
|
+
这些动作如果每一步都要求飞书用户手动同意,会把用户拖进 assistant 的自测过程里,造成高频、低价值、打断式参与。
|
|
24
|
+
|
|
25
|
+
这条 spec 的核心判断是:
|
|
26
|
+
|
|
27
|
+
> 默认不该让用户审批 assistant 自己的低风险本地验证流程;用户只应被拉入真正需要人类拍板的边界动作。
|
|
28
|
+
|
|
29
|
+
## Background
|
|
30
|
+
|
|
31
|
+
近期真实对话中已经反复暴露出一个协作问题:
|
|
32
|
+
|
|
33
|
+
- assistant 在修复 bug、跑聚焦测试、提交代码时,频繁向飞书用户发起审批
|
|
34
|
+
- 用户被迫反复点“同意”,哪怕动作只是当前仓库内的自测和 commit
|
|
35
|
+
- 这让产品负责人兼开发者的工作流变成“每一步都要等用户当临时操作员”
|
|
36
|
+
|
|
37
|
+
用户已经明确指出:
|
|
38
|
+
|
|
39
|
+
1. 这类审批体验非常蠢
|
|
40
|
+
2. assistant 需要先反思哪些动作根本不该找用户审批
|
|
41
|
+
3. 如果确实存在不能自主决策的边界,也应把协作合同说清楚,而不是默认把所有动作都抛给用户
|
|
42
|
+
|
|
43
|
+
## Problem Statement
|
|
44
|
+
|
|
45
|
+
当前问题有三层。
|
|
46
|
+
|
|
47
|
+
### 1. 低风险动作被错误归类为人工审批项
|
|
48
|
+
|
|
49
|
+
例如:
|
|
50
|
+
|
|
51
|
+
- 查看日志
|
|
52
|
+
- 运行本地测试
|
|
53
|
+
- `git add`
|
|
54
|
+
- 当前仓库内 `git commit`
|
|
55
|
+
- assistant desk 根目录内的稳定文档更新
|
|
56
|
+
|
|
57
|
+
这些动作虽然技术上属于 runtime approval,但从产品语义上看,并不都应该要求飞书用户逐条批准。
|
|
58
|
+
|
|
59
|
+
### 2. 审批边界站在 runtime 视角,而不是站在协作关系视角
|
|
60
|
+
|
|
61
|
+
runtime 看到的是:
|
|
62
|
+
|
|
63
|
+
- 命令执行
|
|
64
|
+
- 文件修改
|
|
65
|
+
|
|
66
|
+
但用户真正关心的是:
|
|
67
|
+
|
|
68
|
+
- 你是不是在做正常自测
|
|
69
|
+
- 你是不是在改当前项目
|
|
70
|
+
- 你是不是要碰项目外、外部系统、破坏性动作或发布动作
|
|
71
|
+
|
|
72
|
+
如果产品不把这层翻译清楚,审批就会持续过度触发。
|
|
73
|
+
|
|
74
|
+
### 3. 用户被卷入 assistant 的内部验证过程
|
|
75
|
+
|
|
76
|
+
用户应该只在需要人类拍板时参与,而不是在 assistant 自己的修复闭环里扮演按钮操作员。
|
|
77
|
+
|
|
78
|
+
## Product Decision
|
|
79
|
+
|
|
80
|
+
建立一套更贴近协作现实的审批边界:
|
|
81
|
+
|
|
82
|
+
### A. assistant 可自主决策的动作
|
|
83
|
+
|
|
84
|
+
默认应允许 assistant 自主完成:
|
|
85
|
+
|
|
86
|
+
- 当前项目仓库内的读操作
|
|
87
|
+
- 当前项目仓库内的测试、lint、局部验证
|
|
88
|
+
- 当前项目仓库内的非破坏性代码改动
|
|
89
|
+
- 当前项目仓库内的 `git add` / `git commit`
|
|
90
|
+
- assistant desk 根目录内的 `SOUL.md` / `NOW.md` / `MEMORY.md` / `MISTAKES.md` / `journal/` / `conversations/` 等维护动作
|
|
91
|
+
- 为排查当前问题所需的本地日志读取与本地状态检查
|
|
92
|
+
|
|
93
|
+
### B. 仍需用户参与拍板的动作
|
|
94
|
+
|
|
95
|
+
默认仍应要求显式人工边界的动作:
|
|
96
|
+
|
|
97
|
+
- `git push`
|
|
98
|
+
- 发布、部署、重启线上入口或影响真实对话可用性的动作
|
|
99
|
+
- 项目外路径写入
|
|
100
|
+
- 可能破坏数据的动作
|
|
101
|
+
- 外网写操作或外部系统副作用
|
|
102
|
+
- 大范围删除 / 重置 / 覆盖
|
|
103
|
+
- 需要用户亲自参与的真人验收、重启、环境变更
|
|
104
|
+
|
|
105
|
+
### C. 产品目标
|
|
106
|
+
|
|
107
|
+
用户不再审批 assistant 的“自测过程”,只审批 assistant 的“风险边界”。
|
|
108
|
+
|
|
109
|
+
## Goals
|
|
110
|
+
|
|
111
|
+
1. 减少飞书里低价值审批打断
|
|
112
|
+
2. 让 assistant 可以独立完成修复、自测、提交的基本闭环
|
|
113
|
+
3. 保留真正高风险动作的人类拍板边界
|
|
114
|
+
4. 把“哪些可自决、哪些必须拉人”变成稳定产品合同,而不是临场猜测
|
|
115
|
+
|
|
116
|
+
## Non-goals
|
|
117
|
+
|
|
118
|
+
本次不做:
|
|
119
|
+
|
|
120
|
+
- 完整权限系统重构
|
|
121
|
+
- 一次性解决所有 runtime 原生 approval 类型
|
|
122
|
+
- 自动放开外部副作用写操作
|
|
123
|
+
- 取消所有人工审批
|
|
124
|
+
|
|
125
|
+
## Scope
|
|
126
|
+
|
|
127
|
+
V1 先收口三件事:
|
|
128
|
+
|
|
129
|
+
1. 明确低风险本地动作的产品白名单
|
|
130
|
+
2. 为这类动作提供默认放行或更低摩擦的授权路径
|
|
131
|
+
3. 明确需要用户配合的高风险边界
|
|
132
|
+
|
|
133
|
+
## Proposed Model
|
|
134
|
+
|
|
135
|
+
### 1. Local Self-Validation Lane
|
|
136
|
+
|
|
137
|
+
引入稳定产品判断:
|
|
138
|
+
|
|
139
|
+
- 当前项目目录
|
|
140
|
+
- 当前 assistant desk
|
|
141
|
+
|
|
142
|
+
这两个根内的低风险动作,属于 assistant 的自驱工作范围。
|
|
143
|
+
|
|
144
|
+
应优先支持:
|
|
145
|
+
|
|
146
|
+
- 无需逐条飞书审批
|
|
147
|
+
- 默认静默自动放行,但在内部状态与 archive 中保持可审计记录
|
|
148
|
+
|
|
149
|
+
### 2. Human-Gated Lane
|
|
150
|
+
|
|
151
|
+
以下动作进入人工拍板:
|
|
152
|
+
|
|
153
|
+
- 发布 / 推送 / 对外副作用
|
|
154
|
+
- 跨项目或跨目录写入
|
|
155
|
+
- 破坏性命令
|
|
156
|
+
- 高成本或不可逆操作
|
|
157
|
+
- 需要用户现实参与的动作(如重启、真人验证)
|
|
158
|
+
|
|
159
|
+
### 3. Cooperation Contract
|
|
160
|
+
|
|
161
|
+
如果某动作 assistant 不能自主完成,应明确告诉用户:
|
|
162
|
+
|
|
163
|
+
- 为什么这步必须你来
|
|
164
|
+
- 你需要做什么
|
|
165
|
+
- 做完后 assistant 会继续什么
|
|
166
|
+
|
|
167
|
+
而不是简单丢一张 runtime 审批卡让用户自己猜。
|
|
168
|
+
|
|
169
|
+
## Implementation Direction
|
|
170
|
+
|
|
171
|
+
优先顺序:
|
|
172
|
+
|
|
173
|
+
1. 先把产品白名单和高风险边界写清楚
|
|
174
|
+
2. 再决定用哪种工程策略实现
|
|
175
|
+
|
|
176
|
+
可选实现方向包括:
|
|
177
|
+
|
|
178
|
+
- 调整 runtime approval policy / trusted boundary
|
|
179
|
+
- 为当前项目与 assistant desk 生成更合理的默认授权配置
|
|
180
|
+
- 针对 `git add` / `git commit` / 本地测试等命令做有限范围的默认放行
|
|
181
|
+
- 在 bridge 层把审批分级,低风险动作走 assistant 自治 lane,高风险动作才出卡
|
|
182
|
+
|
|
183
|
+
V1 不要求一次定死实现方案,但必须先把产品边界拍板。
|
|
184
|
+
|
|
185
|
+
## Acceptance Criteria
|
|
186
|
+
|
|
187
|
+
1. assistant 在当前项目根与 assistant desk 根内做本地修复、自测、改动、提交时,不再频繁要求用户逐条审批
|
|
188
|
+
2. 这类低风险动作默认静默自动放行,不再额外刷“已自动放行”的系统消息
|
|
189
|
+
3. 用户只在高风险、外部副作用或明确需要人类拍板的动作上被拉入
|
|
190
|
+
4. 文档中明确列出:哪些动作 assistant 可直接做,哪些动作必须找用户配合
|
|
191
|
+
5. 用户参与动作时,提示语义从“技术审批”转向“协作边界说明”
|
|
192
|
+
|
|
193
|
+
## Open Questions
|
|
194
|
+
|
|
195
|
+
1. `git commit` 是否默认应进入 assistant 自治白名单
|
|
196
|
+
2. 当前 repo + assistant desk 的 trusted / writable / approval 策略,是否应在 setup 时固化生成
|
|
197
|
+
3. 是否需要为“产品负责人兼开发”提供单独的默认审批 profile
|
|
198
|
+
|
|
199
|
+
## Product Judgment
|
|
200
|
+
|
|
201
|
+
这不是“优化审批卡文案”的小修。
|
|
202
|
+
|
|
203
|
+
这是 `work-ally` 的一个核心产品判断:
|
|
204
|
+
|
|
205
|
+
> assistant 既然承担产品和开发职责,就必须拥有完成低风险本地闭环的默认操作权;否则它就不是负责人,只是一个随时等人点同意的半自动工具。
|