Compare commits
81
Commits
v0.13.6
...
agent-v0.13.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
107dc974e3 | ||
|
|
d23af99708 | ||
|
|
ff746bdc1a | ||
|
|
d4bc10c0f3 | ||
|
|
db5e43d7d5 | ||
|
|
f68584b4e2 | ||
|
|
7adae97baa | ||
|
|
0ed1be1773 | ||
|
|
d5f6dc6990 | ||
|
|
c71c0adf9e | ||
|
|
344008aaec | ||
|
|
cebd513da8 | ||
|
|
2ca19ebf50 | ||
|
|
7756231b3b | ||
|
|
b16fc900bb | ||
|
|
bf8ff7f78e | ||
|
|
bb8c04af6c | ||
|
|
073aa45a30 | ||
|
|
1cf74d3d2c | ||
|
|
163e245b00 | ||
|
|
bf8ece0590 | ||
|
|
83a48a4079 | ||
|
|
cdd573778c | ||
|
|
0265cfb762 | ||
|
|
fb4b9bdd46 | ||
|
|
984f16f178 | ||
|
|
7d487f9326 | ||
|
|
785b1ee468 | ||
|
|
be84534e77 | ||
|
|
f7541203e4 | ||
|
|
24bde0c2e3 | ||
|
|
4366bdc716 | ||
|
|
598c68f955 | ||
|
|
e6a25b779c | ||
|
|
a0b35d1192 | ||
|
|
6c4c054180 | ||
|
|
38d0923d79 | ||
|
|
8dce1d7007 | ||
|
|
6739e8e902 | ||
|
|
fe093bc15f | ||
|
|
3cd6f44d7d | ||
|
|
9e8c98290e | ||
|
|
9836a4cc9d | ||
|
|
bf1d34e897 | ||
|
|
ecf09f4e95 | ||
|
|
a3b7a27045 | ||
|
|
375b2159c8 | ||
|
|
b5d8204986 | ||
|
|
c98eb69cbd | ||
|
|
df89d96803 | ||
|
|
ec08109a09 | ||
|
|
6f06fce039 | ||
|
|
b1eb5d00d5 | ||
|
|
a9b59ceb1c | ||
|
|
935a8f56bf | ||
|
|
17186b0c11 | ||
|
|
c7a93803c9 | ||
|
|
164e89253a | ||
|
|
a8d1dbc11f | ||
|
|
5aad0cf5f7 | ||
|
|
9bd0dae865 | ||
|
|
daa14d0b7e | ||
|
|
ca3c919a34 | ||
|
|
bbae893c45 | ||
|
|
6dd6297414 | ||
|
|
04d9ea9eba | ||
|
|
94852acf82 | ||
|
|
3639566e1b | ||
|
|
1698d9fd3e | ||
|
|
441f14b5f7 | ||
|
|
cc9ea7c718 | ||
|
|
7e11d1099a | ||
|
|
0a8439a855 | ||
|
|
da23810205 | ||
|
|
cb245a6402 | ||
|
|
e84d7bd185 | ||
|
|
6c045bfe2e | ||
|
|
eaf71ce16f | ||
|
|
f1d7197480 | ||
|
|
625182c3dc | ||
|
|
b16b0d10e7 |
@@ -0,0 +1,9 @@
|
||||
from pathlib import Path
|
||||
import difflib
|
||||
files = ['src/main/assistant/assistant-database.test.ts', 'src/main/assistant/activity-history-io.test.ts', 'src/main/assistant/assistant-storage-upgrade.test.ts', 'src/main/assistant/heartbeat-database.test.ts', 'src/main/magic-notes/magic-note-storage.test.ts']
|
||||
patch = ''
|
||||
for name in files:
|
||||
old = Path(name).read_text(encoding='utf-8')
|
||||
new = old.replace('DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;', 'DROP VIEW IF EXISTS supervision_review_current;\n DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;\n DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;\n DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;')
|
||||
patch += ''.join(difflib.unified_diff(old.splitlines(True), new.splitlines(True), fromfile='a/'+name, tofile='b/'+name))
|
||||
Path('.validation-fixtures.patch').write_text(patch, encoding='utf-8')
|
||||
@@ -228,12 +228,14 @@ jobs:
|
||||
set -euo pipefail
|
||||
output="$RUNNER_TEMP/goodbuddy-agent-output"
|
||||
mkdir -p "$output"
|
||||
continue_archive="$(node build/acquire-continue-runtime.cjs "$RUNNER_TEMP/goodbuddy-continue-runtime" "$AGENT_ARCH" "$AGENT_PLATFORM")"
|
||||
node build/agent-package.cjs build \
|
||||
--platform "$AGENT_PLATFORM" \
|
||||
--arch "$AGENT_ARCH" \
|
||||
--minimum-desktop-version "$(node -p "require('./package.json').version")" \
|
||||
--node-archive "$RUNNER_TEMP/goodbuddy-agent-release-inputs/$NODE_ARCHIVE" \
|
||||
--runtime-archive "$RUNNER_TEMP/goodbuddy-agent-release-inputs/$OPENCODE_ARCHIVE" \
|
||||
--continue-archive "$continue_archive" \
|
||||
--output "$output/goodbuddy-agent-$AGENT_VERSION-$AGENT_PLATFORM-$AGENT_ARCH.gbagent" \
|
||||
> "$output/result.json"
|
||||
node - "$output/result.json" "$AGENT_ARCH" "$AGENT_VERSION" <<'NODE'
|
||||
@@ -484,6 +486,7 @@ jobs:
|
||||
AGENT_VERSION: ${{ needs.authorize.outputs.version }}
|
||||
OSS_BUCKET: ${{ vars.ALIYUN_OSS_BUCKET }}
|
||||
OSS_ENDPOINT: ${{ vars.ALIYUN_OSS_ENDPOINT }}
|
||||
OSS_UPLOAD_ENDPOINT: https://oss-accelerate.aliyuncs.com
|
||||
run: |
|
||||
set -euo pipefail
|
||||
export OSS_ACCESS_KEY_ID="$ALIBABA_CLOUD_ACCESS_KEY_ID"
|
||||
@@ -497,7 +500,7 @@ jobs:
|
||||
name="$(basename "$file")"
|
||||
ossutil cp "$file" \
|
||||
"oss://${OSS_BUCKET}/agent-releases/v${AGENT_VERSION}/${name}" \
|
||||
--endpoint "$OSS_ENDPOINT" \
|
||||
--endpoint "$OSS_UPLOAD_ENDPOINT" \
|
||||
--region "$region" \
|
||||
--update
|
||||
done
|
||||
|
||||
@@ -305,11 +305,13 @@ jobs:
|
||||
rm -rf "$directory"
|
||||
mkdir -p "$directory"
|
||||
agent_version="$(node -p "require('./agent-runtime-lock.json').agentVersion")"
|
||||
continue_archive="$(node build/acquire-continue-runtime.cjs "$RUNNER_TEMP/goodbuddy-continue-runtime" "$AGENT_ARCH" "$AGENT_PLATFORM")"
|
||||
node build/agent-ci-bundle.cjs \
|
||||
--platform "$AGENT_PLATFORM" \
|
||||
--arch "$AGENT_ARCH" \
|
||||
--node-archive "$RUNNER_TEMP/goodbuddy-agent-node/$NODE_ARCHIVE" \
|
||||
--opencode-archive "$RUNNER_TEMP/goodbuddy-opencode-runtime/$OPENCODE_ARCHIVE" \
|
||||
--continue-archive "$continue_archive" \
|
||||
--output-directory "$directory/bundle" \
|
||||
--archive "$directory/goodbuddy-agent-$agent_version-$AGENT_PLATFORM-$AGENT_ARCH.gbagent" |
|
||||
tee "$directory/result.json"
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
validate:
|
||||
name: Validate source
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 20
|
||||
timeout-minutes: 30
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
@@ -40,9 +40,22 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Prepare sandboxed Electron tests
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends --yes xvfb xauth
|
||||
sandbox="node_modules/electron/dist/chrome-sandbox"
|
||||
if [[ ! -f "$sandbox" ]]; then
|
||||
rm -rf node_modules/electron/dist
|
||||
force_no_cache=true node node_modules/electron/install.js
|
||||
fi
|
||||
test -f "$sandbox"
|
||||
sudo chown root:root "$sandbox"
|
||||
sudo chmod 4755 "$sandbox"
|
||||
|
||||
- name: Run validators
|
||||
run: |
|
||||
npm test
|
||||
xvfb-run --auto-servernum npm test
|
||||
npm run typecheck
|
||||
npm run lint
|
||||
|
||||
@@ -445,6 +458,7 @@ jobs:
|
||||
env:
|
||||
OSS_BUCKET: ${{ vars.ALIYUN_OSS_BUCKET }}
|
||||
OSS_ENDPOINT: ${{ vars.ALIYUN_OSS_ENDPOINT }}
|
||||
OSS_UPLOAD_ENDPOINT: https://oss-accelerate.aliyuncs.com
|
||||
OSS_REGION: ${{ steps.oss-release.outputs.region }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
@@ -459,7 +473,7 @@ jobs:
|
||||
name="$(basename "$file")"
|
||||
ossutil cp "$file" \
|
||||
"oss://${OSS_BUCKET}/releases/${GITHUB_REF_NAME}/${name}" \
|
||||
--endpoint "$OSS_ENDPOINT" \
|
||||
--endpoint "$OSS_UPLOAD_ENDPOINT" \
|
||||
--region "$OSS_REGION" \
|
||||
--update
|
||||
done
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
--- a/src/main/assistant/assistant-database.test.ts
|
||||
+++ b/src/main/assistant/assistant-database.test.ts
|
||||
@@ -174,7 +174,10 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
- DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
+ DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 35;`)
|
||||
legacy.close()
|
||||
try {
|
||||
@@ -325,7 +328,10 @@
|
||||
DROP VIEW supervision_review_current;
|
||||
DROP TABLE supervision_review_navigation; DROP TABLE supervision_review_batches;
|
||||
DROP TABLE supervision_review_sources; DROP TABLE supervision_review_runs;
|
||||
- DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
+ DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 41;`)
|
||||
legacy.close()
|
||||
try {
|
||||
@@ -1088,6 +1094,9 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 3;
|
||||
`)
|
||||
@@ -1251,6 +1260,9 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 26;
|
||||
`)
|
||||
@@ -1367,6 +1379,9 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 30;
|
||||
`)
|
||||
@@ -1445,6 +1460,9 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 30;
|
||||
`)
|
||||
@@ -1542,6 +1560,9 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 24;
|
||||
`)
|
||||
@@ -1589,6 +1610,9 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 24;
|
||||
`)
|
||||
@@ -1637,6 +1661,9 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 24;
|
||||
`)
|
||||
@@ -1681,6 +1708,9 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 24;
|
||||
`)
|
||||
@@ -1714,6 +1744,9 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 24;
|
||||
`)
|
||||
@@ -1744,6 +1777,9 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 5;
|
||||
`)
|
||||
@@ -1871,6 +1907,9 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 21;
|
||||
COMMIT;
|
||||
@@ -1967,6 +2006,9 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 9;
|
||||
`)
|
||||
@@ -2025,6 +2067,9 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 16`)
|
||||
legacy.close()
|
||||
@@ -2977,6 +3022,9 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 23`)
|
||||
legacy.close()
|
||||
@@ -3141,6 +3189,9 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 18;
|
||||
`)
|
||||
@@ -3654,6 +3705,9 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 31;
|
||||
`)
|
||||
--- a/src/main/assistant/activity-history-io.test.ts
|
||||
+++ b/src/main/assistant/activity-history-io.test.ts
|
||||
@@ -27,6 +27,9 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version=43`)
|
||||
const legacySave = raw.prepare('UPDATE activity_history SET records_json = ?, legacy_history_may_be_incomplete = 1')
|
||||
--- a/src/main/assistant/assistant-storage-upgrade.test.ts
|
||||
+++ b/src/main/assistant/assistant-storage-upgrade.test.ts
|
||||
@@ -101,7 +101,10 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
- DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
+ DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 37`)
|
||||
} finally { legacy.close() }
|
||||
expect(hasPendingAssistantStorageUpgrade(path)).toBe(true)
|
||||
@@ -419,7 +422,10 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
- DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
+ DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = ${sourceVersion}; BEGIN`)
|
||||
const insert = legacy.prepare(
|
||||
`INSERT INTO task_events(
|
||||
--- a/src/main/assistant/heartbeat-database.test.ts
|
||||
+++ b/src/main/assistant/heartbeat-database.test.ts
|
||||
@@ -82,6 +82,9 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 2`)
|
||||
raw.close()
|
||||
@@ -141,6 +144,9 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 20`)
|
||||
raw.close()
|
||||
--- a/src/main/magic-notes/magic-note-storage.test.ts
|
||||
+++ b/src/main/magic-notes/magic-note-storage.test.ts
|
||||
@@ -223,6 +223,9 @@
|
||||
ALTER TABLE activity_history RENAME COLUMN record_order_json TO records_json;
|
||||
DROP TRIGGER messages_review_insert; DROP TRIGGER messages_review_update;
|
||||
DROP TRIGGER messages_review_delete; DROP TRIGGER tasks_review_delete;
|
||||
+ DROP VIEW IF EXISTS supervision_review_current;
|
||||
+ DROP TABLE IF EXISTS supervision_review_navigation; DROP TABLE IF EXISTS supervision_review_batches;
|
||||
+ DROP TABLE IF EXISTS supervision_review_sources; DROP TABLE IF EXISTS supervision_review_runs;
|
||||
DROP TABLE review_checkpoints; ALTER TABLE messages DROP COLUMN review_revision;
|
||||
PRAGMA user_version = 37`)
|
||||
rmSync(join(directory, 'notes'), { recursive: true })
|
||||
@@ -0,0 +1,5 @@
|
||||
--- a/src/renderer/src/App.test.tsx
|
||||
+++ b/src/renderer/src/App.test.tsx
|
||||
@@ -13972 +13972 @@
|
||||
- supervisionReview: { pageSize: 17, batchCharacters: 8000, batchMessages: 30, executionSeconds: 300 }
|
||||
+ supervisionReview: { pageSize: 17, batchCharacters: 8000, batchMessages: 30, executionSeconds: 300, responseKiB: 1024 }
|
||||
@@ -31,7 +31,7 @@ risk or an explicit user requirement.
|
||||
## Design Simplicity
|
||||
|
||||
Follow KISS and YAGNI. Use the smallest conventional design that completes the
|
||||
real user workflow.
|
||||
real user workflow. NO over-engineering or over-design.
|
||||
|
||||
- A demonstrated failure authorizes only the smallest proportional fix, not a
|
||||
general recovery, compatibility, or security framework.
|
||||
@@ -294,7 +294,7 @@ Repository-wide release invariants:
|
||||
|
||||
## Validation
|
||||
|
||||
Run all validators after source changes:
|
||||
Run validators after source changes when function/fix done, not on every source change:
|
||||
|
||||
```text
|
||||
npm test
|
||||
|
||||
@@ -43,6 +43,31 @@ npm run typecheck
|
||||
npm run lint
|
||||
```
|
||||
|
||||
Vitest runs two ordered projects from `vitest.config.ts`. The `unit` project runs
|
||||
first with up to four workers, bounded by Node's available CPU parallelism. Once
|
||||
it finishes, the `integration` project runs with one worker. The serial group
|
||||
contains all test files under `tests/` (including `*.electron.test.ts`),
|
||||
`src/main/agent/**/*runtime*.test.ts`, and
|
||||
`src/agent-daemon/private-endpoint.test.ts`. Other tests remain in the unit group.
|
||||
Both projects inherit the React plugin, jsdom environment, renderer setup, and
|
||||
existing exclusions; test-file isolation remains enabled.
|
||||
|
||||
This preserves serialization for the process/socket tests that previously timed
|
||||
out on Linux with two workers (commit `93721d6`). `sequence.groupOrder` keeps the
|
||||
two phases from overlapping. Existing file filters still work, for example
|
||||
`npm test -- src/main/agent/opencode-runtime.test.ts`. To select a whole phase,
|
||||
use `npm test -- --project unit` or `npm test -- --project integration`. To inspect
|
||||
file selection without running tests, use `npx vitest list --filesOnly`.
|
||||
|
||||
For activity-history persistence, run
|
||||
`npx vitest run src/main/assistant/activity-history-io.test.ts`. This checks both
|
||||
WAL growth and repeated full-history reads: consecutive saves compare against
|
||||
the last committed in-memory records, invalidated on close, data clearing or
|
||||
another SQLite connection's commit. It also checks rollback and retry. The cache
|
||||
does not change the database schema or the full-snapshot IPC contract. WAL size
|
||||
and SQL scan counts do not measure physical disk throughput or conversation
|
||||
message persistence; validate those separately when investigating live I/O.
|
||||
|
||||
监听模式:
|
||||
|
||||
```bash
|
||||
@@ -156,7 +181,7 @@ npm run dist:linux:arm64
|
||||
可安装的远端 Agent 或远端 Runtime payload。没有使用托管 SSH 时,无需下载任何额外
|
||||
组件,缺少远端包也不能阻塞普通桌面打包或发布。
|
||||
|
||||
“设置 > 平台功能 > 远程项目(技术预览)”列出用户数据目录中已经下载或导入的
|
||||
“设置 > 平台功能 > 远程项目”列出用户数据目录中已经下载或导入的
|
||||
Linux x64、Linux arm64 和 macOS arm64 复合 Agent 包。首次打开和手动刷新会从“关于与更新”所选来源读取
|
||||
小型签名目录,显示本地版本、在线最新兼容版本以及 `有更新 / 已是最新 / 未下载 /
|
||||
校验失败`;此检查不会下载 `.gbagent`。只有用户点击“下载”或“更新到”后才传输包,
|
||||
@@ -313,7 +338,7 @@ OSS 先写 `agent-releases/v<version>/` 不可变对象,GitHub Release 公开
|
||||
`agent-catalog.json` 与 `agent-catalog.sig`,避免客户端观察到跨版本组合。Agent Release 必须使用
|
||||
`--latest=false`,不得改变桌面 Release 的 Latest 标记。
|
||||
|
||||
### 远程 OpenCode Runtime 基础
|
||||
### 远程 Runtime 工件
|
||||
|
||||
`remote-runtime-lock.json` 独立锁定首个远程 Runtime:OpenCode 1.18.29 的 Linux x64
|
||||
baseline 与 arm64 官方包 integrity、`bin/opencode` 入口和固定 `acp` 参数。它不同于
|
||||
@@ -321,6 +346,14 @@ baseline 与 arm64 官方包 integrity、`bin/opencode` 入口和固定 `acp`
|
||||
的 Koffi 版本。Agent bundle 将 Koffi loader 作为 external module,并只携带目标
|
||||
Linux 架构的 glibc/musl 原生 binding;构建与导入都会校验其 ELF 架构和 MIT 许可证。
|
||||
|
||||
lock 同时固定 `@continuedev/cli@1.5.47` 的 npm integrity。CN 工件使用
|
||||
`lib/continue/dist/cn.js`,由 Agent 中的 HTTP-to-ACP helper 启动,不能传入原生 `acp`
|
||||
参数。`remote-runtime:build` 与 `build/agent-package.cjs build` 接受
|
||||
`--runtime-id continue`;默认 Agent 组包通过 `--continue-archive` 同时携带 OC/CN。
|
||||
CN 的 JS payload 通过文件摘要与签名校验,执行它的 Node 仍来自 Agent 固定包。
|
||||
默认 workflow 已接入联合组包;开发包与完整桌面到 Linux 的实测见
|
||||
[CN 部署记录](./docs/features/remote-host/runtime-checklist-validation.md#cn-部署与生命周期)。
|
||||
|
||||
远程 Runtime 工件由以下命令独立管理:
|
||||
|
||||
```bash
|
||||
|
||||
+201
-50
@@ -24,7 +24,7 @@ records are listed separately and do not introduce another feature status.
|
||||
packages target compatible x64 and arm64 environments, including UOS, Kylin,
|
||||
Hygon, Zhaoxin, Kunpeng, and Phytium systems. This is not vendor certification.
|
||||
LoongArch has a separate experimental loong64 preview outside standard
|
||||
releases and automatic updates; no preview is planned for 0.13.6. See the
|
||||
releases and automatic updates; it is built only on explicit request. See the
|
||||
[preview boundaries](./docs/development/loongarch-preview-build.md).
|
||||
- [x] **Configurable global shortcut**: Enable, disable, or record an Electron
|
||||
accelerator under Platform Features / General. The default remains
|
||||
@@ -45,6 +45,16 @@ records are listed separately and do not introduce another feature status.
|
||||
menus provide keyboard navigation and exact conversation opening without a
|
||||
full activity dialog. The summary hides when idle, long conversation submenus
|
||||
scroll independently, and project selection continues to show activity counts.
|
||||
Successful background conversations retain a completion notice during the
|
||||
current client session until their chat is visible and loaded, not across restarts.
|
||||
- [x] **Pinned conversations and complete saved history**: Pin common
|
||||
conversations and retain their position after restart. Lists and search no
|
||||
longer stop at 100 conversations, and consecutive sends no longer truncate
|
||||
saved history beyond 500 messages. Model context limits still apply;
|
||||
previously lost messages cannot be restored.
|
||||
- [x] **On-demand contextual help**: Supplementary page and settings
|
||||
explanations use shared title- or field-adjacent help controls with mouse
|
||||
and keyboard access. Important operation consequences and errors remain visible.
|
||||
- [x] **Compact conversation controls**: Composer options share a compact
|
||||
settings panel while Runtime, mode, sending, and queue controls remain
|
||||
available. Conversation search has an inline clear action that restores the
|
||||
@@ -67,6 +77,17 @@ records are listed separately and do not introduce another feature status.
|
||||
model memory after 60 seconds without active or queued work and reloads on
|
||||
demand; fast text/index modes still skip OCR. See the
|
||||
[parsing behavior and real-file evidence](./docs/features/document-processing/chat-attachments-technical-design.md).
|
||||
- [x] **HTTP OCR and persistent attachment previews**: Configure HTTP
|
||||
PaddleOCR-VL under Settings > Document Parsing, shared by chat documents and
|
||||
knowledge imports. Saves originals and extracted images and provides
|
||||
text/image/detail previews, explicit image-to-text actions, and selecting
|
||||
document images into a named conversation draft before sending. Ordinary chat
|
||||
images use image input by default, subject to the effective model and Runtime.
|
||||
This adds no Application Center entry; Local Inference Monitor manages local
|
||||
services only. Real validation covers Windows, direct visual input, and managed
|
||||
OpenCode on the shared Linux Host. Deployment-specific advanced OCR models and
|
||||
other Runtime combinations need separate verification. See
|
||||
[scope and evidence](./docs/features/document-processing/README.md).
|
||||
- [x] **Per-file workspace diffs**: Changed files expose separate staged and
|
||||
unstaged diffs, including deleted, renamed, and untracked files. Refresh
|
||||
reloads the selected diff, browsed directory, and expanded directories; more than 50 changes
|
||||
@@ -85,6 +106,8 @@ records are listed separately and do not introduce another feature status.
|
||||
stash or discard. Managed SSH operations require Agent `0.11.23`.
|
||||
- [x] **Rich responses**: Supports GitHub Flavored Markdown, LaTeX math,
|
||||
constrained Mermaid diagrams, and static in-conversation HTML previews.
|
||||
Mermaid's large-diagram viewer keeps all edges reachable when zoomed and
|
||||
exports the complete diagram as PNG, independently of zoom and pan.
|
||||
Complete HTML and HTML code blocks can be previewed in place after an Agent
|
||||
reply finishes, with icon actions to reveal the source or open a full-screen
|
||||
preview. The feature is on by default and can be disabled under Platform
|
||||
@@ -98,26 +121,56 @@ records are listed separately and do not introduce another feature status.
|
||||
toggle sits beside the theme switch. The right workbar uses a persistent “+”
|
||||
capability catalog and application tabs.
|
||||
Task Center and Workspace are permanent singleton tabs; browser tabs are
|
||||
independent instances. Task Center can show the current project, global
|
||||
tasks, or all projects, and retains the selected scope with the layout.
|
||||
independent instances. Task Center can show the current project or all projects,
|
||||
and retains the selected scope with the layout. Tasks without a project appear
|
||||
under all projects with an “Unbound project” label.
|
||||
Users can open multiple independent terminals for the current local or
|
||||
managed SSH project with bounded output, resizing, termination, and explicit
|
||||
reconnection. Closing a terminal tab ends its Shell; restarting the app
|
||||
restores only an ended tab description and never restarts the Shell. The
|
||||
main sidebar and Magic Notes list support pointer and keyboard resizing;
|
||||
the notes list can be hidden, remembers its layout, and stacks at narrow
|
||||
widths.
|
||||
main sidebar and Magic Notes editor's AI pane support pointer and keyboard
|
||||
resizing. The AI pane remembers its visibility and width, and stacks below
|
||||
the editor at narrow widths.
|
||||
- [ ] **Project Agent Space** (planned): Unifies roles, knowledge, Skills/MCP,
|
||||
models, approval policy, budgets, and timeouts in a Project, with reusable
|
||||
templates.
|
||||
- [ ] **Application navigation and system-tool shortcuts** (design only):
|
||||
Plans fixed system entries, configurable built-in application visibility,
|
||||
and footer shortcuts into the existing workbar. These are not implemented
|
||||
interface changes. See the [design](./docs/features/application-tool-navigation/README.md).
|
||||
- [x] **Application center and navigation**:
|
||||
Clicking the bottom App Center opens a lightweight upward anchored popup with enabled apps,
|
||||
regardless of pinning or opening history, and no modal backdrop. Clicking an app row closes
|
||||
the popup; Local Inference Monitor opens a separate modal preserving the workspace, while other apps
|
||||
open their main content pages. Manage Apps opens searchable cards with settings details;
|
||||
layout and interaction rules are defined in the [UI design](./docs/features/application-tool-navigation/ui-design.md).
|
||||
All four application cards support arrow and drag reordering regardless of enablement or pinning.
|
||||
Cards, the launcher, and the filtered sidebar share one persisted order. Knowledge
|
||||
is always enabled and pinned, with Open and reorder controls. Supervisor, Notes,
|
||||
and Local Inference Monitor have enablement and pinning settings. Supervisor uses
|
||||
the existing Heartbeat identity and defaults to off when no preference is saved;
|
||||
explicit saved choices are preserved. Disabling it retains plans and history,
|
||||
blocks new reviews, and hides sidebar feedback; in-flight work may finish.
|
||||
Enabling the app does not create automatic plans. Older application orders retain
|
||||
their relative order when missing defaults are added.
|
||||
Durable settings saves, including configuration-tool writes, synchronize through change events;
|
||||
reopening the center locks edits until refresh completes, and newer snapshots supersede stale reads.
|
||||
The `local-inference` modal shows a single service list without task history or external
|
||||
connections. Embedding shows its independent process CPU and working-set memory;
|
||||
ASR/OCR explain shared-process attribution limits. ASR readiness is unknown rather
|
||||
than inferred from active requests. Supported service controls retain impact confirmation;
|
||||
failed operations refresh the list and require a new confirmation. TTS remains unavailable.
|
||||
Cancelling inference remains active in service-stop impact until worker
|
||||
acknowledgement or exit. Implementation is connected to production App;
|
||||
acceptance records distinguish full-App checks, component fixtures, real
|
||||
local-engine checks, and remaining platform/package coverage. Historical
|
||||
failures are not the current full-suite result. See the
|
||||
[validation progress](./docs/features/application-tool-navigation/progress.md).
|
||||
- [ ] **Privately deployable application marketplace** (planned): Extends
|
||||
application management with an organization-owned catalog and application
|
||||
distribution. Current management covers built-in apps only, not marketplace
|
||||
browsing or installation. See
|
||||
[FR-15](./docs/features/application-tool-navigation/prd.md#fr-15-私有化市场与-yaml-交换后续).
|
||||
- [ ] **Additional assistant workbar and execution-space capabilities**
|
||||
(planned): Builds on the current workbar and multiple terminals with
|
||||
supervision, unified Runtime monitoring, managed processes, safe static HTML
|
||||
preview, target-pinnable workspace/browser/artifact instances, bottom
|
||||
broader execution supervision, unified Runtime monitoring, managed processes,
|
||||
target-pinnable workspace/browser/artifact instances, bottom
|
||||
docking, and separate windows. Task Center remains the singleton Task index;
|
||||
attachments and knowledge remain in the conversation composer, while memory,
|
||||
when implemented, and historical execution context belong to the associated Task. See
|
||||
@@ -214,8 +267,8 @@ records are listed separately and do not introduce another feature status.
|
||||
first; invalid paths fail at actual launch. On managed Linux ARM Hosts,
|
||||
Runtime activation reuses the registry and manifest verified during setup
|
||||
instead of rehashing or rechecking the complete OpenCode binary.
|
||||
- [x] **Managed SSH OpenCode loop (technical preview)**: Controlled by the
|
||||
separate Remote Projects (Technical Preview) tab under Settings / Platform
|
||||
- [x] **Managed SSH OpenCode and Continue**: Controlled by the
|
||||
separate Remote Projects tab under Settings / Platform
|
||||
Features and disabled by default. Disabling it does not affect local
|
||||
projects, ordinary desktop capabilities, or desktop releases. When enabled,
|
||||
users can manage SSH Hosts with pinned Host Keys, browse bounded remote
|
||||
@@ -227,6 +280,10 @@ records are listed separately and do not introduce another feature status.
|
||||
provider/tool rounds, Runtime processes, a stable model ledger, and a bounded
|
||||
semantic transcript over a private Unix socket and ACP v5. Work continues on
|
||||
the Host after Desktop exit, network loss, or local-process termination.
|
||||
The current Agent may remain resident; a superseded Agent drains active work
|
||||
before exiting. Idle Runtime processes are reclaimed after tasks finish,
|
||||
while shared processes retain other active tasks, including pending questions.
|
||||
Subsequent turns recreate the Runtime and restore conversation history.
|
||||
Reconnection attaches only to the original controller, binding, and
|
||||
operation, then atomically merges provenance into the original conversation
|
||||
without resending the Prompt. Model profiles and API keys enter Agent memory
|
||||
@@ -238,7 +295,7 @@ records are listed separately and do not introduce another feature status.
|
||||
state for Linux x64, Linux arm64, and macOS arm64. Intel Macs are not supported.
|
||||
Downloads occur only after a user action, and
|
||||
packages can also be imported or exported offline. Each compound `.gbagent`
|
||||
package contains the Agent, fixed Node, and the compatible OpenCode Runtime
|
||||
package contains the Agent, fixed Node, and compatible OpenCode and Continue Runtimes
|
||||
maintained by the desktop source. Online sources follow the GitHub/Beijing
|
||||
OSS choice in About and Updates. The cumulative signed catalog binds minimum
|
||||
Desktop version, Agent protocol, platform, architecture, size, SHA-256, and fixed URL.
|
||||
@@ -252,7 +309,8 @@ records are listed separately and do not introduce another feature status.
|
||||
SSH only for that architecture. Projects store only Host, remote path,
|
||||
Runtime selection, and mode. Current Host identity is read when a
|
||||
Workspace/Runtime is first used, then reused by other projects in the same
|
||||
process. Managed SSH conversations expose only supported OpenCode choices.
|
||||
process. Managed SSH conversations expose supported OpenCode and Continue choices;
|
||||
Continue requires the updated compound package and matching Desktop.
|
||||
Multiple projects and conversations can run concurrently on one Host;
|
||||
Agent-owned prompts are not stopped at a fixed model-call count or
|
||||
prompt-wide output-token total, and Runtime output uses transport
|
||||
@@ -264,7 +322,7 @@ records are listed separately and do not introduce another feature status.
|
||||
failure, Agent `SIGKILL`/restart, and recovery from a reopened Desktop SQLite
|
||||
database. Successful tool START/END events appear exactly once, with no
|
||||
Prompt, provider, or tool replay observed. The current Agent source lock is
|
||||
`0.13.0`, while the current Desktop release candidate is `0.13.5`; formal
|
||||
`0.13.4`, while the current Desktop release candidate is `0.13.14`; formal
|
||||
publication status follows the separate Agent and Desktop
|
||||
release channels. Previous macOS validation covered native package installation,
|
||||
detached lifecycle, Attach, real Ask/Execute, and cancellation of tools in
|
||||
@@ -334,15 +392,15 @@ records are listed separately and do not introduce another feature status.
|
||||
paths available to the current local or SSH account, including paths outside
|
||||
the workspace and native subagent work.
|
||||
- [x] **Efficient direct-model workspace tools**: Direct models use bundled
|
||||
ripgrep for compact file discovery and content search, read large UTF-8 files
|
||||
by line, and apply multi-file patches in Execute. Ask can search and read but
|
||||
cannot patch or run commands; packaged ripgrep does not require a system install.
|
||||
Actionable read/search errors let the model correct its arguments and
|
||||
continue. Partial search results remain available with an explicit
|
||||
incomplete-coverage warning.
|
||||
ripgrep with native arguments for file discovery and content search, read large
|
||||
UTF-8 files by line, and apply multi-file patches in Execute. Ask searches stay
|
||||
read-only within the workspace; Execute searches use current-account permissions.
|
||||
No system ripgrep install is required. Search preserves native output and exit
|
||||
codes, with complete large results available through `output_read`; actionable
|
||||
argument errors let the model correct its request and continue.
|
||||
- [x] **Long context and paged tool output**: Message history, long replies,
|
||||
and parsed document text no longer use the previous fixed truncation limits.
|
||||
Direct models can continue reading stored command and Subagent output in
|
||||
Direct models can continue reading stored search, command, and Subagent output in
|
||||
pages; the selected model's context window and bounded transports still apply.
|
||||
- [x] **Native Runtime interaction routing**: OpenCode and Continue questions
|
||||
support choices, yes/no, free-text answers, and skipping through the existing
|
||||
@@ -351,13 +409,19 @@ records are listed separately and do not introduce another feature status.
|
||||
and a newly started managed Runtime;
|
||||
this does not extend question support to arbitrary ACP services.
|
||||
Successful answers and skips retain the original questions and answers
|
||||
across rounds and local conversation reloads; answers discarded by older
|
||||
at their original position across rounds and local conversation reloads; answers discarded by older
|
||||
versions cannot be recovered.
|
||||
Concurrent questions wait in order, duplicate events preserve drafts, and
|
||||
failed submissions remain retryable. Cancelling a pending managed SSH
|
||||
question allows another message in the same conversation.
|
||||
Execute permission confirmations are handled automatically rather than
|
||||
waiting for another approval. See the [interaction boundaries](./docs/features/assistant-workbar/runtime-interactions.md).
|
||||
- [x] **Native execution checklists**: OpenCode and Continue update a
|
||||
read-only checklist above the conversation, with progress saved in history.
|
||||
Explicit clears and remote replay retain request ownership; cancellation
|
||||
does not mark unfinished items complete. Remote delivery requires the matching
|
||||
Agent package. See the
|
||||
[checklist contract](./docs/features/assistant-workbar/runtime-checklist-technical-design.md).
|
||||
- [x] **Experts and Subagents**: Supports explicit experts, team analysis, and
|
||||
up to three experts running in parallel. Experts inherit the parent
|
||||
Ask/Execute mode and can use enabled local direct-model tools; Ask remains
|
||||
@@ -553,6 +617,8 @@ records are listed separately and do not introduce another feature status.
|
||||
[validation evidence and remaining work](./docs/features/knowledge-base/progress.md).
|
||||
- [x] **Knowledge graph**: Supports rule-based, model-based, and hybrid
|
||||
extraction plus entity, relationship, alias, and evidence maintenance.
|
||||
Model extraction adds no fixed 8192-token output cap: OpenAI follows provider
|
||||
limits, and Anthropic uses the model connection's output setting.
|
||||
- [x] **Embedding configuration and retrieval**: Configures compatible
|
||||
Embeddings endpoints and uses them for semantic retrieval.
|
||||
- [x] **Embedding diagnostics and indexing jobs**: Provides real embedding
|
||||
@@ -573,13 +639,53 @@ records are listed separately and do not introduce another feature status.
|
||||
demand or before every response, show zero-result, degraded, failed, and
|
||||
cancelled states, and expose cited context or safely open its source.
|
||||
- [x] **Magic Notes**: A local-first notes and todo workbench with scope
|
||||
management, editing, filtering, and controlled AI comments. The todo view
|
||||
displays the source entry inside the selected item's details, without
|
||||
management, editing, filtering, and controlled AI comments. Notes open from a
|
||||
responsive card overview. Detail displays a continuous stream with independently
|
||||
collapsible left index and right AI pane. The index defaults to 168px and supports
|
||||
pointer and keyboard resizing from 140px to 320px, with saved width preferences;
|
||||
narrow layouts retain the fixed-width drawer without a resize handle.
|
||||
Real first-page thumbnails scroll to
|
||||
records without replacing editors. Successful saves establish the editor's
|
||||
normalized baseline; only unsaved changes require confirmation on leaving. Opening
|
||||
a note shows a blank text/canvas composer above the history, and creation resets it
|
||||
for the next record. Explicit editing updates a saved record. The separate To-dos view offers search, status filters,
|
||||
grouped tasks and direct completion, with instructions, source entries and AI
|
||||
comments in a right detail pane beside a stable task list. Narrow views offer
|
||||
on-demand detail with an explicit return to the list. Wide views support a
|
||||
draggable list/detail divider with a saved width; source-note groups collapse
|
||||
independently without clearing the selected task's details. A content-sized switch sits
|
||||
beside New note in the header; status filters follow search. Source-note
|
||||
navigation returns to the same task context, without
|
||||
showing the previous item's source after a selection change. Failed reads
|
||||
use application notifications, and missing sources have an explicit fallback. The left
|
||||
use application notifications with inline retry, and missing sources have an explicit fallback. The left
|
||||
navigation can show the incomplete-todo count, and create, save, and comment
|
||||
results use application-wide notifications. Agent/MCP note writes automatically
|
||||
refresh the open workbench while preserving selection and unsaved drafts.
|
||||
- [x] **Paged canvas notes**: The integrated PeopleLib Fabric + Quill editor
|
||||
combines flowing body text with pen/highlighter, object selection/transforms,
|
||||
floating text, images and paper templates. Editors and read-only viewers support
|
||||
25%-300% view zoom, 100% reset and responsive Fit width, without changing saved
|
||||
content or export dimensions. Import PDFs as page backgrounds
|
||||
with native text extraction, then export a raster PDF of text and annotations;
|
||||
exported PDFs do not retain a searchable text layer. Manual saves store entry
|
||||
bodies and binary assets in local note files; SQLite retains metadata, indexes,
|
||||
revisions, todos and comments. Backups must coordinate SQLite and the notes
|
||||
directory; there is no one-click backup action. Undo/redo
|
||||
is per annotation page or Quill mode, not global document history; there is no
|
||||
infinite canvas or PNG download button. Drafts, saved entries and canvas-source
|
||||
todos use the default model's image-input capability for page images plus text,
|
||||
or an explicitly labelled text-only fallback. Canvas pages to send selects the
|
||||
first 1-8 pages in current order (default 1) for both text and images; saving and
|
||||
exporting still support 50 pages. Purely visual entries/drafts
|
||||
require an image-capable model. Immediate-comment mode offers manual canvas
|
||||
analysis; after-save automatic analysis failures do not block or undo saving.
|
||||
Text-only comments survive unchanged extracted text; visual comments are
|
||||
invalidated by layout changes and reanalyzed in after-save-auto mode. MCP
|
||||
rejects plain-text replacement of canvas entries. Implementation is complete;
|
||||
final validation status is tracked in the [feature progress record](./docs/features/magic-notes/progress.md).
|
||||
The first upgrade migrates stored bodies into files and raises the database
|
||||
schema to 39 together with attachment changes. Older clients cannot reopen
|
||||
that database; rollback requires a pre-upgrade backup.
|
||||
- [ ] **MCP Server Control Plane** (planned): Unified MCP lifecycle, health
|
||||
checks, reconnection, schema cache, isolation, approval, and audit.
|
||||
- [ ] **Traceable note excerpts and AI editing** (planned): Collects sourced
|
||||
@@ -597,7 +703,9 @@ records are listed separately and do not introduce another feature status.
|
||||
without a persistent legacy-cache truncation warning; previously lost
|
||||
history is not restored. Token usage is grouped by Runtime and
|
||||
model and normalizes the different OpenAI-compatible and Anthropic Messages
|
||||
cache-reporting semantics when showing cache hit rate. Activity is grouped
|
||||
cache-reporting semantics when showing cache hit rate. Project and conversation
|
||||
usage groups show collapsed totals by default and expand to Runtime/model child
|
||||
rows; model grouping remains flat. Activity is grouped
|
||||
by conversation and collapsed by default so long histories do not fill the
|
||||
page.
|
||||
- [x] **Compact conversation tool records**: Expand individual tool records
|
||||
@@ -627,21 +735,38 @@ records are listed separately and do not introduce another feature status.
|
||||
Run Now actions. See the
|
||||
[Task Center PRD](./docs/features/task-and-job/task-center-prd.md) and
|
||||
[Scheduled Task PRD](./docs/features/task-and-job/scheduled-task-prd.md).
|
||||
Status counts and filters identify running tasks and those needing attention;
|
||||
task approvals remain actionable in their task cards.
|
||||
- [x] **Memory and Smart Heartbeat**: Provides periodic review, suggested
|
||||
memories, insights, follow-up tasks, and auditable run history.
|
||||
- [x] **Improved Smart Heartbeat entry and scope**: Smart Heartbeat / Heartbeat
|
||||
Plans is the sole authoritative full-configuration entry. Plans can be
|
||||
- [x] **Automatic supervision settings and scope**: Supervisor / Automatic supervision is
|
||||
the sole authoritative plan configuration, automatic report, and suggestion entry. Daily or weekly plans can be
|
||||
created and edited for Global or one or more selected Projects. Legacy
|
||||
single-project settings migrate without loss, and project-level memory and
|
||||
action output must explicitly target a Project in scope. Task Center and
|
||||
Settings no longer duplicate the form. Partition-aware review, candidate
|
||||
generation, and recall triggers remain to be designed. See the
|
||||
[Smart Heartbeat PRD](./docs/features/smart-heartbeat/prd.md).
|
||||
- [ ] **General supervision** (planned): Uses a fixed supervision section to
|
||||
observe user-selected conversations, Tasks, automations, or experiments and
|
||||
provide evidence-backed comments and requests for human intervention,
|
||||
without automatically speaking, approving tools, or switching to Execute.
|
||||
See the
|
||||
- [x] **Supervisor reviews, story graphs, and activity**: Review a selected scope
|
||||
and period, read dated supervision results and history in Work review, inspect saved result graphs and sources, confirm or revise entities,
|
||||
and preview local knowledge entity writes. Sidebar feedback follows or pins a
|
||||
Conversation or Task and opens its matching result. It shows target names with
|
||||
compact pin/refresh buttons and preserves long-title readability and keyboard
|
||||
focus in narrow sidebars. Activity combines heartbeat
|
||||
and downstream review stages, including failures and links to older results.
|
||||
Automatic stages process only new, changed, or unprocessed source portions;
|
||||
no-change checks skip model calls. Manual review pages through the selected history
|
||||
and saves batches with their facts and sources. Runs can pause and resume from
|
||||
saved progress; settings expose concurrency, timeouts, batch sizes, execution
|
||||
budgets, and response capacity. Activity shows execution stages and saved batch
|
||||
details. Work review and activity use the available panel width.
|
||||
Calls remain read-only without tools and may incur model charges. See the
|
||||
[implementation and evidence](./docs/features/conversation-supervision/progress.md).
|
||||
- [ ] **Further supervision capabilities** (planned): Full event-by-event replay,
|
||||
Experiment targets, chronological manual-edit audits, event
|
||||
triggers, and broader execution observation remain incomplete. Current automatic
|
||||
review respects input budgets and the rolling window; deleting reports does not
|
||||
reset processing progress or rebuild graphs. See the
|
||||
[Conversation Supervision PRD](./docs/features/conversation-supervision/prd.md).
|
||||
- [ ] **Batch runs and comparison lab** (planned): Compares model, Prompt,
|
||||
role, and workflow configurations in batches and summarizes quality,
|
||||
@@ -726,12 +851,29 @@ records are listed separately and do not introduce another feature status.
|
||||
|
||||
### Open source, builds, and releases
|
||||
|
||||
- Current source candidates are Desktop `0.13.6` and Agent `0.13.0`, with
|
||||
OpenCode pinned to `1.18.29`. Publication status follows the independent
|
||||
- Current source candidates are Desktop `0.13.14` and Agent `0.13.4`, with
|
||||
OpenCode pinned to `1.18.29` and Continue to `1.5.47`. Publication status follows the independent
|
||||
Desktop and Agent release channels.
|
||||
- Agent `0.13.0` adds remote image-tool integration and shared Runtime processes
|
||||
with conversation recovery fixes. Its packages require Desktop `0.13.5`;
|
||||
- Desktop `0.13.14` improves paged Supervisor reviews and resume behavior,
|
||||
native ripgrep searches, to-do layouts, and explicit remote follow-up after an
|
||||
uncertain result. Database schema 47 requires a complete pre-upgrade backup
|
||||
for rollback to older clients.
|
||||
- Agent `0.13.3` reclaims idle Runtimes and retires displaced Agents after existing
|
||||
work drains. Its packages require Desktop `0.13.12`;
|
||||
update Desktop first, then the Host environment. Node remains `24.19.0`.
|
||||
- Agent `0.13.4` waits for model delivery acknowledgements before completion,
|
||||
preserves completed results when idle Runtime cleanup fails, and removes verified
|
||||
inactive Agent payloads and owned orphan processes after environment updates.
|
||||
History and active or uncertain installations are retained. Its independent
|
||||
packages require Desktop `0.13.14`; upgrade Desktop first, then update the Host.
|
||||
- Release preparation requires the full local test suite, notes verification,
|
||||
typecheck, and lint. Candidate main-branch CI, its production build, native
|
||||
packaging, and publication verification remain separate acceptance steps.
|
||||
No local production build, packaging, or LoongArch preview is requested.
|
||||
Current validation status is recorded in the
|
||||
[release preparation record](./docs/development/release-preparation-0.13.14.md).
|
||||
- Desktop `0.13.12` and Agent `0.13.3` have completed their native release jobs;
|
||||
this does not replace development-time real-Host scenario coverage.
|
||||
- Validation records remain separate from implementation status. Current-source
|
||||
storage validation covers real local and Linux x64 Host tool workloads,
|
||||
concurrent projects/conversations, cancellation, and lossless database migration.
|
||||
@@ -742,16 +884,14 @@ records are listed separately and do not introduce another feature status.
|
||||
covered by those records. The candidate still requires main CI and
|
||||
native release packaging. See the [Runtime evidence](./docs/features/assistant-workbar/progress.md)
|
||||
and [image-tool evidence](./docs/features/conversation-media-generation/progress.md).
|
||||
- Desktop 0.13.6 history fixes passed 4,301 tests with 67 skipped, type checking,
|
||||
scoped lint, isolated production Electron validation, and three paired
|
||||
synthetic-history comparisons. Development validation used three real text
|
||||
calls, including two on the current-source Linux Agent with Session
|
||||
continuation; it did not alter Agent protocols. Release preparation does not
|
||||
repeat local production builds. The exact candidate still requires its
|
||||
main-branch CI and native tag packaging. At the user's request, the local
|
||||
release-candidate rerun was cancelled after release-note verification; CI is
|
||||
the candidate validation authority. Agent 0.13.0 is not republished for this
|
||||
Desktop update.
|
||||
- Before release preparation, `9836a4c` passed 4,858 local tests with 67 skipped,
|
||||
full typecheck and lint; real-provider canvas analysis remains unverified.
|
||||
Its three native Agent CI builds passed. Desktop CI run `35485936807`
|
||||
passed 4,875 tests but failed eight Electron tests before UI assertions because
|
||||
the Linux sandbox helper was not configured. The candidate configures the
|
||||
helper and runs tests under Xvfb without disabling sandboxing. Candidate-local
|
||||
validation, main-branch CI and native tag packaging are tracked separately;
|
||||
no local production build or package is part of release preparation.
|
||||
- [x] **0BSD open-source license**: Original code can be freely used, copied,
|
||||
modified, distributed, and commercialized. Third-party components and
|
||||
resources retain their own licenses.
|
||||
@@ -774,6 +914,17 @@ records are listed separately and do not introduce another feature status.
|
||||
|
||||
### Open interfaces, team collaboration, and remote execution
|
||||
|
||||
- [x] **Remote Continue delivery**: Current source supports a
|
||||
managed CN 1.5.47 package, Agent HTTP-to-ACP adaptation, model routing, project
|
||||
selection, and native checklists. Linux x64 development packages passed real
|
||||
model, cancellation and reconnection checks. Default packages include OC/CN;
|
||||
Windows desktop-to-Linux checks cover installation, checklist updates, long lists,
|
||||
cancellation and restart recovery. Execute session MCP delivery passed real
|
||||
text-model Host checks with a substituted image service, not real image generation.
|
||||
Candidate Agent `0.13.4` requires Desktop `0.13.14`; publication and native
|
||||
platform acceptance remain separate from Linux development evidence. See the
|
||||
[validation record](./docs/features/remote-host/runtime-checklist-validation.md).
|
||||
|
||||
- [x] **Remote task delegation**: Enabled only after the user explicitly
|
||||
configures an endpoint and token, uses HTTP(S) according to the global
|
||||
private-network compatibility setting, and writes results to a persistent
|
||||
|
||||
+37
-16
@@ -16,22 +16,27 @@
|
||||
### 桌面基础、工作空间与上下文
|
||||
|
||||
- [x] **跨平台桌面应用**:支持 Windows、macOS、Linux,以及 `x64`、`arm64` 发布目标。
|
||||
- [x] **信创软硬件覆盖**:正式 Linux 安装包覆盖国产 `x64` 与 `arm64` 架构,可用于统信 UOS、银河麒麟及海光、兆芯、鲲鹏、飞腾等兼容环境。龙芯 LoongArch 另行提供 `loong64` 实验预览版,不属于正式发布矩阵,也未纳入自动更新;`0.13.6` 不构建该预览。这里的覆盖范围不等同于厂商或整机兼容认证,具体状态与限制见[龙芯预览版构建说明](./docs/development/loongarch-preview-build.md)。
|
||||
- [x] **信创软硬件覆盖**:正式 Linux 安装包覆盖国产 `x64` 与 `arm64` 架构,可用于统信 UOS、银河麒麟及海光、兆芯、鲲鹏、飞腾等兼容环境。龙芯 LoongArch 另行提供 `loong64` 实验预览版,不属于正式发布矩阵,也未纳入自动更新;仅在明确要求时构建。这里的覆盖范围不等同于厂商或整机兼容认证,具体状态与限制见[龙芯预览版构建说明](./docs/development/loongarch-preview-build.md)。
|
||||
- [x] **可配置全局快捷唤起**:在“平台功能 / 通用设置”中启停或录制 Electron accelerator;默认保留 `CommandOrControl+Shift+Space`,冲突或保存失败时继续使用上一组已注册快捷键,并显示可处理的状态。
|
||||
- [x] **Projects、独立对话与会话分支**:按项目隔离上下文,管理会话、附件和 Git 工作区变更;本地会话可在稳定状态下复制当前聊天内容到独立分支,分支持续显示来源徽标且不复制 Task、队列或成果归属;项目选择器区分本地、托管 SSH 与远程消息通道项目,托管 SSH 项目按 Host 分组并在 Host 标题显示真实 Agent 连接状态,项目行只保留远端路径。
|
||||
- [x] **跨项目会话活动**:统一入口汇总各项目正在运行和需要处理的对话,保留仍有活动的较早会话。锚定的“项目 → 会话”菜单支持键盘导航和精确打开对话,不再使用整块活动弹窗;空闲时隐藏摘要,长会话子菜单独立滚动,项目选择器继续显示活动数量。
|
||||
后台会话成功完成后,在本次客户端运行期间保留完成提示,直到对应聊天可见且加载完成;提示不跨重启保留。
|
||||
- [x] **会话置顶与完整历史**:常用会话可置顶,重启后保留。列表和搜索不再限于最近 100 个会话,连续发送不再截断超过 500 条的已保存历史;模型上下文限制仍有效,已丢失的历史无法恢复。
|
||||
- [x] **按需上下文帮助**:页面和设置中的补充说明使用标题或字段旁的共享帮助入口,支持鼠标与键盘;关键操作后果和错误提示保持可见。
|
||||
- [x] **紧凑会话控件**:输入选项集中在紧凑设置面板,Runtime、工作模式、发送和队列操作保持可用;会话搜索提供内嵌清除按钮,清除后恢复当前范围列表并把焦点返回输入框。
|
||||
- [x] **会话历史按需加载**:列表只加载轻量摘要,不再一次读入全部会话的过程元数据;已打开和活动会话保留完整详情,搜索、复制、导出、续聊和远程待答问题保持可用。图片合并不会重新发送未变化消息,已确认保存的空闲详情随现有视图缓存释放。详见[历史读取与保留规则](./docs/features/assistant-workbar/execution-history-storage.md#会话列表读取与前端保留)。
|
||||
- [x] **文件、截图、窗口、剪贴板上下文**:用户明确选择后才加入模型上下文。聊天输入框支持 `Ctrl+V` 粘贴一个或多个本地支持文件并自动添加为附件,沿用附件按钮的解析进度与限制;普通文本和截图仍可直接粘贴。详见[聊天附件规则](./docs/features/document-processing/prd.md#321-聊天附件入口)。
|
||||
- [x] **图片型 PPTX 文字识别**:配置本地 OCR 并选择启用 OCR 的解析方式后,可识别内嵌 PNG、JPEG 和 WebP 图片,保留原生文字与幻灯片定位。OCR 在没有活动或排队任务时空闲 60 秒自动释放模型内存,下次按需重载;快速文本/索引模式仍跳过 OCR。详见[解析行为与真实文件验证](./docs/features/document-processing/chat-attachments-technical-design.md)。
|
||||
- [x] **HTTP OCR 与附件持久预览**:在“设置 / 文档解析”配置 HTTP PaddleOCR-VL,供聊天文档和知识库导入共用。保存原件与插图,提供正文/图片/详情预览、显式图片文字提取,以及选图加入指定会话草稿。普通图片默认走图片输入,发送与入队检查实际模型和 Runtime。Windows、直连视觉输入和共享 Linux Host 托管 OpenCode 已有真实验证;部署高级 OCR 模型及其他 Runtime 组合需分别测试。本机推理监控只管理本地服务,不新增应用中心入口,详见[范围与证据](./docs/features/document-processing/README.md)。
|
||||
- [x] **工作区单文件差异**:变更文件可分别查看已暂存和未暂存差异,覆盖删除、重命名和未跟踪文件;刷新会重读当前差异、当前浏览目录和已展开目录,超过 50 项变更可继续加载。
|
||||
- [x] **工作区文件与 Git 管理**:支持文件和目录的新建、重命名、移动、删除、属性查看,以及本地/远程分支搜索与切换、新建分支、显式 Fetch、提交历史和单文件提交差异。工具按文件/Git 视图分组,分支搜索使用统一紧凑表单,子目录保留文字导航而根目录不再显示孤立图标。提交历史贴底,首次展开占 Git 内容区一半,支持调整比例、独立滚动和统一刷新;切换项目自动检测 Git,不沿用其他项目状态。移动限于工作区内,删除须确认,分支冲突不自动 stash 或丢弃改动;托管 SSH 操作需要 Agent `0.11.23`。
|
||||
- [x] **富文本回答**:支持 GitHub Flavored Markdown、LaTeX 数学公式、受控 Mermaid 图表和会话内 HTML 静态预览。Agent 完成回复后,完整 HTML 与 HTML 代码块可在原位置预览,并通过图标按钮查看源码或打开全屏预览;该能力默认开启,可在“平台功能 / 通用设置”关闭,预览不执行脚本、联网、表单提交或窗口操作。
|
||||
- [x] **富文本回答**:支持 GitHub Flavored Markdown、LaTeX 数学公式、受控 Mermaid 图表和会话内 HTML 静态预览。Mermaid 大图放大后可滚动到各个边缘,并可导出完整 PNG,不受当前缩放和拖动位置影响。Agent 完成回复后,完整 HTML 与 HTML 代码块可在原位置预览,并通过图标按钮查看源码或打开全屏预览;该能力默认开启,可在“平台功能 / 通用设置”关闭,预览不执行脚本、联网、表单提交或窗口操作。
|
||||
- [x] **AI 回复与完整会话复制**:已完成的 AI 回复可从消息底部复制不含推理、工具日志和引用元数据的 Markdown 原文;完整会话复制复用同一条经过 Preload/Main 校验的剪贴板路径。
|
||||
- [x] **助手工作栏、多终端与可调布局**:工作栏开关位于明暗主题按钮旁;右侧工作栏使用持久“+”能力目录和应用 Tab,任务中心与工作区为不可关闭的单实例,浏览器可打开多个独立实例。任务中心可切换当前项目、全局任务和全部项目,范围随布局保存。用户可为当前本机或托管 SSH 项目打开多个独立终端,获得有界输出、调整尺寸、结束与显式重连。关闭终端 Tab 会结束 Shell,应用重启只恢复已结束的 Tab 描述,不自动重启 Shell。主侧栏和魔法笔记列表支持鼠标与键盘调宽,魔法笔记列表可隐藏并记住布局,窄宽度下自动堆叠。
|
||||
- [x] **助手工作栏、多终端与可调布局**:工作栏开关位于明暗主题按钮旁;右侧工作栏使用持久“+”能力目录和应用 Tab,任务中心与工作区为不可关闭的单实例,浏览器可打开多个独立实例。任务中心可切换当前项目和所有项目,范围随布局保存;无项目任务在所有项目中标注“未绑定项目”。用户可为当前本机或托管 SSH 项目打开多个独立终端,获得有界输出、调整尺寸、结束与显式重连。关闭终端 Tab 会结束 Shell,应用重启只恢复已结束的 Tab 描述,不自动重启 Shell。主侧栏和魔法笔记编辑器的 AI 评论栏支持鼠标与键盘调宽;AI 评论栏记住显示状态及宽度,窄窗口下移至编辑器底部。
|
||||
- [ ] **项目 Agent Space**(规划中):在 Project 中统一角色、知识、Skills/MCP、模型、审批策略、预算和超时,并支持模板复用。
|
||||
- [ ] **应用导航与系统工具快捷入口**(仅设计):规划固定系统入口、内置应用显隐和打开现有工作栏的底部工具按钮;尚未实现新界面。详见[设计文档](./docs/features/application-tool-navigation/README.md)。
|
||||
- [ ] **通用助手工作栏与执行空间后续能力**(规划中):在现有工作栏和多终端基础上继续加入监督、统一 Runtime、受管进程、静态安全 HTML 预览、可固定目标的工作区/浏览器/成果实例、底部停靠与独立窗口。Task Center 保持 Task 的单例索引,附件与知识库继续由会话输入区管理,未来的记忆与历史执行上下文归入关联 Task。详见 [Feature PRD](./docs/features/assistant-workbar/prd.md)。
|
||||
- [x] **应用中心与导航**:底部应用中心向上展开轻量锚定菜单,无模态遮罩,只列已启用应用,不按打开历史或常驻筛选。点击应用行关闭菜单,本机推理监控打开保留底层工作区的独立 Modal,其余应用打开主内容区;底部“管理应用”打开可搜索卡片及设置详情,布局与交互见 [UI 设计](./docs/features/application-tool-navigation/ui-design.md)。四张应用卡片均支持上下移动和拖动排序,不受启用或常驻状态限制;卡片、菜单和经过过滤的侧栏共用一份保存顺序。知识库始终启用并常驻,提供“打开”和排序;监督者、魔法笔记和本机推理监控可设置启用与常驻。监督者沿用智能心跳身份,缺失启用设置时默认关闭,已明确保存的选择保留;关闭后保留计划和历史,拒绝新回顾并隐藏侧栏反馈,在途运行可完成。启用应用不会自动创建计划。旧应用顺序补齐默认项并保留已有相对顺序。设置持久化后的事件同步各入口,包括工具配置写入;重开中心刷新期间锁定修改,新快照覆盖迟到的旧读取。`local-inference` Modal 直接显示服务列表,不展示任务历史或外部连接;向量服务显示独立进程 CPU 与工作集内存,ASR/OCR 说明共享进程无法独立统计的原因。ASR 就绪状态未知,不从活动请求推断运行中。受支持的服务操作保留影响确认,失败后刷新列表并要求重新确认;TTS 显示不可用。取消中的推理在 Worker 确认或退出前仍计入活动任务及服务停止影响。实现已接入生产 App;验收记录分别列出完整 App、组件 fixture、真实本地引擎和剩余平台/安装包覆盖,不以历史失败代替当前全量结果,详见[验证进度](./docs/features/application-tool-navigation/progress.md)。
|
||||
- [ ] **私有化应用市场**(规划中):在应用管理基础上提供组织自己的目录与应用分发。当前只管理内置应用,尚无市场浏览或安装能力,详见 [FR-15](./docs/features/application-tool-navigation/prd.md#fr-15-私有化市场与-yaml-交换后续)。
|
||||
- [ ] **通用助手工作栏与执行空间后续能力**(规划中):在现有工作栏和多终端基础上继续加入更完整的执行监督、统一 Runtime 监控、受管进程、可固定目标的工作区/浏览器/成果实例、底部停靠与独立窗口。Task Center 保持 Task 的单例索引,附件与知识库继续由会话输入区管理,未来的记忆与历史执行上下文归入关联 Task。详见 [Feature PRD](./docs/features/assistant-workbar/prd.md)。
|
||||
- [ ] **ShareServer Office 协同编辑**(设计中):通过可选 ShareServer 集成 ONLYOFFICE Docs,在助手工作栏中以文件名打开多个文档 Tab,支持 DOCX、XLSX、PPTX 的人工编辑、保存、撤销、源文件冲突保护和后续 AI 选区修改。Office 正文会由所选 ShareServer 和编辑引擎处理;Desktop 不内置或启动 Document Server,未配置服务时不宣称离线编辑。详见[设计文档](./docs/features/office-document-editing/README.md)。
|
||||
|
||||
### Agent Runtime 与模型连接
|
||||
@@ -42,10 +47,10 @@
|
||||
- [x] **当前会话图片工具**:为图片模型开启会话调用后,支持工具调用的聊天模型可在 Execute 中生成图片、修改上传图片或历史成果,无需另开会话或手动切换图片模型。直连文本模型、本地 OpenCode、Continue、DeepSeek Harness 和托管远程 OpenCode 复用统一服务,Runtime 分配由模型设置派生;图片请求可能产生费用,编辑需服务商支持。详见[实现与验证记录](./docs/features/conversation-media-generation/progress.md)。
|
||||
- [x] **跨项目 Runtime 进程复用**:兼容配置的本地 OpenCode 和 DeepSeek Harness 共享重型进程,托管 OpenCode 会话在同一 Host 上复用进程。项目适配器及会话的工作区、模型、工具、问答和取消仍独立路由;远程待答问题和任务状态可在重连或桌面重启后恢复,不重发已接受请求。新的远程行为需要 Desktop `0.13.5` 与 Agent `0.13.0` 配套更新,详见[设计与实测](./docs/features/assistant-workbar/runtime-process-reuse-technical-design.md)。
|
||||
- [x] **直连模型编程 Agent**:本机直连文本模型可在 Execute 模式运行平台 Shell,并可按父请求模式、模型、工作区和能力范围委派一层编程 Subagent;OpenCode、Continue、DeepSeek Harness 和托管 SSH 不重复注入这两个工具。Windows 本机命令与真实模型“修改、测试、修复、复核”闭环已通过,macOS 与 Linux 真机命令仍待对应平台验收。
|
||||
- [x] **高效直连模型工作区工具**:直连模型使用随包 ripgrep 紧凑地发现文件和搜索内容,按行分页读取大型 UTF-8 文件,并在 Execute 中应用多文件补丁。Ask 可以搜索和读取,但不能应用补丁或运行命令;用户无需另行安装 ripgrep。可处理的读取或搜索错误允许模型调整参数后继续,部分搜索结果保留并明确提示覆盖不完整。
|
||||
- [x] **长上下文与工具输出分页**:历史消息、长回复和文档解析文本不再采用旧的固定截断限制。直连模型可分页续读已保存的命令和 Subagent 输出;所选模型的上下文窗口及传输边界仍然有效。
|
||||
- [x] **高效直连模型工作区工具**:直连模型使用随包 ripgrep 的原生参数发现文件和搜索内容,按行分页读取大型 UTF-8 文件,并在 Execute 中应用多文件补丁。Ask 限于工作区只读搜索和读取,Execute 按当前账号权限搜索;用户无需另行安装 ripgrep。搜索保留原生输出与退出码,大结果通过 `output_read` 完整续读,可处理的参数错误允许模型调整后继续。
|
||||
- [x] **长上下文与工具输出分页**:历史消息、长回复和文档解析文本不再采用旧的固定截断限制。直连模型可分页续读已保存的搜索、命令和 Subagent 输出;所选模型的上下文窗口及传输边界仍然有效。
|
||||
- [x] **OpenCode 与 Continue**:使用隔离子进程、环境变量白名单、统一配置、取消、启动与控制请求时限、有界流式输出和活动记录;共享进程回收逻辑保留 Windows 完整进程树终止,并对采用独立进程组的 POSIX 子进程执行组回收。聊天状态检查使用一次性探测 Runtime 并在返回后立即回收,不进入按模型与项目隔离的执行 Runtime 缓存;本机 GoodBuddy 管理的 OpenCode 在该被动检查中只确认所选路径和模型凭据,不启动用后即毁的 Server,设置中的显式连接测试和原生能力清单仍执行完整启动及健康检查,原生能力清单随后与首次实际请求复用同一个执行 Runtime,不再重复冷启动;执行 Runtime 仍按项目复用,但 OpenCode 配置依赖和按内容摘要生成的 Skill 快照在所有本机项目间全局复用,会话数据、工具输出及请求级 MCP 状态继续隔离。本机 GoodBuddy 管理的 OpenCode 只对同一会话内的请求保持顺序,不同会话即使属于同一项目也可并行,不同项目同样可并行;请求级动态 MCP 通过默认通配禁用和当前请求精确放行保持隔离。托管的本机和远端 OpenCode 跳过界面未使用的自动 Git 快照,避免同步差异计算拖住会话,文件工具、子代理与工作区 Git 差异保留;Continue 持续排空不用展示的宿主 stdout,防止控制台输出填满管道。这些调整随新启动的 Runtime 生效,不改写外部 OpenCode Server 配置。OpenCode 与 Continue 不再按固定工具调用数或活动数中止单次运行,已观察到的工具与 Subagent 活动全部保留在本地会话,不再在原生任务继续执行时丢弃后续详情。交互提问只由前台对话回答,定时任务、远程通道和委派等后台执行遇到提问时会立即失败并提示改为前台运行,避免无限等待。本机 OpenCode 启动时直接使用已选择的绝对路径,不预先检查文件或运行 `--version`,路径失效时由实际启动返回错误;托管 Linux ARM Host 激活 Runtime 时复用准备阶段已验证的 registry 和 manifest,不重新哈希或检查完整 OpenCode 二进制。
|
||||
- [x] **托管 SSH OpenCode 闭环(技术预览)**:该能力由“设置 > 平台功能”中的独立“远程项目(技术预览)”页签控制并默认关闭;关闭时不影响本地项目、普通桌面功能或桌面发布。支持 Linux x64、Linux arm64 和 macOS arm64 Host,不支持 Intel Mac;通过 SSH 按需启动 detached Agent,不要求开机服务。Ask 在 Runtime 工具边界保持只读,Execute 使用所选 SSH 账号的完整权限。Agent 持有 Prompt、模型和工具轮次、Runtime 进程及语义 transcript,Desktop 断开后重新 Attach 原操作而不重发 Prompt;模型凭据仅在当前操作内存中使用,不写入 Renderer、SSH 参数、环境或磁盘。设置页按平台和架构显示包状态,用户显式下载、更新或导入/导出包含 Agent、固定 Node 和 OpenCode 的签名 `.gbagent`;三个目标共用一个签名 catalog,继续校验签名和 SHA-256。首次发布混合 catalog 前需先升级 Desktop,旧版 Linux-only 目录读取器无法解析包含 Darwin 的目录。macOS 原生安装、生命周期、Attach、真实 Ask/Execute 和独立进程组工具取消已在真实 Host 验证;这不等于新版本已经发布。详细运行边界和 Linux 历史回归证据见[远程主机技术设计](docs/features/remote-host/technical-design.md),正式发布状态以 Agent 与 Desktop 的独立发布渠道为准。
|
||||
- [x] **托管 SSH OpenCode 与 Continue**:该能力由“设置 > 平台功能”中的独立“远程项目”页签控制并默认关闭;关闭时不影响本地项目、普通桌面功能或桌面发布。支持 Linux x64、Linux arm64 和 macOS arm64 Host,不支持 Intel Mac;通过 SSH 按需启动 detached Agent,不要求开机服务。Ask 在 Runtime 工具边界保持只读,Execute 使用所选 SSH 账号的完整权限。Agent 持有 Prompt、模型和工具轮次、Runtime 进程及语义 transcript,Desktop 断开后重新 Attach 原操作而不重发 Prompt;模型凭据仅在当前操作内存中使用,不写入 Renderer、SSH 参数、环境或磁盘。当前 Agent 可以常驻,更新替换的旧 Agent 排空活动任务后退出。任务完成后回收空闲 Runtime,共享进程仍有任务或待答问题时保留;下一轮重建 Runtime 并恢复对话历史。设置页按平台和架构显示包状态,用户显式下载、更新或导入/导出包含 Agent、固定 Node、OpenCode 和 Continue 的签名 `.gbagent`;三个目标共用一个签名 catalog,继续校验签名和 SHA-256。Continue 需要更新后的联合包与配套 Desktop。首次发布混合 catalog 前需先升级 Desktop,旧版 Linux-only 目录读取器无法解析包含 Darwin 的目录。历史 macOS OpenCode 原生安装、生命周期、Attach、真实 Ask/Execute 和独立进程组工具取消已在真实 Host 验证,不代表 Continue 的同等实机覆盖或新版本已经发布。当前源码候选为 Agent `0.13.4`、Desktop `0.13.14`;详细运行边界和 Linux 历史回归证据见[远程主机技术设计](docs/features/remote-host/technical-design.md),正式发布状态以独立发布渠道为准。
|
||||
- [x] **SSH Host 手动环境准备源码链路**:Host Key、认证和系统探针成功后先保存 Host,并只读探测共享 Agent/Runtime;保存 Host、打开项目都不自动安装。Host 卡片只有一个按版本事实显示“安装远程环境”“更新远程环境”或“重新安装”的主按钮,次级 SegmentedControl 选择默认且不持久化的“自动”、Host 下载或 GoodBuddy 传输;“版本匹配”badge 不等同环境健康。自动模式只在 operation/prepare 前探测并择一,显式选择保持有效,任何 prepare、commit 或 adoption 失败都不跨 acquisition 自动 fallback。两种方式把同一签名 compound `.gbagent` 交付到固定 staging 后,共用 control-plane prepare、commit、Agent activate/health、Runtime activate、finalize 与显式 cleanup。GoodBuddy 路径可在同一次操作下载并验证缺失候选、缓存并取得 lease,再有界流式 SFTP 上传一个归档和其中已验证的 bootstrap Node,不把约 294 MiB 整包读入 Main `Buffer`;Host 在解包时完成一次完整 payload 校验。未完成操作只记录暂存 cleanup 所需的 operation ID;下次更新尽力清理旧暂存后重新 prepare,不保存远端 metadata 副本,也不让 cleanup 失败阻塞新更新或回滚健康环境。已有项目在实际使用 Workspace/Runtime 时按需解析 Host current identity 并执行固定 `attach-or-bootstrap`,注册后的 health、capabilities 和 prompt 启动不扫描完整 payload。详见 [设计说明](./docs/features/remote-host/environment-provisioning-technical-design.md)。
|
||||
- **SSH Host 环境准备验证记录**:当前源码已在隔离 Linux x64 环境通过包安装、Ask/Execute、原生子代理工作区外写入、重连及 stop/bootstrap。现有记录未覆盖 Host 卡片的完整 GitHub、北京镜像、Linux x64/arm64、取消和离线 GoodBuddy 传输矩阵,候选 CI/原生打包与系统休眠唤醒也尚未验证;开发记录不代表版本已经发布。
|
||||
- [x] **远程工作区与长任务更新**:Agent `0.11.23` 新增远程文件/Git 管理和可选模型限额,随包 Runtime 不再施加固定十分钟 Prompt 时限;无限请求时长仍保留独立连接超时。请先升级 Desktop 至 `0.12.11`,再下载并更新 Host 上的 Agent。
|
||||
@@ -53,7 +58,8 @@
|
||||
- [x] **DeepSeek Harness(预览)**:使用 GoodBuddy 固定 Host 和 OpenAI 兼容模型连接;优先使用管理员提供的连接,否则跟随兼容的默认模型或首个兼容连接,无需单独重复选择,设置页显示实际管理员或回退模型来源。Ask 只允许调用 Host 中真实注册的 `read`、`skill` 以及 Main 管理的 Web Search/Fetch 代理,拒绝插件同名冒充,Execute 放行全部已启用内置及插件工具,并以当前用户权限运行。图像输入跟随所选模型连接的能力声明,文本模型在 Host 或模型调用前拒绝图片,图片模型通过有界内联内容和临时 Attachment Store 接收 JPEG/PNG。Windows Host 启动和 ACP 会话共用规范工作区路径,同目录不同写法可复用正常 Runtime,不再因路径不一致而创建会话失败。
|
||||
- [x] **DSH npm 插件市场**:市场默认关闭,由用户显式开启后搜索公共 npm 的 `dsh-plugin` 包,使用捆绑 npm 执行精确版本安装和普通 lifecycle scripts,并支持启停、JSON 配置、移除、失败启动自动停用和离线管理已安装插件;关闭市场只隐藏目录与管理界面,不改变已有插件的启停状态,第三方代码不受 Ask 初始化隔离。
|
||||
- [x] **Ask 与 Execute 工作模式**:Ask 保持只读;Execute 是用户对当前本机或 SSH 账号可用工具、进程、网络和可写路径的完整授权,包括工作区外路径及原生子代理工作。
|
||||
- [x] **Runtime 原生交互转交**:OpenCode 与 Continue 的选择、yes/no、自由文本回答和跳过使用现有问答卡片,本机与托管 SSH OpenCode 同时转交属于当前请求的子会话提问。托管 SSH 需要 Desktop `0.13.0`、Agent `0.11.24` 和新启动的托管 Runtime,不扩展到任意 ACP 服务。回答或跳过成功后保留原问题与答案,支持多轮记录和本地会话重载;旧版本已丢弃的答案不能恢复。并行问题按顺序等待回答,重复事件保留草稿,提交失败可重试;取消远程待答后可在同一会话继续发送。Execute 权限确认自动处理,不等待第二次审批;具体支持范围见[交互边界](./docs/features/assistant-workbar/runtime-interactions.md)。
|
||||
- [x] **Runtime 原生交互转交**:OpenCode 与 Continue 的选择、yes/no、自由文本回答和跳过使用现有问答卡片,本机与托管 SSH OpenCode 同时转交属于当前请求的子会话提问。托管 SSH OpenCode 问答需要 Desktop `0.13.0`、Agent `0.11.24` 和新启动的托管 Runtime,不扩展到任意 ACP 服务。回答或跳过成功后在原位置保留问题与答案,支持多轮记录和本地会话重载;旧版本已丢弃的答案不能恢复。并行问题按顺序等待回答,重复事件保留草稿,提交失败可重试;取消远程待答后可在同一会话继续发送。Execute 权限确认自动处理,不等待第二次审批;具体支持范围见[交互边界](./docs/features/assistant-workbar/runtime-interactions.md)。
|
||||
- [x] **原生执行清单**:OpenCode 和 Continue 在对话顶部更新只读执行清单,进度随会话保存。显式清空和远程重放保留请求归属,取消不把未完成条目标成完成;远程交付需要配套 Agent,详见[清单契约](./docs/features/assistant-workbar/runtime-checklist-technical-design.md)。
|
||||
- [x] **专家与 Subagent**:支持显式专家、团队分析和最多三个专家并行分析。专家继承父请求 Ask/Execute 模式,可使用已启用的本机直连模型工具,Ask 仍只读;这些工具不是远程 OpenCode 子会话。聊天先展示可逐项展开的专家完整输出,再在其下展示总 Agent 的综合结果,并随会话保存。
|
||||
- [x] **OpenCode 子代理过程与最终结果**:子代理卡片按顺序显示文字、推理和工具过程,最终结果独立展示。远程实时过程需要 Agent `0.11.20`,旧包仍显示最终结果但不补造缺失过程。
|
||||
- [x] **OpenCode 事件连接回收**:聊天与原生上下文压缩在结束事件迭代前关闭各自订阅,覆盖完成、失败、取消和消费方提前结束,不取消其他并行会话。
|
||||
@@ -84,25 +90,34 @@
|
||||
- [x] **外部知识库接入**:在知识库页面管理 Dify、FastGPT 和 RAGFlow 实例,通过目录或手工 ID 验证并绑定远端知识库,保留 Provider 专属检索配置和引用;对话支持按需检索及每次回答前检索。外部系统只负责知识检索,不接入其 App、Chat、Workflow 或 Agent,也不批量同步、本地索引或修改远端内容。被回答引用的有界检索片段随会话保存在本机。详见[外部知识库接入 PRD](./docs/features/knowledge-base/external-knowledge-prd.md)。
|
||||
- **外部知识库验证记录**:三家 Provider 已通过真实服务、本机 HTTP MCP 和生产 IPC 短答案检查,Dify 另通过完整 App 输入区到回答及引用的链路。现有记录未覆盖 FastGPT/RAGFlow 桌面专属参数、复杂多库问答和远程回答前检索,详见[验证记录](./docs/features/knowledge-base/progress.md)。
|
||||
- [x] **知识图谱**:支持规则、模型和混合抽取,以及实体、关系、别名和证据维护。
|
||||
模型抽取不再额外限制为 8192 tokens;OpenAI 使用服务商上限,Anthropic 使用模型连接的输出设置。
|
||||
- [x] **向量模型配置与检索**:可配置兼容 Embeddings 接口并用于语义检索。
|
||||
- [x] **向量诊断与索引任务**:提供真实向量生成诊断、按文档重建进度、取消、失败状态与重启后结果恢复;每篇成功文档立即可用于检索。
|
||||
- [x] **混合检索测试台**:支持全文、中文词组、向量和图谱通道诊断,可调 Top K、阈值、权重、本地或学习型重排及上下文预算。
|
||||
- [x] **分块、维护与评估**:支持固定、结构化和父子分块,分块维护、可取消重建及双语检索评估。
|
||||
- [x] **受控知识本体**:每个知识库可定义实体、关系、别名和端点约束,保留证据偏移、置信度和抽取来源,并显式提示图谱重建。
|
||||
- [x] **强制检索与引用上下文**:对话可按需或每次先检索,显示零结果、降级、失败与取消状态,并可查看引用上下文或安全打开来源。
|
||||
- [x] **魔法笔记 / Magic Notes**:提供本地优先的笔记与待办工作台、范围管理、编辑、筛选和受控 AI 评论;来源记录直接显示在所选待办的详情中,切换事项不会混入上一项的来源内容,读取失败使用应用通知,来源缺失有明确提示。左侧入口可按设置显示未完成待办数量,创建、保存和评论结果使用统一应用通知。Agent / MCP 写入后,已打开的工作台自动更新,并保留当前选择与未保存草稿。
|
||||
- [x] **魔法笔记 / Magic Notes**:提供本地优先的笔记与待办工作台、范围管理、编辑、筛选和受控 AI 评论。笔记从自适应卡片总览打开;独立待办页提供搜索、状态筛选、分组列表和直接勾选,宽屏列表稳定在左侧,任务说明、来源记录及 AI 评论在右侧同一区域显示,窄屏按需显示详情并提供返回列表。紧凑页签放在页头新建按钮左侧,筛选放在搜索右侧。打开原笔记再返回可恢复任务上下文;切换任务不会混入上一项的来源内容,读取失败使用应用通知并提供就地重试,来源缺失有明确提示。左侧入口可按设置显示未完成待办数量,创建、保存和评论结果使用统一应用通知。Agent / MCP 写入后,已打开的工作台自动更新,并保留当前选择与未保存草稿。
|
||||
- 待办宽屏列表与详情之间可拖动分隔条并记住宽度;来源笔记分组可独立折叠,不清除当前选中任务的详情。
|
||||
- [x] **连续记录工作区**:笔记详情连续展示全部记录,左侧索引默认 168px,可拖动右边界或用键盘在 140–320px 内调宽,并记住偏好;索引与右侧 AI 评论独立收起。每条画布记录显示真实首页缩略图、页数和时间,文字记录显示摘要;点击索引只滚动定位,保留当前编辑器和草稿。打开笔记就在标题下方显示空白输入区,保留文字/画布选择和保存操作;新增保存后清空,供下一条记录使用,旧记录通过编辑入口修改。空白输入区和保存后未再修改的记录返回时不提示;保存失败和真实修改仍受保护。窄屏索引使用固定宽度折叠抽屉,不显示调宽分隔条,AI 评论移至底部。
|
||||
- [x] **分页画布笔记**:已集成 PeopleLib Fabric + Quill 编辑器,支持跨页正文、笔迹、高亮、对象选择及变换、浮动文字、图片和纸张模板。PDF 可作为分页底版导入并提取原生文字;正文和批注可导出为栅格 PDF,导出文件不保留可搜索文字层。用户手动保存后,记录正文与二进制资源存入本地笔记文件,SQLite 保留元数据、索引、修订、待办和评论。备份须协调保存 SQLite 与笔记目录,产品无一键备份入口。撤销/重做限当前批注页或 Quill 模式,不提供全局文档撤销、无限画布或 PNG 下载按钮。草稿、已保存记录和来源为画布的待办按默认模型图像输入能力使用页面图像加文字,或明确标注仅文字降级;“发送画布页数”可选 1~8,默认 1,按当前顺序发送前 N 页,文字和图片范围一致,保存与导出仍支持 50 页。纯视觉记录/草稿需要支持图像输入的模型。即时评论模式提供手动画布分析,保存后自动分析失败不阻断或回滚保存。提取文字不变时保留文字评论;布局变化使视觉评论失效,并在保存后自动评论模式下重新分析。MCP 禁止以纯文字覆写画布记录。实现已完成,最终验证状态见[功能进展](./docs/features/magic-notes/progress.md)。
|
||||
编辑与只读画布均支持 25%–300% 视图缩放、100% 还原及随视口调整的“适应宽度”,不改变保存内容或导出尺寸。
|
||||
首次升级将历史正文迁移到文件,并随附件变更把数据库升级至 schema 39;旧客户端不能再打开,回退需使用升级前备份。
|
||||
- [ ] **MCP Server Control Plane**(规划中):统一 MCP 生命周期、健康检查、重连、Schema 缓存、隔离、审批和审计。
|
||||
- [ ] **可追溯笔记摘录与 AI 编辑**(规划中):从对话、知识和网页收集带来源的摘录,并提供需确认的总结、改写和整理操作。
|
||||
|
||||
### 工作管理、长期协作与工作流
|
||||
|
||||
- [x] **任务、活动与成果**:集中管理任务状态、审计活动和独立成果文件;普通聊天回复只保留在会话中,不再自动复制到成果栏,已有重复聊天 Markdown 从成果列表隐藏但不物理删除。运行记录改由 Main SQLite 保存,不再受旧 Renderer 的 500 条、单详情 4,000 字符或约 2 MB 上限影响,页面按批继续显示;不再常驻旧缓存截断警告,但不会恢复此前已丢失的历史。Token 用量按 Runtime 与模型归类,并针对 OpenAI 兼容与 Anthropic Messages 的不同上报口径归一化展示缓存命中率;活动按会话分组并默认收起,避免长历史占满页面。
|
||||
- [x] **任务、活动与成果**:集中管理任务状态、审计活动和独立成果文件;普通聊天回复只保留在会话中,不再自动复制到成果栏,已有重复聊天 Markdown 从成果列表隐藏但不物理删除。运行记录改由 Main SQLite 保存,不再受旧 Renderer 的 500 条、单详情 4,000 字符或约 2 MB 上限影响,页面按批继续显示;不再常驻旧缓存截断警告,但不会恢复此前已丢失的历史。Token 用量按 Runtime 与模型归类,并针对 OpenAI 兼容与 Anthropic Messages 的不同上报口径归一化展示缓存命中率;用量按项目或会话分组时默认收起并显示汇总,展开后显示 Runtime / 模型子行,按模型分组时保持平铺;活动按会话分组并默认收起,避免长历史占满页面。
|
||||
- [x] **紧凑会话工具记录**:逐项展开工具记录即可查看和复制结果、错误及输入参数;会话与子任务进度使用简洁行,减少重复 Runtime 摘要。OpenCode、Continue、DeepSeek Harness 和直连模型可显示已有文件路径、命令或搜索摘要,并在工具完成和会话重载后保留。
|
||||
- [x] **Task 与定制任务体验**:每个产品级 Task 只关联一条 Conversation,一条 Conversation 可承载多个 Task;左侧会话列表通过行首展开按钮显示带共享状态点的 Task 子项,父会话行不重复任务标签,UI 只展示到 Task,不暴露 Job/Run 层级。新建定制任务可关联当前或新 Conversation,默认 Execute 并沿用 Runtime、工具和审批边界;重复触发复用同一 Task,文本结果回写 Conversation,独立文件和图片保留为成果。普通消息与到期 Scheduled Task 共用 Conversation 级持久发送队列,同一会话一次只执行一项;当前回复期间仍可继续发送,队列按顺序续跑,并允许删除或“立即中断并插入”。Task Center 继续作为完整索引,不建设独立 Automation Center。当前计划触发支持单次、每日和每周;高级时区、Cron、事件触发与重试治理仍按 PRD 逐步实现。详见 [Task Center PRD](./docs/features/task-and-job/task-center-prd.md) 和 [Scheduled Task PRD](./docs/features/task-and-job/scheduled-task-prd.md)。
|
||||
- [x] **记忆与智能心跳**:当前提供周期回顾、建议记忆、洞察、后续任务和可审计运行轨迹。
|
||||
- [x] **定时消息沿用会话**:定时消息使用会话当前历史、Runtime、工作模式和已保存的知识检索设置;任务详情显示实际运行模式,当前 occurrence 未结束时禁止重复“立即运行”。
|
||||
- [x] **智能心跳入口与范围改善**:将“智能心跳 > 心跳计划”作为完整配置的唯一权威入口,支持创建和编辑 Global 或指定一个、多个 Project 的计划;旧单项目配置无损迁移,项目级记忆与行动输出必须显式指定范围内的 Project。Task Center 和设置不再复制心跳表单;按分区回顾、候选生成和唤起条件仍待设计。详见 [智能心跳 PRD](./docs/features/smart-heartbeat/prd.md)。
|
||||
- [ ] **通用监督**(规划中):通过固定监督栏目观察用户选择的会话、任务、自动化或实验对象,提供带证据的评论与人工介入请求,但不自动发言、批准工具或切换 Execute。详见 [会话监督 PRD](./docs/features/conversation-supervision/prd.md)。
|
||||
任务中心通过状态数量和筛选定位运行中及待处理任务,审批操作保留在对应任务卡片中。
|
||||
- [x] **自动监督设置与范围**:“监督者 > 自动监督”统一管理计划、自动监督报告和建议,支持创建和编辑 Global 或指定一个、多个 Project 的每日、每周计划;工作回顾阅读带日期的统一监督结果及历史。旧单项目配置无损迁移,项目级记忆与行动输出必须显式指定范围内的 Project。Task Center 和设置中心不再复制心跳表单;按分区回顾、候选生成和唤起条件仍待设计。详见 [智能心跳 PRD](./docs/features/smart-heartbeat/prd.md)。
|
||||
- [x] **监督者回顾、故事线图谱与活动**:按范围和时间回顾工作,查看保存结果的图谱及来源,确认或修订实体,预览写入本地知识库实体。侧栏可跟随或固定 Conversation、Task,并打开匹配结果。活动合并心跳报告与下游监督的阶段状态,保留失败和旧结果入口。自动阶段只处理新增、修改及未处理的来源片段,无变化时跳过模型;手动回顾重新整理有界历史。回顾只读且禁止工具,模型调用可能产生费用,详见[实施与验证记录](./docs/features/conversation-supervision/progress.md)。
|
||||
- 手动回顾分页处理所选历史,并按批保存事实与来源;支持暂停和从保存进度继续。设置提供并发、超时、批次大小、执行预算和响应容量,活动记录显示实际阶段与已保存批次详情;工作回顾和活动记录使用面板可用全宽。
|
||||
- [ ] **监督后续能力**(规划中):逐事件完整回放、Experiment 目标、人工修改时间序列审计、事件触发及更完整的执行观察仍未完成。当前自动回顾受输入预算和滚动窗口限制,删除报告不会重置处理进度或重建图谱。详见 [会话监督 PRD](./docs/features/conversation-supervision/prd.md)。
|
||||
- [ ] **批量运行与对比实验室**(规划中):对模型、Prompt、角色和工作流配置执行批量对比,汇总质量、耗时、Token、费用、失败率和成果差异。
|
||||
- [ ] **时态记忆与事实冲突检测**(规划中):为记忆和知识图谱增加有效期、当前事实、过期与矛盾检测、事实核验及证据回溯。
|
||||
- [ ] **可视化受控工作流**(规划中):提供版本化 DAG、条件分支、审批、取消和恢复,执行节点继续经过 Main Runtime 边界。
|
||||
@@ -128,10 +143,14 @@
|
||||
|
||||
### 开源、构建与发布
|
||||
|
||||
- 当前源码候选为 Desktop `0.13.6`、Agent `0.13.0`,OpenCode 固定为 `1.18.29`;正式发布状态以 Desktop 与 Agent 的独立发布渠道为准。
|
||||
- Agent `0.13.0` 接入远程图片工具、共享 Runtime 进程并修复会话恢复,复合包要求 Desktop `0.13.5`;请先升级 Desktop,再更新 Host 环境。Node 保持 `24.19.0`。
|
||||
- 当前源码候选为 Desktop `0.13.14`、Agent `0.13.4`,OpenCode 固定为 `1.18.29`、Continue 为 `1.5.47`;正式发布状态以独立发布渠道为准。
|
||||
- Desktop `0.13.14` 改善监督者分页回顾与续跑、ripgrep 原生搜索、待办布局和远程结果未知后的显式续聊。数据库升级至 schema 47,回退旧客户端需使用升级前完整备份。
|
||||
- Agent `0.13.3` 回收空闲 Runtime,并使被替换的 Agent 等已有工作结束后退出;复合包要求 Desktop `0.13.12`,请先升级 Desktop,再更新 Host 环境。Node 保持 `24.19.0`。
|
||||
- Agent `0.13.4` 在完成前等待模型交付确认,空闲 Runtime 回收失败不改变已完成结果,并在环境更新后清理已确认停用的旧程序及所属孤儿进程;保留历史、活动及状态不明的安装。独立包要求 Desktop `0.13.14`,请先升级 Desktop,再更新 Host。
|
||||
- 发布准备要求本地全量测试、发布说明校验、类型检查及 lint;候选主分支 CI、生产构建、原生打包及公开发布核验为独立验收步骤。本轮不做本地生产构建、打包或龙芯预览,当前验证状态见[发布准备记录](./docs/development/release-preparation-0.13.14.md)。
|
||||
- Desktop `0.13.12` 和 Agent `0.13.3` 已完成原生发布流程,但不替代开发期真实 Host 场景覆盖。
|
||||
- 验证记录与功能状态分开。存储已有本地及 Linux x64 Host 真实工具任务、多项目多会话、取消和无损迁移验证;进程复用有 Windows 完整 App 与 Linux x64 Host 证据,PPTX OCR 与空闲内存释放有 Windows 真实文件证据。图片工具已有真实服务商生成/编辑及 Host 传输证据,这些记录未覆盖完整界面自然语言调用和本地/远程切换。候选仍需主分支 CI 及原生发布打包,详见[Runtime 验证记录](./docs/features/assistant-workbar/progress.md)与[图片工具验证记录](./docs/features/conversation-media-generation/progress.md)。
|
||||
- Desktop 0.13.6 历史修复已通过 4,301 项测试(67 项跳过)、类型检查、改动文件 lint、隔离生产 Electron 验证及三组合成历史对照。开发阶段使用 3 次真实文本调用,其中 2 次在当前源码 Linux Agent 上验证 Session 续接,未改变 Agent 协议。发布准备不重跑本地生产构建;按用户要求,本地候选重跑在更新说明校验后取消,交由主分支 CI 验证及原生标签打包。本次 Desktop 更新不重新发布 Agent 0.13.0。
|
||||
- 发布准备前,`9836a4c` 已通过本地 4,858 项测试(67 项跳过)、完整类型检查和 lint;真实服务商画布分析仍未验证。该提交的三个原生 Agent CI 构建通过;桌面 CI `35485936807` 为 4,875 项通过、8 项 Electron 测试失败,均因 Linux 沙箱辅助程序未配置,在页面断言前退出。候选配置该程序并在 Xvfb 下运行测试,不关闭沙箱;候选本地验证、主分支 CI 与原生标签打包分别记录,不在发布准备中运行本地生产构建或打包。
|
||||
- [x] **0BSD 开源许可**:原创代码可自由使用、复制、修改、分发和商用;第三方组件和资源仍遵循各自许可证。
|
||||
- [x] **可复现依赖安装与源码构建**:使用锁定依赖、Node.js 24 和统一的测试、类型检查、Lint、生产构建命令。
|
||||
- [x] **六平台原生发布矩阵**:Windows、macOS、Linux 的 `x64`、`arm64` 目标由原生 Runner 构建,并提供发布清单和 SHA-256 哈希。Windows 提供 NSIS 与 portable ZIP,macOS 仅 DMG,Linux 提供 AppImage、DEB 和 RPM,共 12 个安装包、20 个发布资产。
|
||||
@@ -140,6 +159,8 @@
|
||||
|
||||
### 开放接口、团队协作与远程执行
|
||||
|
||||
- [x] **远程 Continue 交付**:已接入 CN 1.5.47 受管包、Agent HTTP-to-ACP 适配、模型路由、项目选择和原生清单。默认包同时携带 OC/CN;Windows 桌面到 Linux x64 已通过安装、真实模型清单更新、长清单、取消及重启恢复验证。Execute 会话 MCP 已通过真实文本模型 Host 往返,图片服务使用替代接收端,不代表真实生图验证。候选 Agent `0.13.4` 需要 Desktop `0.13.14`;正式发布及原生平台验收与 Linux 开发证据分开,详见[验证记录](./docs/features/remote-host/runtime-checklist-validation.md)。
|
||||
|
||||
- [x] **远程任务委派**:仅在用户显式配置端点和令牌后启用,按全局内网兼容模式使用 HTTP(S),结果进入持久化发件箱。
|
||||
- [ ] **Headless Runtime API**(规划中):提供本机优先的任务、事件、状态和成果 API,以及有范围、有效期、限流和撤销能力的令牌。
|
||||
- [ ] **GoodBuddy Team Hub**(规划中):以可选服务提供组织、RBAC、项目共享、远程 Agent、策略下发和租户审计。
|
||||
|
||||
@@ -53,7 +53,7 @@ current limitations.
|
||||
| Area | Available in GoodBuddy |
|
||||
| --- | --- |
|
||||
| Agent Runtimes | Direct model connections, OpenCode, Continue, and the preview DeepSeek Harness |
|
||||
| Data and execution | Local SQLite storage, read-only `Ask`, full-account `Execute`, and recorded tool activity |
|
||||
| Data and execution | Local SQLite and note-file storage, read-only `Ask`, full-account `Execute`, and recorded tool activity |
|
||||
| Knowledge | File, folder, and web imports with full-text, Chinese phrase, vector, and knowledge graph retrieval |
|
||||
| Desktop releases | Official Windows, macOS, and Linux builds for `x64` and `arm64`, plus an experimental LoongArch preview |
|
||||
| Integrations | Local or hosted model endpoints, custom MCP servers, WeChat ClawBot, WeCom, and DingTalk |
|
||||
@@ -68,6 +68,15 @@ current limitations.
|
||||
|
||||
### Magic Notes and Smart Heartbeat
|
||||
|
||||
Magic Notes displays a continuous record stream, with a narrow left index and
|
||||
independent right AI comments. Both panes can collapse; thumbnails scroll to a
|
||||
record while preserving drafts. It supports rich-text entries and paged canvases with flowing text,
|
||||
handwriting, highlights, images and PDF backgrounds. Import PDF text and export
|
||||
annotated pages as a raster PDF. Canvas saves are manual; AI comments use the
|
||||
default model's image-input capability, with an explicit text-only fallback when
|
||||
images are unsupported. See the [Magic Notes documentation](./docs/features/magic-notes/README.md)
|
||||
for behavior and final validation status.
|
||||
|
||||

|
||||
|
||||

|
||||
@@ -156,7 +165,8 @@ npm run lint
|
||||
|
||||
Scan the QR code to join the GoodBuddy WeChat group:
|
||||
|
||||
<img width="1250" height="1743" alt="46b5c3b5936388b0a55db6fe034a7d7f" src="https://github.com/user-attachments/assets/45cd1736-f1ae-4f99-85be-cdb2cc1160c0" />
|
||||
<img width="1250" height="1743" alt="8d6cd5dc3f347a0f3f1cf3844071b4d3" src="https://github.com/user-attachments/assets/1c92c1b8-d1f7-4270-be2c-b9543beadc11" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+9
-2
@@ -38,7 +38,7 @@ GoodBuddy 是用于模型对话、编程 Agent、知识检索、笔记和定时
|
||||
| 范围 | GoodBuddy 提供的能力 |
|
||||
| --- | --- |
|
||||
| Agent Runtime | 直连模型、OpenCode、Continue 和预览版 DeepSeek Harness |
|
||||
| 数据与执行 | 本地 SQLite、只读 `Ask`、使用当前账号完整权限的 `Execute`、工具调用记录 |
|
||||
| 数据与执行 | 本地 SQLite 与笔记文件存储、只读 `Ask`、使用当前账号完整权限的 `Execute`、工具调用记录 |
|
||||
| 知识库 | 导入文件、目录和网页,通过全文、中文词组、向量和知识图谱检索 |
|
||||
| 桌面版本 | Windows、macOS、Linux 的 `x64` 与 `arm64` 正式版本,以及龙芯 LoongArch 实验预览版 |
|
||||
| 外部连接 | 本机或内网模型服务、自定义 MCP、微信 ClawBot、企业微信和钉钉 |
|
||||
@@ -53,6 +53,12 @@ GoodBuddy 是用于模型对话、编程 Agent、知识检索、笔记和定时
|
||||
|
||||
### 魔法笔记与智能心跳
|
||||
|
||||
魔法笔记连续展示全部记录,左侧窄栏索引与右侧 AI 评论可独立收起,点击缩略图滚动定位并保留草稿。
|
||||
支持富文本记录和分页画布,可混排跨页正文、手写笔迹、高亮、图片与 PDF 底版,
|
||||
导入 PDF 时提取原生文字,并将批注页面导出为栅格 PDF。画布由用户手动保存;AI 评论按
|
||||
默认模型的图像输入能力分析,不支持图像时明确提示仅文字降级。具体行为与最终验证状态见
|
||||
[魔法笔记文档](./docs/features/magic-notes/README.md)。
|
||||
|
||||

|
||||
|
||||

|
||||
@@ -128,7 +134,8 @@ npm run lint
|
||||
|
||||
扫描二维码加入 GoodBuddy 微信群:
|
||||
|
||||
<img width="1250" height="1743" alt="46b5c3b5936388b0a55db6fe034a7d7f" src="https://github.com/user-attachments/assets/cc094468-2885-447a-afb4-91c7ac117fd3" />
|
||||
<img width="1250" height="1743" alt="8d6cd5dc3f347a0f3f1cf3844071b4d3" src="https://github.com/user-attachments/assets/e2cd4874-0b6c-40d4-8867-0a9e843a7cb5" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+119
-42
@@ -35,6 +35,7 @@
|
||||
- 加载、空内容、无结果、失败、只读和权限不足是不同状态,必须分别呈现。
|
||||
- 可撤销操作优先提供撤销,不可撤销操作必须在执行前说明影响范围。
|
||||
- 不依赖颜色单独表达状态或风险。
|
||||
- 当前模型或 Runtime 及切换进度集中显示在会话输入区,顶栏不重复显示可用性标识。切换失败、自动状态检查失败或返回不可用时,通过应用通知说明原因;发送按钮保留关联的无障碍状态说明。
|
||||
|
||||
### 2.5 默认支持浅色与深色主题
|
||||
|
||||
@@ -145,8 +146,9 @@
|
||||
| 变体 | 最大内容宽度 | 适用场景 | 页面映射 |
|
||||
| --- | --- | --- | --- |
|
||||
| `reading` | `820px` | 连续阅读、单列编辑、对话撰写 | 聊天正文与输入区 |
|
||||
| `standard` | `960px` | 常规列表、设置与表单 | 最近对话、设置 |
|
||||
| `dashboard` | `1040px` | 指标、卡片网格、宽表格与审计数据 | 智能心跳、活动记录 |
|
||||
| `standard` | `960px` | 常规列表与表单 | 最近对话 |
|
||||
| `dashboard` | `1040px` | 指标、卡片网格、宽表格与审计数据 | 活动记录 |
|
||||
| `supervisor` | `1440px` | 选择列表、图谱画布、来源详情三栏工作台 | 监督者(含智能心跳设置) |
|
||||
| `master-detail` | 可用空间内流式铺开 | 左侧选择、右侧编辑或预览 | 知识库 |
|
||||
|
||||
共同规则:
|
||||
@@ -176,7 +178,8 @@
|
||||
`PageHeader` 统一接收以下内容:
|
||||
|
||||
- `title`:必填,简短名词或任务名称。
|
||||
- `description`:可选,一行说明当前页面能做什么,不重复标题。
|
||||
- `description`:可选,需要常显的简短上下文,不重复标题或帮助内容。
|
||||
- `help`:可选,补充用途和操作说明,通过标题同行的 `InlineHelp` 按需展开,规则见 6.14。
|
||||
- `scope`:涉及范围的数据页面必填,使用 `ScopeBadge`。
|
||||
- `primaryAction`:可选,页面唯一主操作。
|
||||
- `secondaryActions`:可选,最多两个直接显示,其余进入更多菜单。
|
||||
@@ -193,7 +196,7 @@
|
||||
|
||||
### 6.1 PageTabs
|
||||
|
||||
用于同一一级页面内的同级内容面板,例如智能心跳的“运行概览”和“心跳计划”。
|
||||
用于同一一级页面内的同级内容面板,例如监督者的“工作回顾”和“故事线图谱”。
|
||||
|
||||
- 使用 `tablist`、`tab` 和 `tabpanel` 语义,当前项使用 `aria-selected="true"`。
|
||||
- 一级页面之间的导航由应用主导航承担,不复用 `PageTabs`。
|
||||
@@ -283,9 +286,12 @@
|
||||
|
||||
- 浅色侧栏使用冰蓝灰表面,与白色主内容画布形成明确边界;深色侧栏使用比主画布略亮的蓝灰表面。
|
||||
- 一级导航与最近会话之间、最近会话与底部账户区之间必须有可见结构分隔线。浅色主题使用 `--border-default`,深色主题可在可辨前提下使用 `--border-subtle`。
|
||||
- 当前导航项和当前会话必须同时使用至少三种信号中的两种:强调背景、可见边框、图标或文字强调。浅色主题的当前项优先使用更完整的蓝色选中表面和较高字重。
|
||||
- 当前导航项和当前会话使用一致的淡蓝选中背景、强调色文字和中等字重,不额外描边或添加侧边标记;导航图标随选中状态使用强调色,悬停时保留选中状态。浅深主题遵循相同规则,强制颜色模式保留系统选中边框。
|
||||
- 固定导航行高至少 `36px`、行间距 `4px`,图标统一为 `16px`;导航与最近会话分隔线上下各留 `16px`。普通待办和待处理建议数量使用中性色徽标,不使用危险红色;徽标高度不得改变导航行高。搜索框获得内部焦点时显示完整焦点环。
|
||||
- 未选中项保持平整,不为每一行添加卡片边框或阴影。悬停反馈不得强于选中状态。
|
||||
- 项目选择器下方常驻跨项目活动汇总,空闲时显示“暂无活动”;项目计数与项目、会话级联菜单遵守
|
||||
- 项目选择器顶行保留项目切换与新建按钮;项目设置统一从下拉列表对应行的管理按钮进入,关闭后焦点返回项目选择器。
|
||||
- 项目下拉列表通过 `FloatingPortal` 浮在侧栏与会话区上方,默认宽度 `380px`,不受侧栏裁剪;距窗口边缘至少 `16px`,窄窗口自动收窄,长列表内部滚动。浮层锚定项目选择按钮,窗口、滚动和尺寸变化后重新定位。
|
||||
- 项目选择器下方仅在有运行中、待处理或已完成未查看会话时显示跨项目活动汇总,无活动时隐藏;项目计数与项目、会话级联菜单遵守
|
||||
[项目活动汇总 UI 规则](./docs/features/application-tool-navigation/ui-design.md#10-项目活动汇总)。
|
||||
活动数量不改变项目菜单的稳定分组和顺序。
|
||||
- 桌面停靠状态下,主侧栏宽度允许在 `220px` 至 `420px` 范围内通过指针或键盘调整,
|
||||
@@ -314,9 +320,11 @@
|
||||
接受用户本机选择的 PNG、JPEG 或 WebP 内联数据,宽高不超过 `4096 × 4096`、总像素不
|
||||
超过 1600 万且大小不超过 `512 KB`;不得读取远程 Logo URL。名称或副标题溢出品牌区时
|
||||
单行截断,不能撑宽或挤压侧栏。
|
||||
- 侧栏底部固定显示文件、终端、浏览器和资源监控四个系统工具图标,设置按钮独立靠右。工具
|
||||
图标只负责打开或聚焦助手工作栏面板,不在侧栏复制工具内容。已有稳定设置入口时,不在顶栏
|
||||
重复提供同一入口。
|
||||
- 侧栏底部提供应用中心入口,设置按钮独立靠右。点击应用中心向上展开轻量锚定菜单,无模态遮罩,只列已启用应用,不按打开历史或常驻状态筛选;点击行关闭菜单,知识库、智能心跳和魔法笔记在主内容区打开页面。本机推理监控从菜单、常驻入口或管理 Modal 打开时均使用独立 Modal,保留底层工作区。菜单底部“管理应用”打开既有整窗管理 Modal,共同应用设置沿用设置 Modal。
|
||||
四个内置应用的管理列表、菜单和侧栏共用保存顺序,菜单按启用过滤,侧栏按启用且常驻过滤。知识库、智能心跳始终启用并常驻,可调整位置;管理 Modal 提供“打开”和排序,不提供启用、常驻开关或通用空设置,标签为“始终显示”(英文 `Always shown`)。原工作区配置及心跳单条计划 enabled 保持不变。只有魔法笔记、本机推理监控可修改启用和常驻,取消常驻不关闭功能。当前对话、运行记录和设置保持固定系统位置,不参与应用排序。
|
||||
设置持久化后的事件同步各入口及工具配置写入;重开中心刷新期间锁定修改,新快照不被旧读取覆盖。文件、终端、浏览器和资源监控只通过右侧工作栏访问,不在左侧复制入口。
|
||||
管理 Modal 始终使用紧凑单列横向列表,纵向对应保存顺序;左侧为图标、名称及简短用途,右侧为打开、可用的设置及上下移动操作,行间使用弱分隔线,不加卡片外框。窄窗口操作换行且不溢出,保留共享键盘焦点环。可选应用关闭时禁用“打开”。启用、常驻位于紧凑设置详情;四行均提供上下移动及拖动排序,不受启用或常驻影响,箭头按完整顺序判断边界。搜索时禁用拖动,箭头仍按完整顺序移动,不丢失未命中项。尺寸、返回与滚动重置规则见[应用中心 UI 设计](./docs/features/application-tool-navigation/ui-design.md)。已有稳定设置入口时,不在顶栏重复提供同一入口。
|
||||
- 本机推理监控 Modal 复用整窗 Portal、背景隔离和焦点循环;停止、重启及释放使用其上方的紧凑影响确认。Escape 只关闭最上层,操作提交期间禁止关闭及重复提交;焦点恢复与尺寸见[本机推理监控 UI 规则](./docs/features/application-tool-navigation/ui-design.md#91-本机推理管理)。
|
||||
|
||||
### 6.9 助手工作栏
|
||||
|
||||
@@ -334,10 +342,10 @@
|
||||
- Tab 行只显示已打开实例,行末始终提供“+”普通按钮;点击后在内容区打开应用选择页。
|
||||
“+”不使用 `tab` 语义,也不能因为终端或其他应用已经打开而消失。
|
||||
- 应用注册表分别声明单实例或多实例、默认打开、必须存在、可关闭和排序策略。在工作栏“+”目录中,
|
||||
单实例重复选择时聚焦已有 Tab;浏览器和终端每次选择创建独立实例。主侧栏系统工具快捷
|
||||
入口按当前上下文聚焦最近使用的匹配实例,额外实例仍从“+”目录创建。
|
||||
单实例重复选择时聚焦已有 Tab;浏览器和终端每次选择创建独立实例。选择已有 Tab 只激活
|
||||
该实例,保持其目标和历史;主侧栏不再提供按当前上下文匹配实例的重复工具入口。
|
||||
- Task Center 是默认跟随当前项目的单实例索引,不使用多实例模式。用户可在同一 Tab 内切换
|
||||
“当前项目”“全局任务”和“全部项目”;范围变化不得创建第二个 Task Center。每个 Task 只
|
||||
“当前项目”和“所有项目”;无项目任务在“所有项目”中标注“未绑定项目”。范围变化不得创建第二个 Task Center。每个 Task 只
|
||||
关联一条 Conversation,一条 Conversation 可以关联多个 Task;列表不得复制会话内容,也
|
||||
不得把 Job/Run 提升为可导航 UI 对象。
|
||||
- 各栏目读取当前会话或项目的对应内容;当前内容不可用时仍保留栏目入口并说明原因。
|
||||
@@ -373,6 +381,8 @@
|
||||
遮罩或隔离其内容。分栏计算排除最左侧主导航,主工作区与工作栏采用相同的 `300px`
|
||||
最小宽度;拖动保存两者在可用分栏宽度中的比例,任一侧到达最小值后都不得继续侵占。
|
||||
所有可容纳两侧最小值的尺寸均支持指针和键盘调整,并须保留全部栏目入口。
|
||||
- 工作栏分隔条须保留完整的 `9px` 拖拽命中区域,位于侧栏内部,不能被容器裁剪;栏目内容
|
||||
和原生浏览器视口须避开该区域。浏览器全屏时隐藏分隔条并取消这段预留空间。
|
||||
- 工作栏开关固定在顶栏 `.topbar__actions` 内、紧邻浅色/深色主题按钮之前,同一个按钮负责展开与收起并持续暴露状态;图标中的
|
||||
箭头方向必须随当前动作切换,收起状态提示展开,展开状态提示收起。按钮保持普通图标按钮
|
||||
视觉,不通过持续高亮表达展开状态。工作栏内部不再重复关闭按钮;当前会话没有 Task 时,
|
||||
@@ -397,6 +407,9 @@
|
||||
顶栏,不能保留一条未被遮罩的窗口控制色块。关闭操作由 Modal 自身提供。
|
||||
- 整窗固定遮罩通过 `createPortal(..., document.body)` 挂载,避免页面层叠上下文影响;
|
||||
保留 React Context、关闭与焦点管理,表单样式不得依赖已移出的页面祖先选择器。
|
||||
- 共享锚定菜单 `AnchoredMenu`、同行帮助 `InlineHelp` 和应用通知共用 `FloatingPortal` 的非模态 `popover="manual"`,由浏览器 top layer
|
||||
绘制,避免被 Modal 或滚动容器遮挡。菜单和帮助挂在触发按钮所属 Modal 内,通知跟随当前活动
|
||||
Modal,无 Modal 时挂在 `body`;不能只提高 `z-index` 或解除背景的 `inert`。
|
||||
- 原生浏览器 `WebContentsView` 不受网页 `z-index` 控制。应用级 Modal 打开时统一释放
|
||||
浏览器 viewport;非模态对话框、菜单、局部确认或应用通知与浏览器区域相交时也释放。
|
||||
浮层关闭后只恢复当前活动实例的 viewport,不关闭、重建或刷新网页。接线与测试见
|
||||
@@ -436,14 +449,18 @@
|
||||
尚未完成而禁用该入口。选择入口只展开流程,验证成功前不提交实际值;不得自动采用第一个
|
||||
候选。取消保持原值,验证错误在对应操作附近显示。
|
||||
- 项目选择器属于富信息单选菜单。收起时保持当前项目名称紧凑可见;展开后每项至少显示项目名称,并按数据可用性补充目录、远程来源或其他能帮助辨认项目的信息。托管 SSH 项目在“远程项目”内按已保存 SSH Host 做一层轻量分组,Host 标题旁显示真实 Agent 连接状态;项目副标题只显示远端路径,不重复“托管 SSH”类型。
|
||||
- 项目菜单区分“本地项目 / 远程项目 / 远程通道”,不把消息通道命名为远程项目。“远程项目(技术预览)”关闭时完整移除远程项目分组,而不是显示不可操作的残留选项;本地项目和远程通道继续可用。Agent 连接状态使用“未连接 / 连接中 / 就绪 / 异常”等文字标签,读取状态不得发起连接,且不能只依赖图标或颜色;新增项目来源时不得退回仅列名称的原生 `select`。
|
||||
- 项目菜单区分“本地项目 / 远程项目 / 远程通道”,不把消息通道命名为远程项目。“远程项目”关闭时完整移除远程项目分组,而不是显示不可操作的残留选项;本地项目和远程通道继续可用。Agent 连接状态使用“未连接 / 连接中 / 就绪 / 异常”等文字标签,读取状态不得发起连接,且不能只依赖图标或颜色;新增项目来源时不得退回仅列名称的原生 `select`。
|
||||
|
||||
### 6.12 应用通知与就地反馈
|
||||
|
||||
应用级通知统一进入全局通知视口,页面不得自行复制通知卡片或在内容流中长期堆放短期消息。
|
||||
|
||||
- 右上角通知使用紧凑的中性表面、弱边框和轻阴影,最大宽度为 `360px`,窄窗口保留两侧各 `16px`。状态色仅用于图标,不铺满卡片;正文直接呈现,通用状态标题仅供屏幕阅读器读取。图标、正文与关闭按钮统一垂直居中,不使用各自的上下偏移。
|
||||
- 异步操作成功、无需立即处理的信息,以及不属于某个字段的异步失败,使用应用级 `success`、`info` 或 `error` 通知。
|
||||
- 成功和信息通知默认在约 4.5 秒后自动消失;错误通知保持可见,直到用户关闭或同一去重键的更新替换它。
|
||||
- 成功和信息通知默认在约 2 秒后自动消失;错误通知保持可见,直到用户关闭或同一去重键的更新替换它。
|
||||
- 通知出现不抢焦点;Modal 打开时,通知关闭按钮纳入当前 Modal 的 Tab 循环。聚焦通知时
|
||||
Escape 返回原控件或当前 Modal 的可用控件,不关闭 Modal;关闭通知后同样恢复焦点。
|
||||
打开、关闭或切换 Modal 不重建通知,不重置自动消失计时。
|
||||
- 同一语义和文案的重复通知应去重。通知正文必须有长度上限,不包含凭据、私人内容或未脱敏的提供商响应。
|
||||
- 字段校验、破坏性确认、操作进度、阻塞整个页面的状态,以及需要就地重试或修正的错误保留在相关控件附近。
|
||||
- 就地错误必须与对应字段或操作建立程序化关联;全局错误使用 `alert` 和 assertive 实时区域,成功与信息使用 `status` 和 polite 实时区域。
|
||||
@@ -462,6 +479,28 @@ Switch 用于在两个持久状态之间立即切换,例如启用能力、开
|
||||
- 涉及联网、上传、电脑控制或其他外部影响的 Switch,附近必须持续说明数据去向、权限范围或风险,不能只靠设置名称表达影响。
|
||||
- 自动化测试应按 `switch` 角色查询二元开关,按 `checkbox` 角色查询多选或确认项,防止视觉迁移后语义回退。
|
||||
|
||||
### 6.14 InlineHelp 同行帮助
|
||||
|
||||
页面、区块和字段的补充用途、格式及操作说明统一收进标题或标签旁的 `InlineHelp` 问号按钮。
|
||||
`PageHeader` 与 `SettingsSectionHeader` 通过 `help` 接收这类内容;`description` 保留需要常显的上下文,两处不重复说明。描述缺省或为空时不渲染段落,不保留空行。
|
||||
当前范围、状态、字段错误、操作进度、风险、费用和影响用户决策的限制仍就地可见。混合文案先拆分,再将补充说明移入帮助。
|
||||
|
||||
- 标题或标签与问号使用共享 `inline-help-label` 同行排列,不为帮助另占说明行;字段标签通过 `htmlFor` 关联控件,帮助按钮放在 `label` 外,避免点击帮助触发输入或开关。
|
||||
- 悬停或键盘聚焦按钮时展开;指针可移入浮层阅读。点击(含 Enter、Space 激活)固定展开,再次点击关闭;未固定时,指针和焦点均离开后关闭。
|
||||
- Escape、点击外部或焦点移到外部关闭帮助。Escape 优先关闭帮助,不同时关闭所属 Modal;展开和关闭不主动转移焦点,不建立焦点陷阱。
|
||||
- 按钮提供与标题或标签对应的可访问名称、`aria-expanded`,展开时通过 `aria-controls`、`aria-describedby` 关联 `role="tooltip"` 内容。提供稳定帮助 ID 时,关闭状态保留同 ID 的隐藏说明,展开时由浮层替换,确保字段的 `aria-describedby` 始终有唯一目标。持久标签与必要提示不得依赖浮层。
|
||||
- SSH 密码字段常显认证用途和验证成功后加密保存的说明;Main 处理及不写入命令行、日志、普通设置的细节收进同行帮助。应用常驻说明与关闭后果使用独立翻译键,关闭后果保持常显,不按标点拆分翻译文本。
|
||||
- 浮层复用 `FloatingPortal`,遵循 6.10 的 Modal 归属规则。宽度最多 `360px`,四周保留 `16px` 视口边距;空间不足时翻转或调整位置,长内容在浮层内滚动,窗口及滚动变化后重新定位。
|
||||
- 问号使用共享 `28px × 28px` 点击区域、焦点环与主题令牌。不得另做原生 `title` 长说明或页面专属帮助浮层。
|
||||
|
||||
### 6.15 表单 Modal
|
||||
|
||||
- 复用 `styles.css` 中的 `custom-task-dialog__surface`、`custom-task-dialog__header`、`custom-task-dialog__content` 和 `custom-task-dialog__actions`,与定制任务、SSH Host 等现有弹窗使用同一结构;不为单个业务另建 Modal 框架。
|
||||
- 标题放在 `header` 左侧,右侧只放共享 `icon-button` 的 X 关闭按钮,使用本地化 `aria-label` 和工具提示。取消不放在标题下方或表单顶部。
|
||||
- `footer` 使用共享操作区,按钮整体右对齐,次要取消在前、主要保存在后。放弃修改确认沿用同一操作区,继续编辑在前、危险操作在后。
|
||||
- header、滚动 body、footer 是 surface 的三个直接子区域。沿用共享 Grid 的 `auto minmax(0, 1fr) auto`,只有 body 滚动,短窗口仍可见标题、关闭和提交操作;窄窗口沿用共享外边距、内边距和主题令牌。
|
||||
- X、取消、Escape 和遮罩关闭共用业务关闭检查,不绕过未保存确认或提交锁定。确认中的 Escape 或 X 返回编辑;继续复用 `activateModalFocus`、`trapTabFocus`,保留背景隔离、初始焦点和关闭后的焦点恢复。保存失败保留草稿及可恢复错误。
|
||||
|
||||
## 7. 交互状态
|
||||
|
||||
所有可交互组件必须实现:
|
||||
@@ -605,8 +644,9 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
|
||||
右边缘与用户气泡右边缘对齐;两侧为头像保留独立安全边距,滚动条出现或工作栏缩放不得改变对齐。
|
||||
- 对话标题和当前项目范围位于 `PageHeader` 或对话上下文区,不在消息流中重复。
|
||||
- 已结束且包含正文的助手回复在底部提供带可访问名称和工具提示的复制图标;复制 Markdown
|
||||
原文,不包含推理过程、工具日志或引用元数据,结果通过应用通知反馈。流式回复完成前不显示
|
||||
复制入口。
|
||||
原文,不包含推理过程、工具日志或引用元数据。复制成功后,当前按钮显示使用 `--success`
|
||||
的绿色对号,可访问名称和工具提示同步表示已复制,3 秒后恢复复制图标;重复复制成功重新
|
||||
计时,不显示成功通知。失败仍通过应用错误通知反馈。流式回复完成前不显示复制入口。
|
||||
- 默认开启“会话 HTML 渲染”时,已完成 Agent 回复中的完整 HTML 与 `html` / `htm`
|
||||
代码块在原位置显示隔离静态预览;用户消息、推理过程和流式中的回复不得执行 HTML。
|
||||
每个预览持续标注脚本和网络已禁用,并在标题右侧复用与 Mermaid 图表相同的图标按钮组:
|
||||
@@ -614,6 +654,9 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
|
||||
全屏预览遵循统一 Modal 的背景隔离、初始焦点、Tab 循环、Escape 关闭和焦点恢复规则,
|
||||
并沿用同一套清理与 Sandbox/CSP 边界。关闭平台功能后必须显示 HTML 源码,不能隐藏内容
|
||||
或保留旧预览。
|
||||
- Mermaid 大图支持缩放和拖动;图形超过画布时,四个边缘都必须能通过滚动到达,不能因居中而裁掉顶部或左侧。
|
||||
弹窗工具栏提供“导出 PNG”,保存完整图形,不受当前缩放和滚动位置影响;背景使用当前主题的画布底色。
|
||||
导出按原图四周留出 `16px`,以 2 倍分辨率绘制,任一边最多 `4096px`。导出期间禁用重复点击,失败在按钮旁提示重试。
|
||||
- 本地会话的更多操作提供“在新会话中继续”。创建前必须先持久化当前稳定消息;有活动运行
|
||||
或待发送内容时不可创建。新会话复制聊天内容与当前 Runtime/知识检索选择,重置压缩状态,
|
||||
使用新的会话和消息 ID,且不复制 Task、队列、运行、记忆或成果归属。
|
||||
@@ -641,6 +684,7 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
|
||||
- 输入框原生支持 `Ctrl+V`:文本直接进入草稿,系统截图和从文件管理器复制的一个或多个支持文件加入本次消息附件。文件粘贴与上传按钮共用读取、解析进度和数量限制;读取期间阻止重复导入及发送,错误就地关联输入框和附件按钮。具体类型、大小及批次失败规则见[聊天附件要求](./docs/features/document-processing/prd.md#321-聊天附件入口)。默认工具栏不提供独立“读取剪贴板”“截取当前屏幕”和“选择应用窗口”入口。
|
||||
- “Enter 发送 · Shift+Enter 换行 · Ctrl+V 粘贴文件、图片或文本”等输入操作提示放在空输入框内部,作为主占位文案的次级行;不得在输入框下方单独占用第二行。输入框下方只保留一行当前模式、安全边界或全局快捷键说明。
|
||||
- 输入操作提示不能替代表单的可访问名称,输入框始终保留持久的程序化标签。
|
||||
- 草稿与消息中的附件操作使用紧凑图标按钮,并提供悬停和键盘焦点提示;文件名、解析状态与操作行的布局遵循[附件操作规则](./docs/features/document-processing/ui-design.md#12-附件操作菜单)。
|
||||
- 空对话展示可执行的起始建议,发送失败保留输入并提供重试。
|
||||
- 消息流的滚动范围必须使用已渲染消息的真实高度。长会话可以按批次只挂载最近消息并由用户
|
||||
加载更早内容,但不得用固定的消息占位高度替代实际布局,避免推理、工具列表或长正文被
|
||||
@@ -669,12 +713,17 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
|
||||
|
||||
### 13.4 智能心跳
|
||||
|
||||
- 使用 `dashboard` 壳层。
|
||||
- 顶部先呈现运行状态、实际范围和主操作,再呈现指标、建议、历史与配置。
|
||||
- 监督者入口使用 `supervisor` 壳层;智能心跳设置位于同一壳层内。
|
||||
- 顶层页签为“工作回顾 / 故事线图谱 / 自动监督 / 活动记录 / 设置”,使用一套共享 PageTabs,窄窗口单行横向滚动。页签下不重复同名标题,也不保留空标题容器;所选回顾使用“监督结果”标题,历史结果不得标为最新。
|
||||
- 工作回顾只展示统一监督结果、历史选择和紧凑的新回顾控件;结果保留自身范围、覆盖区间和生成时间,图谱读取同一结果 ID。自动监督集中展示当前计划、运行概览、指标、趋势、自动监督报告、记忆与行动建议及心跳运行审计;活动记录只展示统一执行活动、进度和详情。每页保留所属数据的一处刷新入口,范围与时间选择仅作用于新回顾请求。详细布局与阅读规则见[监督者 UI 设计](docs/features/conversation-supervision/ui-design.md#应用入口)。
|
||||
- 监督者保留统一页面壳层;阅读与活动子面板分别复用 `--content-reading` 和 `--content-dashboard`,在壳层内左对齐。各面板的工具栏、历史导航、状态和结果共享宽度,不在全宽卡片内部再次居中窄正文。活动筛选按内容紧邻排列,阶段以文字状态行呈现。
|
||||
- 范围明确区分 Global 与指定的一个或多个 Project;Global 与指定项目互斥,多项目选择使用
|
||||
Checkbox,不能依靠进入页面时的当前项目推断。
|
||||
- 状态卡片使用统一状态令牌,不只依赖颜色。
|
||||
- 保留“运行概览 / 待处理建议 / 报告与记录 / 心跳计划”四个同级页面。
|
||||
- 活动记录先显示运行状态、时间、范围及实际阶段,恢复操作紧邻进度。原始错误默认折叠,展开后在有界面板内滚动并可选择复制;配置归入次要详情。任务层级只展示可查询的记录,缺失阶段明确标注,不能用剩余来源为零推断发布成功。具体阶段与分页规则见[监督者活动设计](docs/features/conversation-supervision/ui-design.md#活动)。
|
||||
- 自动监督的当前状态下显示计划列表,仅保留列表的一个创建按钮;无计划时使用列表的未配置说明,不叠加第二个空态或创建入口。创建与编辑复用同一份表单及共享 Modal 焦点、背景隔离和遮罩样式;未保存关闭须确认,保存失败保留草稿,编辑暂停计划不恢复运行。“执行记录”进入按该计划 ID 筛选的活动页,活动页不重复显示心跳审计列表。
|
||||
- 设置只展示模型超时与并发、分页、分批和执行预算,不含计划、报告、审计或嵌套页签,不显示整页全局徽标或独立刷新行。
|
||||
- 监督者的设置、帮助、空态及错误文案不得向终端用户展示内部路线图、交付进度或设计与实现缺项;只说明当前行为、适用范围和可执行的下一步。不得因此夸大为提供商全局限流或完整语义识别;真实的运行失败、超时、暂停、来源失效及恢复操作必须保留。
|
||||
- 智能心跳菜单入口是完整配置的权威位置;任务中心和设置中心不得复制同一 CRUD 表单。
|
||||
- 分区记忆协同与唤起条件尚未实现,不得显示占位入口或平行的记忆类型。
|
||||
|
||||
@@ -682,14 +731,19 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
|
||||
|
||||
- 保留现有助手工作栏入口,首期在窄栏内适度完善,不先扩张成新的独立一级页面。
|
||||
- 只展示 Task;普通 Conversation、Job、Run、工具步骤、Subagent 和心跳事项不独立占行。
|
||||
- 每项显示名称、关联 Conversation、Global 或 Project 范围、Ask/Execute、状态、最近进展、
|
||||
最近真实活动时间及需要关注信息。
|
||||
- 范围固定为当前项目,不提供全局或所有项目入口;切换会话不筛掉本项目其他会话的任务。
|
||||
- 顶部紧凑显示当前会话消息总数和 Agent 累计回复时间,按每轮请求区间累加,包含轮内工具和
|
||||
审批等待,不计轮间空闲。历史缺失显示不完整提示,远程缺少可靠证据时不虚构用时。
|
||||
- 每项显示名称、关联 Conversation、所属项目、Ask/Execute、状态、最近进展、
|
||||
最近真实活动时间、累计执行时间及需要关注信息。任务用时汇总历次执行请求,不按父任务生命周期计算。
|
||||
- 点击列表项打开关联 Conversation 并定位该 Task,不显示第二份内容载体。
|
||||
- 需要关注、进行中、已暂停和已结束使用共享 `SegmentedControl`;窄栏不足时单行滚动。
|
||||
- 项目任务标题右侧依次为“+”新建和筛选图标;图标按钮提供可访问名称,筛选按钮提供展开状态。
|
||||
状态筛选默认收起且为全部;展开后用共享 `SegmentedControl` 提供全部、进行中、暂停、已结束,
|
||||
窄栏不足时单行滚动。收起保留条件,非全部时图标高亮并显示条件。状态归类遵循[任务中心 PRD](docs/features/task-and-job/task-center-prd.md#5-交互)。
|
||||
- 完整消息留在 Conversation;长错误、工具、审批和成果按 Task 关联到 Runtime、活动记录和
|
||||
成果查看器,不撑高列表,也不显示 Job/Run 树。
|
||||
- “新建定制任务”使用共享 Modal,明确选择当前或新 Conversation。聊天入口默认当前
|
||||
Conversation,Task Center 入口默认新 Conversation;选择必须持续可见。
|
||||
- “新建定制任务”使用共享 Modal,默认立即执行和当前 Conversation,可选择本项目其他可用
|
||||
Conversation 或新建会话,选择必须持续可见。内容必填,名称可选并从内容生成;定时提供单次、每日和每周。
|
||||
- 定时消息创建 Modal 使用关联 Conversation 的 Runtime 与 Ask/Execute 模式,不再提供独立
|
||||
模式开关。未运行计划的详情显示会话当前模式,已运行任务显示该次执行记录的模式;
|
||||
不得用旧计划字段表示下一次执行权限。不支持工具时不得静默降级。
|
||||
@@ -700,34 +754,52 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
|
||||
- 默认视图按“项目 → 任务或会话 → 活动详情”组织,项目范围持续可见,任务或会话详情可以折叠。
|
||||
- 任务或会话的综合状态以最近一次顶层请求对应的最终 Agent 结果为准;最终结果尚未产生时使用该请求的当前状态。中间工具或子专家的失败、取消和中断保留在活动详情中,但不得覆盖最终成功状态。
|
||||
- 活动时间线按项目分组、按任务或会话建立横向轨道,所有轨道共享同一执行顺序并按事件时间排列,以带身份名称的节点表达用户、主 Agent、子专家、工具、审批、状态和并行关系。节点内使用 `U / G / S / T / A` 拉丁字母简称,节点下显示完整身份或名称;选择节点后显示所属会话、不可变范围快照和完整详情。
|
||||
- 用量统计与活动记录分离,支持按项目、会话和模型切换统计维度,宽表格在独立容器内横向滚动。
|
||||
- 用量统计与活动记录分离,支持按项目、会话和模型切换统计维度;按项目或会话分组时默认收起并显示汇总,展开后显示 Runtime / 模型子行,按模型分组时保持平铺。宽表格在独立容器内横向滚动。
|
||||
- 活动状态筛选使用 `SegmentedControl`,不与页面页签混合。清空历史遵循破坏性操作政策。
|
||||
|
||||
### 13.7 魔法笔记
|
||||
|
||||
- “笔记 / 待办”属于同一工作台内的同级内容面板,使用 `PageTabs` 的 `segmented` 视觉变体,与模型设置的分段控件保持同一外观。
|
||||
- 页签切换保留 `tablist`、`tab` 和 `tabpanel` 语义;待办状态仍使用独立的 `SegmentedControl`,不得与内容页签合并。
|
||||
- 笔记编辑器保留 Quill 原生列表快捷输入:输入 `-`、`*`、`1.`、`[ ]` 或 `[x]` 后按空格,分别转换为项目符号、编号或待办清单。快捷输入只生成现有 Delta 富文本,不引入 Markdown 源码模式、第二套持久化格式或第三方 Markdown 插件;编辑区附近持续提供简短提示。
|
||||
- 左侧笔记与待办列表在双栏布局中允许在 `180px` 至 `360px` 范围内通过指针或键盘调整,
|
||||
也可从页面标题区隐藏和恢复;宽度与显示状态均记住用户选择。分隔条保留 `separator`
|
||||
语义及方向键、Home、End 操作,窄窗口堆叠布局不显示拖拽分隔条。
|
||||
- 左侧笔记列表依次显示标题、最多两行内容预览,以及同一行的记录数和本地化更新时间。记录数靠左、更新时间靠右,窄宽度下均保持单行省略,不挤压标题或扩大列表栏。
|
||||
- 笔记总览与待办使用一个普通按钮切换,放在页头“新建笔记”按钮左边。按钮显示目标“切换到待办”或“切换到笔记”及对应图标,按内容宽度显示,不拉伸;窄窗口操作组可换行到标题下方,保留控件高度。
|
||||
- 切换按钮不使用页签语义,切换后焦点回到同一个按钮;内容 section 持续提供当前列表的 `aria-label`。切换沿用草稿保护入口,待办状态仍使用独立的 `SegmentedControl`。
|
||||
- 文字记录编辑器保留 Quill 原生列表快捷输入:输入 `-`、`*`、`1.`、`[ ]` 或 `[x]` 后按空格,分别转换为项目符号、编号或待办清单。快捷输入只生成现有 Delta 富文本,不引入 Markdown 源码模式、第二套文字记录持久化格式或第三方 Markdown 插件;新建文字记录的操作提示收进类型选择旁的 `InlineHelp`。
|
||||
- 新记录可选择“文字 / 画布”;已保存画布提供分页阅读和“展开编辑画布”。画布使用 PeopleLib 的 Fabric 分页批注与 Quill 跨页正文,支持纸张模板、增删页、笔迹、高亮、整对象橡皮、选择及移动缩放旋转、浮动文字、图片和 PDF 底版。画布有固定页面边界;已保存记录、待办来源和编辑态默认容纳完整当前页及工具栏,高度随当前页实际显示尺寸变化,纵向由外层笔记流滚动。多页仍属于一条记录,通过页码翻页;窄窗口工具栏换行,纸张仅在自身视口内横向滚动,不撑宽外层页面。
|
||||
- 画布通过“保存记录 / 保存修改”手动持久化,离开或切换记录类型前沿用未保存草稿确认。撤销/重做作用于当前批注页或 Quill 正文模式,增删页和 PDF 导入不属于全局撤销历史。删除非空批注页或 PDF 页需要在五秒内再次点击确认。
|
||||
- 画布编辑与只读工具栏均提供缩小、当前百分比、放大和“适应宽度”。默认 100%,范围 25%–300%;加减每次调整 25 个百分点,点击百分比还原 100%。画布区域内 Ctrl/Command + 滚轮连续缩放,普通滚轮和 Shift 横滚保持原有方向与边界传递。适应宽度按实际视口计算,窗口或分栏调整后自动更新;翻页保留固定比例或适应宽度策略。缩放仅影响显示,整页高度与横向滚动范围随之变化,不新增内层纵向滚动,也不改变正文分页、保存内容、导出尺寸或未保存状态。控件沿用画布的主题、焦点环与提示,窄屏整组换行,适应宽度通过 `aria-pressed` 和选中样式表达状态。
|
||||
- “导入 PDF”保留页面并提取原生文字供搜索与 AI 使用,扫描页不自动 OCR。“导出 PDF”将底版、正文和批注逐页合成为栅格 PDF,不保留可搜索文字层;当前 UI 无 PNG 下载按钮,页面 PNG 捕获仅作为程序接口提供。
|
||||
- 画布新建与编辑态不显示常驻分析提示。操作放在编辑区右上角:新建与类型选择同排,顺序为“分析画布草稿、保存记录”;编辑放在记录头部,顺序为“分析画布草稿、取消、保存修改”。保存为最右侧主按钮,窄屏允许换行;底部不重复操作,新建不增加取消按钮。`immediate` 仍通过按钮手动分析,不随笔画自动请求;`after-save-auto` 在手动保存后分析。设置读取或页面捕获失败不阻断保存,保存成功后通过应用通知说明自动分析失败,可稍后重新分析;模型分析失败也不回滚已保存内容。
|
||||
- 魔法笔记设置提供“发送画布页数”单选框,复用设置表单 `.field` 控件样式,范围 1~8,默认 1。标题同行的问号使用 `InlineHelp`,下拉框通过稳定的帮助 ID 关联展开的说明,交互遵循 6.14。说明包含当前页序前 N 页、不足时发送全部页、数量指单个画布页数而非记录数,以及完整保存与导出不受影响。修改后沿用设置保存反馈,下次分析生效。
|
||||
- 草稿、已保存画布记录及来源为画布的待办,按默认模型的 `supportsImageInput` 使用所选前 N 页图像与文字,或只发送同一范围文字并标注“仅文字分析(未使用画布图像)”。所选页只有视觉内容的记录/草稿遇到文字模型时提示切换支持图像输入的默认模型,不声称已理解笔迹或布局。待办分析同时保留任务标题、说明和所选来源画布页的上下文。
|
||||
- 画布文字评论在提取的 `plainText` 不变时保留;图像评论随画布内容或布局变化失效,在保存后自动评论模式下重新分析。草稿内容变化清除旧草稿分析,评论区显示实际使用的输入模式。存储和判定细节见[画布技术设计](./docs/features/magic-notes/technical-design.md#canvas-analysis-and-comments)。
|
||||
- 默认进入笔记总览,不自动打开第一篇笔记。笔记以自适应卡片网格平铺,卡片显示标题、最多两行内容预览、记录数和本地化更新时间。点击笔记进入详情,详情不保留内部左列表;“返回总览”恢复搜索、滚动位置和入口焦点。新建入口在笔记总览与待办页可见,创建成功后进入笔记详情。
|
||||
- 待办页是独立任务视图:搜索任务标题、说明或来源笔记,搜索框右侧同一工具栏放置“全部 / 未完成 / 已完成”筛选,显示匹配数与总数;任务按来源笔记分组。列表独立滚动,页头、搜索和筛选保持可见,窄屏工具栏空间不足时换行。直接勾选完成或恢复未完成,不自动选择任务;状态变化后按当前筛选更新列表。
|
||||
- 待办内容容器宽于 `700px` 时,任务列表稳定在左侧,右侧显示当前任务说明、来源记录和 AI 评论;未选择时右侧提示选择任务。点击任务只更新右侧,不改变列表宽度、行位置或滚动位置,再次点击取消选择。AI 不增加第三栏。容器不超过 `700px` 时,按需详情替换同一内容区域,并提供“返回待办列表”;列表保持挂载,返回恢复滚动和任务行焦点,不将列表与详情上下堆叠。任务按钮提供 `aria-expanded` 和 `aria-controls`。
|
||||
- 待办左右栏分割线支持拖拽,并记住列表宽度;键盘左右方向键每次调整 `16px`,Home/End 调整至边界。列表默认 `320px`、最小 `240px`,详情至少保留 `300px`;窄屏隐藏分割线,临时缩窄窗口不覆盖已保存宽度。每个来源笔记标题可独立收起或展开待办列表,保留标题与数量,通过 `aria-expanded` 和 `aria-controls` 表明状态;收起不清除右侧当前详情。
|
||||
- 待办 AI 评论保留方向选择、流式输出、重新分析和已有结果;来源加载失败通过应用通知提示,并提供就地重试。从待办打开原笔记后,“返回待办”恢复任务选择、搜索、筛选和列表滚动;宽屏焦点返回任务行,窄屏返回待办详情中的返回按钮。
|
||||
- 笔记详情主区按新到旧连续展示全部记录。打开笔记即在标题下、历史记录上显示空白输入区,保留文字/画布类型选择和保存操作,标题栏与索引均不提供“新增记录”按钮。新增保存成功后重建空白输入区,保留所选类型;自动分析结束也不转入旧记录编辑。旧记录通过显式编辑入口修改,保存使用同一 ID 和返回的修订号。点击索引或当前笔记内的 AI 来源只按 entry ID 滚动定位,不卸载编辑器、不清草稿、不询问离开。空白输入区离开不提示,返回总览、打开其他笔记或改为编辑另一条记录时仍保护未保存修改。
|
||||
- 详情页的一级标题显示当前笔记已保存的标题,不显示模块说明;全局范围、返回导航和索引开关保持可见。原标题输入框使用正文大小,保留原有编辑、失焦保存与键盘操作,不形成第二个醒目标题。新建文字记录的输入区初始约四行,随内容自然增高,保留原有调整大小与滚动方式;此高度不作用于旧记录编辑器或画布。笔记内部列表、记录流、AI 区域和文字编辑区在精细指针设备上使用细原生滚动条,轨道透明;强制颜色与粗指针设备保留系统默认。说明、摘要和输入提示使用 `--font-body` 与 `--text-secondary`,时间和短标签保留 `--font-caption`。
|
||||
- 左侧记录索引默认 `168px`,可拖动右边界在 `140–320px` 内调整栏宽,缩略图随列宽等比适配。开关独立位于详情标题左侧,提供 `aria-controls`、展开状态和动作提示;收起后完全移除索引列、分隔条、间隙、边框及轨道,不占宽度,隐藏内容不可聚焦。右侧 AI 评论独立显示、滚动和收起,不与索引共享页签。每条记录一个导航项:画布显示真实首页缩略图及总页数,文字显示摘要,两者均显示类型和时间。两侧分别记住桌面展开状态和宽度,共用原有布局偏好;宽布局分隔条共用样式及 `separator` 语义,方向键每次调整 `16px`,Home、End 跳到当前上下限。两侧宽度按可用空间协调限制,为主区保留至少 `300px`,窗口缩小时不覆盖保存的偏好。内容容器不超过 `800px` 时索引默认隐藏,由同一标题栏按钮开闭标题栏下方固定 `168px` 的抽屉,不显示调宽分隔条;标题栏自身保留控件高度,不保留轨道。选择记录或按 Escape 后关闭,Escape 将焦点返回开关。AI 移至底部,高度不超过可用布局的 40% 且最多 `280px`,不显示分隔条。
|
||||
AI 评论的唯一显隐入口位于页面右上角,使用紧凑面板图标按钮;展开时应用共享选中样式,图标随显示或隐藏动作切换,并提供对应的工具提示、可访问名称、`aria-expanded` 和 `aria-controls`。评论面板内部不再显示重复的“AI 评论”标题或隐藏按钮行,保留区域的可访问名称、评论方向选择及各记录的分析操作。
|
||||
- 笔记详情的内部记录索引使用透明底色,与主内容表面连续,仅保留细调宽分隔线。每条索引记录保留 `1px` 的 `--border-subtle` 淡边框,不加卡片底色或阴影,悬停使用轻微次级底色;选中记录使用淡蓝背景和左侧 `3px` 蓝色内侧标记,悬停时保留选中态。缩略图保留纸张边界,键盘焦点环与强制颜色模式选中边框保持可见。窄屏抽屉使用不透明的主内容表面,避免下层正文透出;深色主题沿用语义令牌。
|
||||
- 笔记详情的标题、正文和记录流不再包裹额外的大外框,记录流容器不加内边距;缩略图列表顶部与正文区顶部对齐,左边界与索引栏左边界对齐,仅在右侧和底部保留 `8px` 留白,键盘焦点环向内绘制以避免滚动容器裁切。每条已保存记录使用一层 `1px` 的 `--border-subtle` 淡边框和 `--radius-control` 圆角,保留透明底色与记录间留白,不加阴影或头部下划线。记录编辑时保留输入区边框、页面安全边距及 AI 区域分隔。此规则仅作用于笔记详情,不改变卡片总览与待办独立布局。
|
||||
- 画布新建、编辑和只读态统一保留 `12px` 内容间距;详情中已保存画布去掉预览外框和工具栏底色、下划线,保留灰色画布衬底与白色纸张,编辑态保留编辑器边框。记录头部操作按内容宽度排列,空间不足时自然换行。文字新建的保存区与编辑器属于同一卡片,空白编辑区使用最小高度,不随容器百分比高度拉长。文字工具栏图标、菜单和原生滚动条须跟随主题。
|
||||
- 整篇笔记的置顶、取消置顶和删除放在卡片的“更多”菜单,标题编辑区不重复提供入口。按钮在悬停、键盘聚焦或菜单展开时显示,触屏设备常显;菜单沿用会话列表样式,通过 Portal 避免滚动区域裁切,支持方向键、Home、End 和 Escape。删除在菜单内二次确认并说明笔记标题及全部记录;打开菜单不进入详情。
|
||||
- 左侧“魔法笔记”入口可显示未完成待办数量。数量为 0 时隐藏徽标,超过 99 时显示 `99+`,可访问名称仍说明精确数量;勾选、取消勾选以及 Agent / MCP 对笔记清单的写入都必须刷新数量。
|
||||
- 未完成待办徽标的高度不得超过主导航图标;数量从 0 变为可见或再次隐藏时,主导航项行高和相邻菜单位置必须保持不变。
|
||||
- 在待办面板勾选或取消勾选后,当前缓存中的源笔记正文必须立即同步对应清单状态,不得依赖离开工作区后重新加载。
|
||||
- Agent / MCP 或其他入口成功写入笔记后,已打开的工作台自动更新笔记列表、当前详情、待办及来源记录,不增加刷新按钮。保留仍存在的笔记与待办选择、搜索和筛选,以及未保存的标题、输入区和记录编辑草稿;普通后台刷新不重建编辑器。
|
||||
- 外部删除当前笔记时,无草稿则选择剩余笔记;有草稿则保留编辑区并提示先复制草稿。外部删除正在编辑的记录时,同样保留该编辑器并明确提示记录已删除;保存失败不得清空草稿。
|
||||
- 当前记录草稿非空时,切换笔记、待办或内容面板必须先在编辑器旁就地确认;继续编辑时保留草稿并恢复编辑焦点,只有明确选择放弃后才切换。
|
||||
- 外部删除当前笔记时,无草稿则返回总览;有草稿则保留编辑区并提示先复制草稿。外部删除正在编辑的记录时,同样保留该编辑器并明确提示记录已删除;保存失败不得清空草稿。
|
||||
- 当前记录草稿非空或标题尚未保存时,返回总览必须先就地确认;编辑另一条记录等会丢弃正文草稿的操作沿用同一保护。继续编辑时保留草稿并恢复编辑焦点,只有明确选择放弃后才切换。
|
||||
- 保存成功后,以该修订的编辑器初始化并完成 flush 后的内容作为修改比较基线;没有新编辑时返回不再提示。编辑器归一化不算用户修改,后续输入、画布变更和保存失败仍保留离开保护;同修订的后台刷新不替换已建立的基线。
|
||||
- 创建、保存、更新、删除和 AI 评论完成等短期结果进入应用级通知,不在编辑区或列表上方堆放页内通知。
|
||||
- 标题或正文校验、删除确认、同步进度和可就地恢复的错误仍靠近对应编辑器或操作呈现。
|
||||
|
||||
### 13.8 设置中心
|
||||
|
||||
- 全页设置使用固定标题区、左侧分类导航和独立滚动的内容区。右上角关闭按钮是离开设置中心的稳定入口。
|
||||
- 设置中心以整窗遮罩上的大 Modal 打开,保留底层工作区、会话草稿和助手工作栏,不切换一级页面。固定标题区和右上角关闭按钮,左侧分类与右侧内容各自滚动。关闭后返回原工作区并恢复入口焦点。
|
||||
- 新建项目与项目设置弹窗固定标题、关闭按钮和底部操作区,仅表单内容滚动;滚动条不进入标题区,关闭按钮保留至少 40 × 40px 的点击区域。
|
||||
- 项目弹窗通过 Portal 挂载到 `document.body`,避免侧栏内的弹窗与主窗口标题栏拖动区域重叠时,Electron 截获关闭按钮的鼠标输入。
|
||||
- 左侧分类导航在宽屏使用 `220px`,中等窗口使用 `196px`,窄窗口转为横向滚动;纵向滚动条仅在内容溢出时占用右侧空间,不在左侧创建镜像预留,选项与左侧可见边界保持默认内距。分类标题使用正文级字号,分类说明使用辅助字号;右侧内容区在可用空间内流式伸缩,最大宽度使用 `standard` 壳层的 `960px`,不得以页面专属较窄宽度压缩表单。
|
||||
- 全页设置标题区与双栏内容使用共享 `--page-gutter`,标题、分类导航和内容区在同一页面边距基线上;标题继续使用标准 `PageHeader` 的底部分隔线与下内距。固定标题和双独立滚动区域不改变这些一级页面壳层规则;模态设置保留自身外框与标题边界。
|
||||
- Modal 最大尺寸为 `1200px × 900px`,常规外边距为 `24px`;窗口宽度不超过 `1020px` 或高度不超过 `720px` 时接近全屏,保留 `16px` 外边距。左侧分类宽度依次为 `220px`、`180px`(宽度不超过 `1020px`)、`120px`(不超过 `720px`);极窄窗口隐藏分类辅助说明,仍保留左右布局。内容区在可用空间内伸缩,最大宽度为 `960px`。
|
||||
- 设置 Modal 通过 Portal 挂载到 `document.body`,复用共享背景隔离与焦点恢复机制。打开后聚焦关闭按钮,Tab 在弹窗内循环;Escape 先交由嵌套弹窗处理,设置自身的 Escape 与关闭按钮共用未保存确认,确认出现后再按 Escape 返回编辑。点击遮罩不关闭;保存、测试和清理数据期间禁止关闭。每次打开重新挂载设置,模型和消息通道等直达入口使用本次指定分类,普通入口默认打开 Agent Runtime。
|
||||
- 设置中心不显示全局操作页脚,避免重复关闭入口和没有功能意义的整宽分隔线。
|
||||
- 所有分类使用共享的 `SettingsCategoryHeader` 呈现分类标题、说明、错误与操作,不得在内容卡片内复制分类标题或创建页面专属操作栏。左侧分类名称与说明来自同一份分类定义,新增分类时不得分别维护导航和内容标题。
|
||||
- 当前分类存在“保存”或“测试”等未提交配置操作时,统一放在分类页头右侧;主保存操作在最右侧,测试等次操作排列在其左侧。
|
||||
@@ -741,19 +813,24 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
|
||||
- “平台功能 / 通用设置”同时提供默认开启的“在会话中渲染 HTML”持久 Switch。说明必须
|
||||
明确该能力只生成禁用脚本、联网、表单提交和窗口操作的静态预览;保存成功后当前会话立即
|
||||
更新,保存失败时保留最后确认状态。
|
||||
- “平台功能”使用共享 `PageTabs` 区分“通用设置”“远程项目(技术预览)”和“魔法笔记”,默认进入通用设置。“远程项目”页将持久 Switch 与用户数据目录中的 Linux x64、Linux arm64、macOS arm64 Agent 包清单分成独立卡片;每张卡片与下载进度按平台和架构区分,不能把两个 arm64 目标合并。清单延迟到首次打开该页签时由 Main 完整校验,并读取所选更新源的小型签名目录,显示本地版本、在线最新兼容版本以及“有更新”“已是最新”“未下载”“校验失败”。打开页签或刷新不得自动下载 `.gbagent`;只有用户点击带目标版本的下载/更新按钮后才传输包。签名目录不可用时继续显示并允许导出已验证本地包,提供就地重试错误;清单不混入 Host 已安装状态、缓存路径或敏感签名细节。下载和导入期间保留上下文进度,成功进入全局通知视口。“魔法笔记”页提供默认开启的“显示未完成待办数量”持久 Switch,关闭后只隐藏左侧数量徽标,不改变待办数据或魔法笔记入口。全局模型下载源使用 `fieldset`、持久 `legend` 与整行可点击的原生 Radio 卡片;选中状态同时依靠 Radio、边框和背景表达,读取失败时不得用默认值伪装为已保存选择。
|
||||
- “关于与更新”的更新源位于“启动时检查新版本”开关下方,常规宽度下将标签、原生单选下拉框和用途说明放在同一行,并复用设置表单的统一控件样式;关闭启动检查后仍可选择更新源,手动检查继续使用当前选择。选项显示“GitHub(默认)”和中性的“镜像节点”。该选择同时控制手动检查、启动时检查、下载页和 Agent 包在线下载,不显示底层服务商名称。
|
||||
- “平台功能”使用共享 `PageTabs` 区分“通用设置”和“远程项目”,默认进入通用设置。“远程项目”页将持久 Switch 与用户数据目录中的 Linux x64、Linux arm64、macOS arm64 Agent 包清单分成独立卡片;每张卡片与下载进度按平台和架构区分,不能把两个 arm64 目标合并。清单延迟到首次打开该页签时由 Main 完整校验,并读取所选更新源的小型签名目录,显示本地版本、在线最新兼容版本以及“有更新”“已是最新”“未下载”“校验失败”。打开页签或刷新不得自动下载 `.gbagent`;只有用户点击带目标版本的下载/更新按钮后才传输包。签名目录不可用时继续显示并允许导出已验证本地包,提供就地重试错误;清单不混入 Host 已安装状态、缓存路径或敏感签名细节。下载和导入期间保留上下文进度,成功进入全局通知视口。全局模型下载源使用 `fieldset`、持久 `legend` 与整行可点击的原生 Radio 卡片;选中状态同时依靠 Radio、边框和背景表达,读取失败时不得用默认值伪装为已保存选择。
|
||||
- “关于与更新”的更新源位于“启动时检查新版本”开关下方,常规宽度下将标签、同行帮助和原生单选下拉框放在同一行,用途说明收进 `InlineHelp`,并复用设置表单的统一控件样式;关闭启动检查后仍可选择更新源,手动检查继续使用当前选择。选项显示“GitHub(默认)”和中性的“镜像节点”。该选择同时控制手动检查、启动时检查、下载页和 Agent 包在线下载,不显示底层服务商名称。
|
||||
- “关于与更新”在版本信息之后提供应用内反馈卡片。反馈对话框使用原生字段、明确隐私说明和一张可选截图;另提供默认关闭的原生 Checkbox,让用户主动附加最近桌面诊断摘要。Checkbox 附近必须持续说明摘要只含时间、组件、阶段、稳定错误码、错误类型和固定短消息,不含对话、Prompt、凭据、文件内容、路径、Provider 原始响应或远端 Agent 日志。勾选时为诊断摘要预留描述字符预算,超长草稿必须保留并显示字段错误,不得静默截断。失败时保留草稿、勾选状态和请求编号并提供就地重试,成功时在对话框内保留可复制编号而不重复发送全局通知;对话框遵循统一 Modal 的背景隔离、初始焦点、Tab 循环、Escape、焦点恢复和窄窗口布局。
|
||||
- Agent Runtime 分类页头的“保存设置”同时保存 Runtime 基础配置与 Runtime 原生定制,不在原生定制卡片内提供第二个保存入口。原生定制存在未保存更改时持续显示状态和撤销入口;切换设置分类或 Runtime 不丢弃草稿,关闭设置中心前必须先保存或撤销。
|
||||
- Agent Runtime 页面在低层程序与配置覆盖之外提供“能力与默认配置”区域。能力清单使用共享 `PageTabs`,按 Agents、Tools、Commands、Skills、MCP、Rules、Prompts、Resources、LSP、Formatters 和上下文 11 类单行滚动展示,一次只呈现当前分类的 `tabpanel`;清单只显示 Runtime 自有能力,不混入 GoodBuddy 分配的 Skills、临时 MCP 或 Continue 预设。Tools 必须独立于 Commands、LSP 和 Formatters,显示工具类型、来源及 Ask/Execute 可用性;清单状态必须区分完整、部分、不可用、仅连接和不支持,不能用进程连通性冒充清单可读。
|
||||
- “能力与默认配置”只显示一个模块标题,刷新入口位于该标题右侧,能力状态压缩为一行并排在默认 Agent 或 Continue 预设编辑器之前;不得再复制“Runtime 原生能力”等同义标题、说明或状态结论。刷新只更新能力快照,不覆盖未保存的原生定制草稿。
|
||||
- OpenCode 的默认 Agent 使用原生下拉选择;Continue 预设编辑器允许管理名称、说明、启用的 Rules 以及 Prompt 名称、说明和正文,并可展开查看原生 Rules 与启用预设 Rules 的最终合并顺序。持久启停仍使用共享 Switch,添加与删除使用明确按钮和可访问名称。
|
||||
- 魔法笔记设置统一从应用中心对应行进入;笔记页和全局设置不再提供重复入口。“显示未完成待办数量”持久 Switch 只控制左侧数量徽标,不改变待办数据。旧入口开关绑定业务启用值,迁为“启用应用”并保留原值,常驻使用独立配置。默认开启仅适用于未配置布尔值,只有独立显隐偏好才映射到常驻。此迁移规则见[应用导航 PRD](./docs/features/application-tool-navigation/prd.md#fr-11-应用导航设置)。
|
||||
- MCP 设置按“内置 MCP / 直连模型 / 自定义 MCP / 电脑控制”四个同级 `PageTabs` 组织。联网搜索保留在直连模型;内置浏览器归入内置 MCP,提供持久启停和 Runtime 分配,并明确说明其操作 GoodBuddy 隔离浏览器、不控制客户端已安装的浏览器,开启后可由已分配 Runtime 的 Execute 直接使用,不逐次询问。该开关只控制 Agent 工具访问,关闭时不得禁用浏览器工作栏中的用户手动导航、返回、刷新、停止加载、交互或关闭操作。尚未生效的命名浏览器配置不得显示在界面中,“电脑控制”只显示实际操作客户端电脑的能力。内置 MCP 卡片与 Skills 一样提供持久启停和 Runtime 分配;直连模型、GoodBuddy 管理的 OpenCode 与 Continue 默认选中且可调整,DeepSeek Harness 必须以置灰、未选择和“暂不支持”文案持续显示,不能呈现为可保存的分配。魔法笔记 MCP 的自身启停与平台功能依赖分别显示,依赖未开启时保留用户配置并说明当前不会加载。
|
||||
- MCP Server 测试结果在同一展开卡片中分组显示 Tools、Prompts 和 Resources 的支持状态、数量与有界元数据;Prompt 参数标明必填项,Resource 只显示 URI、名称、类型和说明,不读取或渲染 Resource 内容。
|
||||
- 内置 MCP 中的“图片生成与编辑”是自动管理例外:该行始终可见,标记“内置 · 自动管理”,不提供独立启停、分配保存、移除或凭据编辑。直连 Model、OpenCode、Continue、DeepSeek Harness 的分配只由已保存图片模型的“允许 AI 在会话中调用”设置推导;至少一个开启则全部勾选,没有开启模型则全部取消。使用禁用的原生 Checkbox、Runtime 标签及关联说明,保留可聚焦的“前往图片模型设置”导航;远程适用范围通过 GoodBuddy Agent 说明持续可见。Ask、离线、无工具能力或调用错误不改变勾选。图片模型默认关闭会话调用;默认生图模型独立于普通默认连接,关闭或删除后保留失效选择并提示改选,不自动回退。
|
||||
|
||||
### 13.9 文档解析设置
|
||||
|
||||
现有本地解析与本轮 HTTP 扩展的实现边界见[文档处理进度](./docs/features/document-processing/progress.md)。
|
||||
跨场景入口、附件菜单、共享预览及选图返回规则由[文档处理 UI 设计](./docs/features/document-processing/ui-design.md)维护;
|
||||
当前接线与存储行为见[生产实现说明](./docs/features/document-processing/implementation.md)。文档解析属于全局设置,不新增应用中心成员;本机推理监控只管理本地服务。
|
||||
|
||||
- 设置中心新增独立的“文档解析”分类,统一管理聊天附件、知识库导入以及后续文档审阅场景使用的提取、转换和 OCR 策略。OCR 不作为普通对话模型出现在“模型连接”中。
|
||||
- 分类页头说明文档解析的跨场景作用,右侧依次显示“测试解析”和“保存设置”;保存位于最右侧。测试必须选择真实文件并执行实际解析,不能只检查模型文件或接口连通性。
|
||||
- 页面首先显示原生解析、文档转换和 OCR 的运行状态,并明确当前可处理格式、回退能力与不可用原因。部分能力未配置时使用“部分可用”状态,不得把原生文本解析一并标记为失败。
|
||||
@@ -767,9 +844,9 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
|
||||
- PDF 先读取文本层。仅当页面无有效文本、乱码比例过高或用户选择“始终 OCR”时渲染该页并识别;不得因为单页需要 OCR 而丢弃其他页面已经提取的可靠文本。
|
||||
- DOCX、XLSX、PPTX 优先保留段落、单元格、公式、备注等原生语义。转换为 PDF 用于补充版面、页码、图表和图片理解,不作为唯一中间格式。
|
||||
- DOC、XLS、PPT 等旧格式通过受控转换 Provider 生成新式 Office 文档和 PDF。转换子进程必须禁用宏和网络,限制输入、输出、内存、超时与临时目录,并在关闭或取消时清理。
|
||||
- OCR 来源使用“本地模型 / 远程服务”互斥选择。选择本地后显示模型下载、模型下拉选择和本地运行参数;选择远程后显示 MinerU、PaddleOCR-VL 等服务连接配置。未实现的远程服务入口保持可读但禁用,不再增加与来源选择重复的“隐私与云端处理”授权区。
|
||||
- OCR 来源使用“本地模型 / 远程服务”互斥选择,远程为 HTTP PaddleOCR-VL;MinerU 不在本轮范围。来源切换保留两套配置,未保存时禁止测试。来源配置、保存和测试规则见[文档解析设置设计](./docs/features/document-processing/ui-design.md#2-文档解析设置),不另加逐文件授权区。
|
||||
- 用户配置并保存远程 OCR 服务即表示选择该处理路径,不再逐场景重复询问。界面仍须明确显示当前服务名称、处理范围和远程属性,API 密钥只保存在主进程加密设置中,未选中远程服务时不得上传文档。
|
||||
- 解析结果使用统一文档结构,至少保留文档标题、来源格式、页码或工作表定位、正文块、置信度、处理方式和警告。聊天附件保留完整解析文本,超过暂存容量时报错;知识库使用完整结果分块和索引,具体要求见[文档处理 PRD](./docs/features/document-processing/prd.md#32-场景工作流)。
|
||||
- 解析结果使用统一文档结构,保留文档标题、来源格式、页码或工作表定位、正文块、处理方式和警告;置信度仅在解析器提供时显示,缺失不补造。聊天文档附件保留完整解析文本,超过暂存容量时报错;知识库使用完整结果分块和索引,具体要求见[文档处理 PRD](./docs/features/document-processing/prd.md#32-场景工作流)。普通图片默认走图片输入,显式 OCR、原图保存和文档选图遵循该功能的规划规则。
|
||||
- 测试结果显示文件类型、页数、实际工作流、提取字数、OCR 页数、耗时和警告。测试文件不得自动进入聊天上下文或知识库。
|
||||
|
||||
## 14. 文案规则
|
||||
@@ -819,7 +896,7 @@ GoodBuddy 是可调整窗口大小的桌面应用。响应式设计优先保证
|
||||
- [ ] 将输入快捷键与附件提示置于空输入框内部,输入区下方保持单行说明。
|
||||
- [ ] 最近对话迁移到 `standard`,统一搜索、范围、时间和删除行为。
|
||||
- [ ] 知识库迁移到 `master-detail`,清除内联浅色样式并补齐窄窗口单面板流程。
|
||||
- [x] 智能心跳使用 `dashboard`,保留概览、建议、轨迹和计划,并在计划中支持 Global / 多 Project 范围。
|
||||
- [x] 监督者使用 `supervisor`,工作回顾读取统一监督结果;自动监督包含 Global / 多 Project 计划、概览、自动监督报告、建议与审计;活动记录显示统一执行,设置只保留算法参数。
|
||||
- [x] 在现有工作栏中完善任务中心,统一范围、状态、最近进展和筛选,不新建平行任务平台。
|
||||
- [ ] 活动记录迁移到 `dashboard`,统一导航、筛选和表格行为。
|
||||
- [ ] 设置中心使用共享分类定义与 `SettingsCategoryHeader`,将保存与测试操作统一放到分类页头右侧,并把成功反馈接入应用通知。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"agentVersion": "0.13.0",
|
||||
"agentVersion": "0.13.4",
|
||||
"protocol": {
|
||||
"major": 2,
|
||||
"minor": 0
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
const { createHash } = require('node:crypto')
|
||||
const { mkdirSync, readFileSync } = require('node:fs')
|
||||
const { join, resolve } = require('node:path')
|
||||
const { execFileSync } = require('node:child_process')
|
||||
const { readRemoteRuntimeLock, resolveLockedRuntimeInput } = require('./remote-runtime-bundle.cjs')
|
||||
|
||||
const [directory, architecture, platform = 'linux'] = process.argv.slice(2)
|
||||
if (!directory) throw new Error('Continue archive destination is required')
|
||||
const input = resolveLockedRuntimeInput(readRemoteRuntimeLock(), architecture, platform, 'continue')
|
||||
const destination = resolve(directory)
|
||||
mkdirSync(destination, { recursive: true })
|
||||
execFileSync('npm', ['pack', `${input.packageName}@${input.version}`, '--ignore-scripts',
|
||||
'--registry=https://registry.npmjs.org', '--pack-destination', destination], { stdio: 'ignore' })
|
||||
const archive = join(destination, input.archive)
|
||||
if (`sha512-${createHash('sha512').update(readFileSync(archive)).digest('base64')}` !== input.integrity) {
|
||||
throw new Error('Continue archive integrity mismatch')
|
||||
}
|
||||
process.stdout.write(`${archive}\n`)
|
||||
+22
-4
@@ -152,8 +152,8 @@ function assertPackageDescriptor(descriptor) {
|
||||
descriptor.files.length > 50_000 ||
|
||||
typeof descriptor.remoteRuntime !== 'object' ||
|
||||
descriptor.remoteRuntime === null ||
|
||||
descriptor.remoteRuntime.runtimeId !== 'opencode' ||
|
||||
descriptor.remoteRuntime.provider !== 'opencode' ||
|
||||
!['opencode', 'continue'].includes(descriptor.remoteRuntime.runtimeId) ||
|
||||
descriptor.remoteRuntime.provider !== descriptor.remoteRuntime.runtimeId ||
|
||||
!semanticVersionPattern.test(
|
||||
descriptor.remoteRuntime.version ?? ''
|
||||
) ||
|
||||
@@ -164,6 +164,7 @@ function assertPackageDescriptor(descriptor) {
|
||||
throw new Error('Agent package descriptor is invalid')
|
||||
}
|
||||
assertProtocol(descriptor.agentProtocol, 'Agent protocol')
|
||||
assertAdditionalRuntimes(descriptor)
|
||||
assertProtocol(
|
||||
descriptor.remoteRuntime.protocol,
|
||||
'Remote Runtime protocol'
|
||||
@@ -592,8 +593,8 @@ function assertCatalog(catalog) {
|
||||
entry.formatVersion !== 1 ||
|
||||
entry.product !== 'GoodBuddy' ||
|
||||
entry.component !== 'agent' ||
|
||||
entry.remoteRuntime?.runtimeId !== 'opencode' ||
|
||||
entry.remoteRuntime?.provider !== 'opencode' ||
|
||||
!['opencode', 'continue'].includes(entry.remoteRuntime?.runtimeId) ||
|
||||
entry.remoteRuntime?.provider !== entry.remoteRuntime?.runtimeId ||
|
||||
!semanticVersionPattern.test(
|
||||
entry.remoteRuntime?.version ?? ''
|
||||
) ||
|
||||
@@ -604,6 +605,7 @@ function assertCatalog(catalog) {
|
||||
throw new Error('Agent catalog entry is invalid')
|
||||
}
|
||||
assertProtocol(entry.agentProtocol, 'Agent protocol')
|
||||
assertAdditionalRuntimes(entry)
|
||||
assertProtocol(
|
||||
entry.remoteRuntime.protocol,
|
||||
'Remote Runtime protocol'
|
||||
@@ -634,6 +636,18 @@ function readVerifiedCatalog(catalogPath, signaturePath, registry) {
|
||||
return catalog
|
||||
}
|
||||
|
||||
function assertAdditionalRuntimes(value) {
|
||||
if (value.additionalRuntimes === undefined) return
|
||||
if (!Array.isArray(value.additionalRuntimes) || value.additionalRuntimes.length > 1) throw new Error('Invalid additional Runtimes')
|
||||
for (const runtime of value.additionalRuntimes) {
|
||||
if (!runtime || !['opencode', 'continue'].includes(runtime.runtimeId) || runtime.runtimeId === value.remoteRuntime.runtimeId ||
|
||||
runtime.provider !== runtime.runtimeId || !semanticVersionPattern.test(runtime.version ?? '') || !/^sha256:[a-f0-9]{64}$/u.test(runtime.bundleDigest ?? '')) {
|
||||
throw new Error('Invalid additional Runtime identity')
|
||||
}
|
||||
assertProtocol(runtime.protocol, 'Additional Runtime protocol')
|
||||
}
|
||||
}
|
||||
|
||||
function parseVersion(value) {
|
||||
const match =
|
||||
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?$/u
|
||||
@@ -710,6 +724,10 @@ function createCatalog(options) {
|
||||
...(options.darwinArm64Package ? [readPackageMetadata(options.darwinArm64Package, registry)] : [])
|
||||
]
|
||||
const [first, second] = packages
|
||||
const additionalIdentity = descriptor => canonicalJson((descriptor.additionalRuntimes ?? []).map(({ bundleDigest: _digest, ...identity }) => { void _digest; return identity }))
|
||||
if (packages.some(value => additionalIdentity(value.descriptor) !== additionalIdentity(first.descriptor))) {
|
||||
throw new Error('Agent package matrix has inconsistent additional Runtimes')
|
||||
}
|
||||
const {
|
||||
bundleDigest: _firstRuntimeDigest,
|
||||
...firstRuntimeIdentity
|
||||
|
||||
@@ -38,6 +38,7 @@ function parseArguments(argv) {
|
||||
'--platform',
|
||||
'--node-archive',
|
||||
'--opencode-archive',
|
||||
'--continue-archive',
|
||||
'--output-directory',
|
||||
'--archive'
|
||||
].includes(argument)
|
||||
@@ -63,6 +64,7 @@ function parseArguments(argv) {
|
||||
for (const key of [
|
||||
'nodearchive',
|
||||
'opencodearchive',
|
||||
'continuearchive',
|
||||
'outputdirectory',
|
||||
'archive'
|
||||
]) {
|
||||
@@ -77,6 +79,7 @@ function parseArguments(argv) {
|
||||
platform,
|
||||
nodeArchive: resolve(options.nodearchive),
|
||||
opencodeArchive: resolve(options.opencodearchive),
|
||||
continueArchive: resolve(options.continuearchive),
|
||||
outputDirectory: resolve(options.outputdirectory),
|
||||
archive: resolve(options.archive)
|
||||
}
|
||||
@@ -160,6 +163,11 @@ async function buildCiAgentBundle(options) {
|
||||
registry: signing.registry,
|
||||
testSigningIdentity: identity
|
||||
})
|
||||
const continueRuntime = buildRuntimeBundle({
|
||||
platform, runtimeId: 'continue', lock: runtimeLock, projectRoot, architecture: options.arch,
|
||||
runtimeArchive: options.continueArchive, outputRoot: runtimeRoot,
|
||||
registry: signing.registry, testSigningIdentity: identity
|
||||
})
|
||||
const assemble = (archive) =>
|
||||
assembleAgentPackage({
|
||||
platform,
|
||||
@@ -170,6 +178,7 @@ async function buildCiAgentBundle(options) {
|
||||
output: archive,
|
||||
agentBundle: options.outputDirectory,
|
||||
runtimeBundle: runtime.bundleDirectory,
|
||||
additionalRuntimeBundles: [continueRuntime.bundleDirectory],
|
||||
agentLock: lock,
|
||||
runtimeLock,
|
||||
registry: signing.registry,
|
||||
|
||||
+43
-6
@@ -142,7 +142,7 @@ function packageFileMode(path) {
|
||||
if (path.startsWith('agent/')) {
|
||||
return expectedAgentManifestMode(path.slice('agent/'.length))
|
||||
}
|
||||
return /\/bin\/opencode$/u.test(path) ? '0755' : '0644'
|
||||
return /(?:\/bin\/opencode|\/lib\/continue\/dist\/cn\.js)$/u.test(path) ? '0755' : '0644'
|
||||
}
|
||||
|
||||
function stagePackagePayload(options) {
|
||||
@@ -153,10 +153,13 @@ function stagePackagePayload(options) {
|
||||
join(
|
||||
staging,
|
||||
'runtime',
|
||||
'opencode',
|
||||
options.runtimeId ?? 'opencode',
|
||||
basename(options.runtimeBundle)
|
||||
)
|
||||
)
|
||||
for (const runtime of options.additionalRuntimes ?? []) {
|
||||
copyTree(runtime.bundleDirectory, join(staging, 'runtime', runtime.manifest.runtimeId, basename(runtime.bundleDirectory)))
|
||||
}
|
||||
for (const [value, destination] of [
|
||||
[
|
||||
options.registry,
|
||||
@@ -420,13 +423,22 @@ function assembleAgentPackage(options) {
|
||||
join(tmpdir(), `goodbuddy-agent-package-${target}-`)
|
||||
)
|
||||
try {
|
||||
const additionalRuntimes = (options.additionalRuntimeBundles ?? []).map(directory => verifyRuntimeBundle(directory, {
|
||||
platform, projectRoot, architecture, registry, lock: runtimeLock,
|
||||
verificationEnvironment: options.testSigningIdentity ? 'test' : 'production'
|
||||
}))
|
||||
if (new Set([runtime, ...additionalRuntimes].map(value => value.manifest.runtimeId)).size !== additionalRuntimes.length + 1) {
|
||||
throw new Error('Duplicate packaged Runtime')
|
||||
}
|
||||
stagePackagePayload({
|
||||
staging,
|
||||
registry,
|
||||
agentLock,
|
||||
runtimeLock,
|
||||
agentBundle: resolve(options.agentBundle),
|
||||
runtimeBundle: runtime.bundleDirectory
|
||||
runtimeBundle: runtime.bundleDirectory,
|
||||
runtimeId: runtime.manifest.runtimeId,
|
||||
additionalRuntimes
|
||||
})
|
||||
const payload = payloadFiles(staging)
|
||||
const initial = {
|
||||
@@ -447,6 +459,10 @@ function assembleAgentPackage(options) {
|
||||
bundleDigest: runtime.manifest.bundleDigest,
|
||||
protocol: runtime.manifest.protocol
|
||||
},
|
||||
...(additionalRuntimes.length ? { additionalRuntimes: additionalRuntimes.map(({ manifest }) => ({
|
||||
runtimeId: manifest.runtimeId, provider: manifest.provider, version: manifest.runtimeVersion,
|
||||
bundleDigest: manifest.bundleDigest, protocol: manifest.protocol
|
||||
})) } : {}),
|
||||
contentDigest: `sha256:${'0'.repeat(64)}`,
|
||||
files: payload.files
|
||||
}
|
||||
@@ -510,6 +526,7 @@ function buildAgentPackage(options) {
|
||||
: { signingIdentity })
|
||||
})
|
||||
const runtime = buildRuntimeBundle({
|
||||
runtimeId: options.runtimeId,
|
||||
platform: options.platform ?? 'linux',
|
||||
projectRoot,
|
||||
architecture: options.architecture,
|
||||
@@ -521,6 +538,16 @@ function buildAgentPackage(options) {
|
||||
? { testSigningIdentity: options.testSigningIdentity }
|
||||
: { signingIdentity })
|
||||
})
|
||||
const additionalRuntimeBundles = []
|
||||
if (!options.runtimeId || options.runtimeId === 'opencode') {
|
||||
if (!options.continueArchive) throw new Error('Default Agent package requires the locked Continue archive')
|
||||
additionalRuntimeBundles.push(buildRuntimeBundle({
|
||||
runtimeId: 'continue', platform: options.platform ?? 'linux', projectRoot,
|
||||
architecture: options.architecture, runtimeArchive: options.continueArchive, outputRoot: runtimeRoot,
|
||||
registry, ...(options.runtimeLock ? { lock: options.runtimeLock } : {}),
|
||||
...(options.testSigningIdentity ? { testSigningIdentity: options.testSigningIdentity } : { signingIdentity })
|
||||
}).bundleDirectory)
|
||||
}
|
||||
return assembleAgentPackage({
|
||||
...options,
|
||||
projectRoot,
|
||||
@@ -529,7 +556,8 @@ function buildAgentPackage(options) {
|
||||
? {}
|
||||
: { signingIdentity }),
|
||||
agentBundle,
|
||||
runtimeBundle: runtime.bundleDirectory
|
||||
runtimeBundle: runtime.bundleDirectory,
|
||||
additionalRuntimeBundles
|
||||
})
|
||||
} finally {
|
||||
rmSync(agentRoot, { recursive: true, force: true })
|
||||
@@ -555,6 +583,9 @@ function parseArguments(argv) {
|
||||
'--minimum-desktop-version',
|
||||
'--node-archive',
|
||||
'--runtime-archive',
|
||||
'--runtime-id',
|
||||
'--continue-archive',
|
||||
'--continue-bundle',
|
||||
'--agent-bundle',
|
||||
'--runtime-bundle',
|
||||
'--output'
|
||||
@@ -579,11 +610,15 @@ function parseArguments(argv) {
|
||||
throw new Error('Agent package arguments are incomplete')
|
||||
}
|
||||
const common = {
|
||||
runtimeId: options.runtimeId ?? 'opencode',
|
||||
platform: options.platform ?? 'linux',
|
||||
architecture: options.arch,
|
||||
minimumDesktopVersion: options.minimumDesktopVersion,
|
||||
output: resolve(options.output)
|
||||
}
|
||||
if (!['opencode', 'continue'].includes(common.runtimeId)) {
|
||||
throw new Error('Unsupported Agent package Runtime')
|
||||
}
|
||||
targetName(common.architecture, common.platform)
|
||||
if (command === 'build') {
|
||||
if (!options.nodeArchive || !options.runtimeArchive) {
|
||||
@@ -593,7 +628,8 @@ function parseArguments(argv) {
|
||||
command,
|
||||
...common,
|
||||
nodeArchive: resolve(options.nodeArchive),
|
||||
runtimeArchive: resolve(options.runtimeArchive)
|
||||
runtimeArchive: resolve(options.runtimeArchive),
|
||||
continueArchive: options.continueArchive ? resolve(options.continueArchive) : undefined
|
||||
}
|
||||
}
|
||||
if (!options.agentBundle || !options.runtimeBundle) {
|
||||
@@ -603,7 +639,8 @@ function parseArguments(argv) {
|
||||
command,
|
||||
...common,
|
||||
agentBundle: resolve(options.agentBundle),
|
||||
runtimeBundle: resolve(options.runtimeBundle)
|
||||
runtimeBundle: resolve(options.runtimeBundle),
|
||||
additionalRuntimeBundles: options.continueBundle ? [resolve(options.continueBundle)] : []
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,17 @@ const profileDigests = Object.freeze({
|
||||
"sha256:ac7696abe504bbea444d8ad80dd8faa96437e6dbb27ed987ce46e1045f3dd365",
|
||||
acp: "sha256:0e3764ab897258bc0234162357c2eefc3faeb9918080ae81b60e2084ed7996a6",
|
||||
});
|
||||
function runtimeProfileDigests(runtimeId) {
|
||||
if (runtimeId === 'opencode') return profileDigests;
|
||||
if (runtimeId !== 'continue') throw new Error('Unsupported Runtime');
|
||||
return {
|
||||
adapter: `sha256:${sha256('GoodBuddy Continue HTTP ACP facade v1')}`,
|
||||
acp: `sha256:${sha256(canonicalJson({ protocolVersion: 1, capabilities: {
|
||||
loadSession: true, mcpCapabilities: { http: true }, promptCapabilities: { image: true },
|
||||
sessionCapabilities: { resume: {}, close: {} }
|
||||
} }))}`
|
||||
};
|
||||
}
|
||||
const fixedLimits = Object.freeze({
|
||||
// Zero leaves prompt duration to its requested deadline and cancellation.
|
||||
maximumPromptRuntimeMilliseconds: 0,
|
||||
@@ -365,9 +376,9 @@ function validateManifestShape(manifest) {
|
||||
if (
|
||||
manifest?.formatVersion !== 2 ||
|
||||
manifest.product !== "GoodBuddy" ||
|
||||
manifest.runtimeId !== "opencode" ||
|
||||
manifest.runtimeVersion !== "1.18.29" ||
|
||||
manifest.provider !== "opencode" ||
|
||||
!['opencode', 'continue'].includes(manifest.runtimeId) ||
|
||||
manifest.runtimeVersion !== (manifest.runtimeId === 'continue' ? '1.5.47' : '1.18.29') ||
|
||||
manifest.provider !== manifest.runtimeId ||
|
||||
!(
|
||||
manifest.platform === "linux" ||
|
||||
(manifest.platform === "darwin" && manifest.architecture === "arm64")
|
||||
@@ -396,10 +407,10 @@ function validateManifestShape(manifest) {
|
||||
throw new Error("Runtime manifest limits contract is invalid");
|
||||
}
|
||||
if (
|
||||
manifest.entrypoint?.identity !== "opencode-acp" ||
|
||||
manifest.entrypoint.path !== "bin/opencode" ||
|
||||
manifest.entrypoint?.identity !== `${manifest.runtimeId}-acp` ||
|
||||
manifest.entrypoint.path !== (manifest.runtimeId === 'continue' ? 'lib/continue/dist/cn.js' : 'bin/opencode') ||
|
||||
!/^[a-f0-9]{64}$/u.test(manifest.entrypoint.sha256) ||
|
||||
JSON.stringify(manifest.entrypoint.argvPrefix) !== JSON.stringify(["acp"])
|
||||
JSON.stringify(manifest.entrypoint.argvPrefix) !== JSON.stringify(manifest.runtimeId === 'continue' ? [] : ['acp'])
|
||||
) {
|
||||
throw new Error("Runtime manifest entrypoint is invalid");
|
||||
}
|
||||
@@ -436,13 +447,14 @@ function assertManifestMatchesLock(
|
||||
architecture,
|
||||
platform = "linux",
|
||||
) {
|
||||
const expected = lock.runtimes.opencode;
|
||||
const expected = lock.runtimes[manifest.runtimeId];
|
||||
if (!expected) throw new Error('Runtime is not locked');
|
||||
const target =
|
||||
expected.targets[
|
||||
platform === "linux" ? architecture : targetName(architecture, platform)
|
||||
];
|
||||
if (
|
||||
manifest.runtimeId !== "opencode" ||
|
||||
manifest.runtimeId !== expected.provider ||
|
||||
manifest.runtimeVersion !== expected.version ||
|
||||
manifest.provider !== expected.provider ||
|
||||
manifest.platform !== platform ||
|
||||
@@ -457,8 +469,8 @@ function assertManifestMatchesLock(
|
||||
JSON.stringify(expected.allowedEnvironmentNames) ||
|
||||
manifest.protocol?.major !== expected.protocol.major ||
|
||||
manifest.protocol?.minor !== expected.protocol.minor ||
|
||||
manifest.adapterDigest !== profileDigests.adapter ||
|
||||
manifest.acpCapabilitiesDigest !== profileDigests.acp
|
||||
manifest.adapterDigest !== runtimeProfileDigests(manifest.runtimeId).adapter ||
|
||||
manifest.acpCapabilitiesDigest !== runtimeProfileDigests(manifest.runtimeId).acp
|
||||
) {
|
||||
throw new Error(
|
||||
"Runtime manifest does not match the locked OpenCode profile",
|
||||
@@ -572,7 +584,7 @@ function verifyBundleDirectory(bundleDirectoryInput, options = {}) {
|
||||
) {
|
||||
throw new Error("Runtime entrypoint payload is missing");
|
||||
}
|
||||
assertElfArchitecture(
|
||||
if (manifest.runtimeId === 'opencode') assertElfArchitecture(
|
||||
join(bundleDirectory, ...manifest.entrypoint.path.split("/")),
|
||||
architecture,
|
||||
platform,
|
||||
@@ -584,8 +596,8 @@ function verifyBundleDirectory(bundleDirectoryInput, options = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
function lockedArchive(lock, architecture, archivePath, platform = "linux") {
|
||||
const input = resolveLockedRuntimeInput(lock, architecture, platform);
|
||||
function lockedArchive(lock, architecture, archivePath, platform = "linux", runtimeId = 'opencode') {
|
||||
const input = resolveLockedRuntimeInput(lock, architecture, platform, runtimeId);
|
||||
if (!archivePath || !existsSync(archivePath)) {
|
||||
throw new Error(`Locked OpenCode archive is required: ${input.archive}`);
|
||||
}
|
||||
@@ -603,14 +615,15 @@ function lockedArchive(lock, architecture, archivePath, platform = "linux") {
|
||||
`Runtime archive integrity mismatch for ${targetName(architecture)}`,
|
||||
);
|
||||
}
|
||||
return lock.runtimes.opencode.targets[
|
||||
return lock.runtimes[runtimeId].targets[
|
||||
platform === "linux" ? architecture : targetName(architecture, platform)
|
||||
];
|
||||
}
|
||||
|
||||
function resolveLockedRuntimeInput(lock, architecture, platform = "linux") {
|
||||
function resolveLockedRuntimeInput(lock, architecture, platform = "linux", runtimeId = 'opencode') {
|
||||
targetName(architecture, platform);
|
||||
const runtime = lock.runtimes.opencode;
|
||||
const runtime = lock.runtimes[runtimeId];
|
||||
if (!runtime) throw new Error('Runtime is not locked');
|
||||
const target =
|
||||
runtime.targets[
|
||||
platform === "linux" ? architecture : targetName(architecture, platform)
|
||||
@@ -619,7 +632,7 @@ function resolveLockedRuntimeInput(lock, architecture, platform = "linux") {
|
||||
packageName: target.package,
|
||||
version: runtime.version,
|
||||
integrity: target.integrity,
|
||||
archive: `${target.package}-${runtime.version}.tgz`,
|
||||
archive: `${target.package.replace('@', '').replace('/', '-')}-${runtime.version}.tgz`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -636,24 +649,24 @@ function createManifest(bundleDirectory, metadata) {
|
||||
path,
|
||||
size: statSync(filePath).size,
|
||||
sha256: sha256FileSync(filePath),
|
||||
mode: path === "bin/opencode" ? "0755" : "0644",
|
||||
mode: path === metadata.runtime.entrypoint ? "0755" : "0644",
|
||||
};
|
||||
});
|
||||
const entrypoint = files.find((file) => file.path === "bin/opencode");
|
||||
const entrypoint = files.find((file) => file.path === metadata.runtime.entrypoint);
|
||||
if (!entrypoint) {
|
||||
throw new Error("Runtime entrypoint is missing");
|
||||
}
|
||||
const initial = {
|
||||
formatVersion: 2,
|
||||
product: "GoodBuddy",
|
||||
runtimeId: "opencode",
|
||||
runtimeId: metadata.runtime.provider,
|
||||
runtimeVersion: metadata.runtime.version,
|
||||
provider: metadata.runtime.provider,
|
||||
platform: metadata.platform ?? "linux",
|
||||
architecture: metadata.architecture,
|
||||
signingKeyId: metadata.signingKeyId,
|
||||
bundleDigest: `sha256:${"0".repeat(64)}`,
|
||||
adapterDigest: profileDigests.adapter,
|
||||
adapterDigest: runtimeProfileDigests(metadata.runtime.provider).adapter,
|
||||
sourcePackage: metadata.sourcePackage,
|
||||
entrypoint: {
|
||||
identity: metadata.runtime.entrypointIdentity,
|
||||
@@ -664,15 +677,15 @@ function createManifest(bundleDirectory, metadata) {
|
||||
files,
|
||||
licenses: [
|
||||
{
|
||||
package: "opencode-ai",
|
||||
package: metadata.runtime.provider === 'continue' ? '@continuedev/cli' : 'opencode-ai',
|
||||
version: metadata.runtime.version,
|
||||
spdx: "MIT",
|
||||
path: "licenses/opencode-MIT.txt",
|
||||
spdx: metadata.runtime.provider === 'continue' ? 'Apache-2.0' : 'MIT',
|
||||
path: metadata.runtime.provider === 'continue' ? 'licenses/continue-Apache-2.0.txt' : 'licenses/opencode-MIT.txt',
|
||||
},
|
||||
],
|
||||
allowedEnvironmentNames: metadata.runtime.allowedEnvironmentNames,
|
||||
protocol: metadata.runtime.protocol,
|
||||
acpCapabilitiesDigest: profileDigests.acp,
|
||||
acpCapabilitiesDigest: runtimeProfileDigests(metadata.runtime.provider).acp,
|
||||
limits: fixedLimits,
|
||||
};
|
||||
return {
|
||||
@@ -685,11 +698,11 @@ function defaultOutputRoot(projectRoot = root) {
|
||||
return join(projectRoot, ".remote-runtime-resources");
|
||||
}
|
||||
|
||||
function bundleDirectory(outputRoot, architecture, digest, platform = "linux") {
|
||||
function bundleDirectory(outputRoot, architecture, digest, platform = "linux", runtimeId = 'opencode') {
|
||||
return join(
|
||||
outputRoot,
|
||||
targetName(architecture, platform),
|
||||
"opencode",
|
||||
runtimeId,
|
||||
digest.slice("sha256:".length),
|
||||
);
|
||||
}
|
||||
@@ -699,12 +712,14 @@ function buildRuntimeBundle(options) {
|
||||
const architecture = options.architecture;
|
||||
const platform = options.platform ?? "linux";
|
||||
const lock = options.lock ?? readRemoteRuntimeLock(projectRoot);
|
||||
const runtime = lock.runtimes.opencode;
|
||||
const runtimeId = options.runtimeId ?? 'opencode';
|
||||
const runtime = lock.runtimes[runtimeId];
|
||||
const sourcePackage = lockedArchive(
|
||||
lock,
|
||||
architecture,
|
||||
options.runtimeArchive,
|
||||
platform,
|
||||
runtimeId,
|
||||
);
|
||||
const registry = options.registry ?? readTrustedKeyRegistry(projectRoot);
|
||||
const signingIdentity =
|
||||
@@ -730,6 +745,7 @@ function buildRuntimeBundle(options) {
|
||||
sync: true,
|
||||
strict: true,
|
||||
filter(path) {
|
||||
if (runtimeId === 'continue') return path === 'package/package.json' || path.startsWith('package/dist/') || path === 'package/LICENSE';
|
||||
return (
|
||||
path === "package/package.json" || path === "package/bin/opencode"
|
||||
);
|
||||
@@ -753,6 +769,15 @@ function buildRuntimeBundle(options) {
|
||||
"OpenCode source package version does not match the lock",
|
||||
);
|
||||
}
|
||||
if (runtimeId === 'continue') {
|
||||
mkdirSync(join(staging, 'lib'), { mode: 0o700 });
|
||||
cpSync(join(extracted, 'package'), join(staging, 'lib', 'continue'), { recursive: true });
|
||||
for (const file of listFiles(staging)) chmodSync(file, 0o644);
|
||||
chmodSync(join(staging, runtime.entrypoint), 0o755);
|
||||
mkdirSync(join(staging, 'licenses'), { mode: 0o700 });
|
||||
// The pinned CN package declares Apache-2.0 but omits the license text.
|
||||
copyFileSync(join(projectRoot, 'node_modules', '@agentclientprotocol', 'sdk', 'LICENSE'), join(staging, 'licenses', 'continue-Apache-2.0.txt'));
|
||||
} else {
|
||||
mkdirSync(join(staging, "bin"), { mode: 0o700 });
|
||||
copyFileSync(
|
||||
join(extracted, "package", "bin", "opencode"),
|
||||
@@ -780,6 +805,7 @@ function buildRuntimeBundle(options) {
|
||||
join(staging, "licenses", "opencode-MIT.txt"),
|
||||
);
|
||||
chmodSync(join(staging, "licenses", "opencode-MIT.txt"), 0o644);
|
||||
}
|
||||
const manifest = createManifest(staging, {
|
||||
platform,
|
||||
architecture,
|
||||
@@ -811,6 +837,7 @@ function buildRuntimeBundle(options) {
|
||||
architecture,
|
||||
manifest.bundleDigest,
|
||||
platform,
|
||||
runtimeId,
|
||||
);
|
||||
mkdirSync(dirname(destination), { recursive: true, mode: 0o700 });
|
||||
if (existsSync(destination)) {
|
||||
@@ -849,6 +876,8 @@ function buildRuntimeBundle(options) {
|
||||
|
||||
function createRuntimeArchive(bundleDirectoryInput, archivePath) {
|
||||
const directory = resolve(bundleDirectoryInput);
|
||||
const manifest = readJson(join(directory, manifestFileName), 'Runtime manifest');
|
||||
const modes = new Map(manifest.files.map(file => [file.path, parseInt(file.mode, 8)]));
|
||||
const entries = listFiles(directory)
|
||||
.map((filePath) => posixRelative(directory, filePath))
|
||||
.sort(compareUtf8);
|
||||
@@ -862,6 +891,9 @@ function createRuntimeArchive(bundleDirectoryInput, archivePath) {
|
||||
portable: true,
|
||||
noPax: true,
|
||||
mtime: new Date(0),
|
||||
onWriteEntry(entry) {
|
||||
if (entry.stat) entry.stat.mode = modes.get(entry.path) ?? 0o644;
|
||||
},
|
||||
},
|
||||
entries,
|
||||
);
|
||||
@@ -896,6 +928,8 @@ function importRuntimeArchive(archivePath, options) {
|
||||
outputRoot,
|
||||
architecture,
|
||||
verified.manifest.bundleDigest,
|
||||
verified.manifest.platform,
|
||||
verified.manifest.runtimeId,
|
||||
);
|
||||
mkdirSync(dirname(destination), { recursive: true, mode: 0o700 });
|
||||
if (existsSync(destination)) {
|
||||
@@ -917,8 +951,8 @@ function importRuntimeArchive(archivePath, options) {
|
||||
}
|
||||
}
|
||||
|
||||
function findOnlyBundle(outputRoot, architecture) {
|
||||
const runtimeRoot = join(outputRoot, targetName(architecture), "opencode");
|
||||
function findOnlyBundle(outputRoot, architecture, runtimeId = 'opencode') {
|
||||
const runtimeRoot = join(outputRoot, targetName(architecture), runtimeId);
|
||||
const entries = existsSync(runtimeRoot)
|
||||
? readdirSync(runtimeRoot, { withFileTypes: true })
|
||||
.filter((entry) => entry.isDirectory())
|
||||
@@ -954,6 +988,7 @@ function parseArguments(argv) {
|
||||
const allowed = new Set([
|
||||
"--arch",
|
||||
"--runtime-archive",
|
||||
"--runtime-id",
|
||||
"--archive",
|
||||
"--output-root",
|
||||
"--bundle",
|
||||
@@ -997,6 +1032,7 @@ function main(argv = process.argv.slice(2)) {
|
||||
if (options.command === "build") {
|
||||
const verified = buildRuntimeBundle({
|
||||
architecture: options.arch,
|
||||
runtimeId: options.runtimeId,
|
||||
runtimeArchive: resolve(options.runtimeArchive ?? ""),
|
||||
outputRoot,
|
||||
});
|
||||
@@ -1006,7 +1042,7 @@ function main(argv = process.argv.slice(2)) {
|
||||
root,
|
||||
"dist",
|
||||
"remote-runtime-bundles",
|
||||
`goodbuddy-opencode-${targetName(options.arch)}.tar`,
|
||||
`goodbuddy-${options.runtimeId ?? 'opencode'}-${targetName(options.arch)}.tar`,
|
||||
);
|
||||
createRuntimeArchive(verified.bundleDirectory, archivePath);
|
||||
console.log(
|
||||
@@ -1027,7 +1063,7 @@ function main(argv = process.argv.slice(2)) {
|
||||
verifyBundleDirectory(
|
||||
options.bundle
|
||||
? resolve(options.bundle)
|
||||
: findOnlyBundle(outputRoot, options.arch),
|
||||
: findOnlyBundle(outputRoot, options.arch, options.runtimeId),
|
||||
{ architecture: options.arch },
|
||||
);
|
||||
console.log(`Remote Runtime bundle verified: ${targetName(options.arch)}`);
|
||||
|
||||
@@ -425,9 +425,8 @@ Renderer 只通过显式 IPC 列出、加入、删除、提升、释放和接收
|
||||
|
||||
```text
|
||||
任务中心
|
||||
├─ 需要关注
|
||||
├─ 进行中
|
||||
├─ 已暂停
|
||||
├─ 暂停
|
||||
└─ 已结束
|
||||
└─ 打开任务自身
|
||||
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
# 本机专用推理架构与任务控制评估
|
||||
|
||||
## 记录范围与状态
|
||||
|
||||
评估日期:2026-09-18。本文记录 ASR、OCR、内置 embedding 的执行边界、任务控制缺口和后续优化依据,供语音输入、文档解析、知识检索与索引共同使用。
|
||||
|
||||
现状依据本次工作区源码核对,包含尚未提交的变更,不等同于已发布版本或实机验收结论。本次仅整理文档,未运行引擎性能测试、复现故障或实施迁移。下文“建议”是评估方向,“待办”均未在本次实施,也没有约定交付日期。
|
||||
|
||||
产品 Task、Job、Run 的定义沿用 [Task 与 Job 领域模型](../features/task-and-job/task-and-job-model.md)。本文的“推理任务”指一次引擎请求及其执行记录,不自动等同于产品 Task,不据此新增产品对象、持久化日志或全局调度器。本机推理管理的现有界面和接入清单见[应用导航技术设计](../features/application-tool-navigation/technical-design.md#111-本机推理任务与执行服务);本文负责跨功能架构评估及优化待办。
|
||||
|
||||
## 当前执行边界
|
||||
|
||||
| 能力 | 源码现状 | 所有权与释放 | 评估建议 |
|
||||
| --- | --- | --- | --- |
|
||||
| ASR | 录音结果就绪后提交完整 PCM;Main 创建 `node:worker_threads` Worker,以 sherpa-onnx WASM 执行离线识别;服务一次只接受一个请求 | 每请求创建识别器;超时或 abort 调用并等待 `worker.terminate()`;正常结果路径的清理确认存在缺口,见 TC-4 | 保留请求级 Worker,适合间歇语音输入;常驻化需流式需求或测量支持 |
|
||||
| OCR | Main 排队后发往主窗口 Renderer;Renderer 复用 Web Worker,运行 `ppu-paddle-ocr/web` 与 `onnxruntime-web` WASM;文档串行处理,PDF 逐页处理 | Renderer 模块持有 Worker;活动请求取消或单页超时会终止 Worker;无活动和排队请求时空闲 60 秒释放 | 当前保留 Web Worker;独立于 UI 的持续任务可评估专用后台 Renderer 加 Worker |
|
||||
| 内置 embedding | Main 按模型目录共享引用计数 Broker;transport 使用 `utilityProcess.fork`,Granite tokenizer 和 ONNX session 首次请求懒加载并复用 | 多使用方共享进程;最后一个 provider 释放时 shutdown;也支持显式启停,停止等待 child exit | 保留共享 utilityProcess;优先核对取消后的容量回收与加载失败重试 |
|
||||
|
||||
### ASR 的证据与边界
|
||||
|
||||
[App.tsx](../../src/renderer/src/App.tsx) 在 `recording.result` 完成后调用 `speech.transcribe`;[录音模块](../../src/renderer/src/speech-recognition.ts)负责采样与重采样。[SpeechTranscriptionService](../../src/main/speech/speech-transcription-service.ts) 的 `active.size` 拒绝并发识别,`runSherpaTranscription` 为每次请求创建 Worker,转移音频缓冲区,使用 `createOfflineRecognizer` 同步 decode。Worker 创建后的识别超时为 120 秒,不包含此前模型解析的等待时间。
|
||||
|
||||
[package.json](../../package.json) 固定 `sherpa-onnx` 为 1.13.4;本次同时核对已安装包的 `index.js`,其入口加载 `sherpa-onnx-wasm-nodejs.js`。因此这里记录的是 Node Worker 内的 WASM 路径。Node Worker 与 Main 共享 OS 进程,线程隔离不能视为独立进程故障边界。
|
||||
|
||||
Worker 先 `postMessage`,再在 `finally` 中调用 `stream.free()` 和 `recognizer.free()`。Main 收到结果后以 `finish(..., false)` 结算、移除监听和超时,不等待 Worker exit;随后 `active` 删除请求,推理记录转为终态。这证明结果完成与资源清理确认之间存在记账间隙,但尚未证明实际泄漏或清理耗时。
|
||||
|
||||
### OCR 的浏览器依赖
|
||||
|
||||
[OCR Worker](../../src/renderer/src/document-ocr-worker.ts) 显式选择 WASM provider、`canvas-native` 图像处理,当前 `ort.env.wasm.numThreads = 1`。PDF 路径使用 `pdfjs-dist`、`OffscreenCanvas` 和 `convertToBlob`。本次核对的依赖链为 `ppu-paddle-ocr/web/platform.web.js` 到 `ppu-ocv/canvas-web`;后者的 `platform/web.js` 使用 `createImageBitmap` 解码图片。包依赖以 [package-lock.json](../../package-lock.json) 为准,依赖升级后需重新核对此结论。
|
||||
|
||||
这些能力与浏览器执行环境有关。直接迁到 Node utilityProcess,需要另行验证图像解码、Canvas、PDF 渲染及打包资源路径,不能只替换进程创建 API 就宣称等价。
|
||||
|
||||
[DocumentOcrBroker](../../src/main/document-ocr-broker.ts) 最多保留 4 个 pending 请求,一次派发一个;总超时从派发时开始,不覆盖 Main 排队等待。其预算为 `min(10 分钟, max(单页预算, 60 秒 + 单页预算 × 页数))`。[Renderer bridge](../../src/renderer/src/document-ocr-bridge.ts) 另有串行 Promise 队列,Worker 就绪超时为 120 秒,就绪后按单页预算计时,并在进度消息到达时重置单页计时器。Main 的 60 秒启动预算与 Renderer 的 120 秒就绪等待未对齐,短文档冷启动时 Main 有机会先判超时。
|
||||
|
||||
[Main 初始化](../../src/main/index.ts) 将 Broker 绑定到 `mainWindow`;[Renderer 入口](../../src/renderer/src/main.tsx) 安装 bridge。Broker 派发前仅检查窗口销毁等条件,没有等待 bridge 就绪的握手,也没有在该派发链路中处理 bridge 就绪丢失。Renderer 内的 Worker `ready` 只确认模型初始化,不能证明 Main 发请求时 Renderer 已安装 IPC 监听。窗口重载、Renderer 崩溃及启动时监听尚未安装的影响应按 TC-3 复现。
|
||||
|
||||
来源请求可以取消 OCR;已派发请求取消时 Main 发送取消消息并立即结算,然后派发下一项,未等待 Renderer 清理确认。Renderer 对当前活动请求的取消会终止共享 Worker。管理页因此没有将该操作作为安全的单任务取消能力;手动释放也会拒绝活动或排队请求。
|
||||
|
||||
### Embedding 的共享执行与取消
|
||||
|
||||
[Main provider 工厂](../../src/main/index.ts) 按模型目录维护 `sharedEmbeddingBrokers` 及使用计数;[transport](../../src/main/knowledge/embedding-utility-transport.ts) 以 spawn 确认进程启动。[Granite 引擎](../../src/main/knowledge/granite-embedding-engine.ts) 缓存 `loadTask`,在首次请求时创建 tokenizer 和 WASM ONNX session。进程已启动不等于模型已加载;当前没有为模型加载完成提供独立服务状态。
|
||||
|
||||
[Broker](../../src/main/knowledge/embedding-inference-broker.ts) 默认请求超时为 120 秒,计时在 transport 就绪后建立 pending 时开始。调用方取消或超时会先返回错误,同时发送 cancel,保留 pending 和推理执行记录,等待结果、`CANCELLED` 或进程退出。保留容量避免把仍在计算的任务当作已释放。
|
||||
|
||||
[执行端](../../src/main/knowledge/embedding-inference-worker.ts) 收到 cancel 后设置 AbortSignal;Granite 在加载、输入构造和 `session.run` 前后检查取消,但没有将可抢占的取消句柄传入 `session.run`。若该调用一直不返回,取消检查和确认就无法完成。Broker 的 `cancelPending` 会清除原计时器,当前没有针对该 pending 的后续有界清理升级,因此持续挂起可一直占用在途容量。此为源码可达的条件风险,真实引擎挂起尚未复现。
|
||||
|
||||
`loadTask ??= this.loadFiles(...)` 也会缓存被拒绝的加载 Promise;同一引擎实例后续请求复用同一个失败,不能通过再次请求重新加载。重建进程会建立新引擎;是否增加实例内重试及其清理方式,按 TC-2 验证后确定。
|
||||
|
||||
## 架构建议与触发条件
|
||||
|
||||
ASR 继续按录音请求创建 Worker。对低频、间歇输入,每次释放模型能避免长期占用;本次没有冷启动与常驻成本数据,不能量化收益。只有连续流式识别成为真实需求,或高频请求测出重复加载已影响目标延迟,才评估常驻 utilityProcess。届时需比较加载成本、暖请求延迟、空闲工作集和取消时延,并重新设计音频流与识别器复用;单纯常驻不会把当前离线 API 变成流式识别。
|
||||
|
||||
OCR 保留 Web Worker。如果文档任务需要在主 UI 重载或不可用时继续执行,优先评估由 Main 持有的专用后台 Renderer 加 Worker,以保留浏览器图像/PDF 能力。该方案仍是候选,需要完成以下设计与实机验证后才可决定迁移:
|
||||
|
||||
- 保持现有 sandbox、context isolation、禁用 Node integration 的边界;通过专用 preload 暴露必要方法,并沿用可信 sender 与输入校验。当前 IPC 若限定主窗口 sender,需要明确新增宿主的受信身份及响应归属。
|
||||
- 分别确认后台 Renderer 的 IPC 监听就绪与 Worker 模型就绪;定义重载、崩溃、启动失败及应用退出时 pending、队列和模型的处置,防止静默丢请求或自动重放。
|
||||
- Main 持有后台宿主生命周期;管理 UI 关闭不销毁执行宿主,服务释放和应用退出才按约定清理。验证隐藏 Renderer 的调度、计时器和 Worker 执行,不根据窗口不可见推断运行不受影响。
|
||||
- 用实际 OS PID、Electron 进程指标和崩溃影响验证隔离程度。单独的 BrowserWindow/WebContents 不能直接作为独立 OS 进程或独立服务资源统计的证据;若与其他 Renderer 共享 PID,必须继续报告共享范围。
|
||||
|
||||
Embedding 保留共享 utilityProcess。查询与索引共享模型和进程的现有方式具备复用基础,优化先处理取消回收和失败重试。单请求取消继续尽量只影响该请求;进程终止属于服务级操作,必须说明其他使用方的结果。不得把每次请求超时直接等同于可以无条件杀死共享进程。
|
||||
|
||||
## 任务控制覆盖与缺口
|
||||
|
||||
[共享契约](../../src/shared/local-inference-contracts.ts)将服务状态与任务状态分开;[LocalInferenceService](../../src/main/local-inference-service.ts) 在 Main 内存登记请求,保存活动项及最近 100 条终态记录。任务的 `running` 当前包含排队、模型加载与执行,未提供统一细分阶段。该登记器是观测和控制入口,各引擎仍负责实际队列与清理。
|
||||
|
||||
| 控制维度 | 当前覆盖 | 后续评估和验收要求 |
|
||||
| --- | --- | --- |
|
||||
| 状态 | 任务有 `running/cancelling/completed/cancelled/failed`;服务另有启动、运行、停止、未知和错误状态;ASR 模型可用时服务仍为 unknown | 区分调用方已返回、执行仍活动、资源清理已确认;进程 spawn、模型 ready、请求完成不能互相替代。先增加必要证据,不预建完整阶段状态机 |
|
||||
| 所有权 | ASR 请求持有 Worker;OCR Renderer 持有共享 Worker;embedding 按目录共享 Broker 并引用计数 | 控制记录注明真实来源与宿主;缺失项目、会话关联时不猜测归属。产品 Task 的取消不能凭名称去停止整个推理服务 |
|
||||
| 取消 | ASR terminate;OCR 来源取消会终止当前 Worker;embedding 协作取消并保留执行记录 | 验证取消与自然完成竞态、重复取消及清理结果。单请求取消与共享服务停止分别定义影响,不用 UI 隐藏代替取消 |
|
||||
| 超时与容量 | ASR Worker 120 秒;OCR 两端预算与排队语义不同;embedding 默认请求 120 秒且保留取消后容量 | 分别说明排队、启动、推理、清理的计时起点和上限;调用方超时后仍在执行的工作需要有界处置,不得提前释放容量制造超额并发 |
|
||||
| 停止与清理 | 服务操作按 serviceId 串行,期间拒绝新增请求;停止校验当前活动 taskId 确认清单;向量停止等待 exit,重启不重放请求 | 清理失败保持可观察的活动/错误信息;窗口或宿主丢失需使受影响请求得到明确结果;ASR 和 OCR 的结果/清理间隙分别核对 |
|
||||
| 可观测性 | 有来源、状态、时间、可选模型和错误;向量有 PID 资源数据;管理 Modal 不显示任务历史 | 测量排队、加载、执行、取消请求到执行结束、清理确认的耗时与在途数;日志不记录音频、图片或向量原文,未知指标不补零 |
|
||||
|
||||
本文的覆盖要求用于后续问题复现和最小修复,不构成引入统一常驻服务框架、持久化执行副本或自动恢复协议的决定。范围为桌面本机引擎;远程 Agent、外部向量端点和未接入的 TTS 不因此获得同样控制能力。
|
||||
|
||||
## 资源观测口径
|
||||
|
||||
[LocalInferencePage](../../src/renderer/src/LocalInferencePage.tsx) 在启用、文档可见且 Modal 内容未被 hidden/inert 隔离时每 5 秒读取快照,跳过重叠请求,关闭时清理轮询。该刷新间隔不能用于证明短请求的峰值或清理完成时刻。
|
||||
|
||||
[向量资源采样器](../../src/main/inference-process-resources.ts) 按 transport 当前 PID 匹配 `app.getAppMetrics()` 的 Utility 条目,用累计 CPU 秒差除以单调时间间隔。100% 表示一个逻辑核心,可超过 100%;首次采样、PID/创建时间变化、间隔超过 15 秒或读数异常时重建基线。工作集从 KiB 转为字节,UI 显示 MiB。
|
||||
|
||||
向量工作集包含运行时、模型、临时缓冲区及并发使用方,不能当作模型权重大小或单任务内存。ASR Node Worker 和 OCR Web Worker 没有当前可独立归属的 OS 进程工作集;宿主进程总量不能直接填入某个服务行。未来 OCR 后台宿主也必须先核实 PID 与共享情况,再决定指标范围。显存当前未采集。
|
||||
|
||||
## 优化待办与验收
|
||||
|
||||
优先级表示建议处理顺序:P1 为执行正确性和回收问题,P2 为测量及有条件的架构选型。全部待办待开展;源码核对已完成,故障复现、修复和实机验收分别记录,不以文档完成替代实现完成。
|
||||
|
||||
| ID | 优先级与事项 | 依赖及验收条件 |
|
||||
| --- | --- | --- |
|
||||
| TC-1 | P1:embedding 取消/超时后的有界回收 | 先覆盖 `session.run` 延迟返回及不返回,观察 caller、pending、执行记录与容量。定义取消后的清理等待上限及升级条件,数值依据测量确定。普通取消不终止其他共享任务;确需服务级终止时明确受影响使用方及失败结果,等实际退出再释放容量,且不重放请求。模拟用于稳定触发条件,最终以真实引擎取消和停止路径补验 |
|
||||
| TC-2 | P1:embedding 加载失败重试 | 覆盖首次读模型或建 session 失败、随后恢复依赖的场景;选择清除失败缓存或显式重建引擎的最小方案。验收后续请求能够重新加载,并发请求不重复加载,失败中已创建的资源被清理,销毁后不再复活;不能把一次成功创建进程当作重试通过 |
|
||||
| TC-3 | P1:OCR 宿主就绪、丢失与超时预算 | 保留当前 Web Worker 即可先验证,无须等待迁移。覆盖监听安装前派发、主 Renderer 重载/崩溃、冷启动超过 Main 启动预算及排队取消。明确 bridge 就绪与 Worker ready 的区别;受影响请求有明确结果,队列能继续使用,没有静默丢失或重放;两端预算、起算点和取消后下一项派发与清理顺序一致 |
|
||||
| TC-4 | P1:ASR 结果与清理记账 | 覆盖结果先到、延迟退出、清理异常以及取消/超时与结果竞争。选择延后请求结算或单独保留清理记录的最小方案;验收资源仍活动时不会被报告为已释放,也不提前放开实际单请求容量。正常完成及异常路径均验证 Worker 最终退出 |
|
||||
| TC-5 | P2:统一测量记录与资源归属核验 | 可与 P1 复现并行,为 TC-6/TC-7 提供依据。记录冷/暖加载时间、端到端延迟、排队时长、取消至执行结束/资源回收时长、空闲与峰值工作集、CPU 时间和样本数;按 PID 对照 OS 工具,分别报告引擎、宿主与应用总量。重启后不沿用旧 PID 基线,不以 5 秒 UI 轮询代替专门测量 |
|
||||
| TC-6 | P2、条件待定:OCR 后台 Renderer 加 Worker | 依赖明确的 UI 独立执行需求、TC-3 生命周期结论及 TC-5 数据。候选验证需包含图片、多页 PDF、UI 重载时执行、后台宿主崩溃、空闲释放、应用退出、preload/IPC sender 边界和各支持平台打包资源。证实任务连续性及实际 PID 隔离后,再记录采用或保留现状的结论 |
|
||||
| TC-7 | P2、条件待定:ASR 常驻 utilityProcess | 依赖流式/高频场景成立,或 TC-5 证明重复加载达到需优化程度,并完成 TC-4。对比请求级 Worker 与候选的冷启动、暖请求、空闲内存和取消成本;若为流式,验证连续音频、结果提交和停止语义。没有测量或实际需求时继续保留请求级 Worker |
|
||||
|
||||
测量记录应包含 OS/架构、Electron 与依赖版本、模型版本、硬件、输入时长/页数/token 与批量大小、冷暖条件、请求频率和并发使用方。使用固定可重复输入,报告样本数、分布及异常,不填入未经测量的性能收益。后续验收覆盖受影响的 Windows、macOS、Linux 构建;尚未测的平台明确保留为待验。
|
||||
|
||||
本次证据为源码和已安装依赖入口检查。后续执行待办时,在所属功能进度文档记录命令、环境、真实路径结果及遗留项,并引用 TC 编号;若迁移条件未成立,记录保留现状的原因即可。
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
1. “平台功能”保留现有一级设置分类,在分类内部增加共享 `PageTabs`:
|
||||
- **通用设置**
|
||||
- **魔法笔记**
|
||||
- **远程项目**
|
||||
2. “通用设置”首个功能为“模型下载源”。
|
||||
3. 首期提供两个互斥选项:
|
||||
- `modelscope`,用户文案为 **ModelScope**,默认值。
|
||||
@@ -49,7 +49,7 @@
|
||||
下载使用新来源。
|
||||
9. 当前来源失败时,不自动重试另一个来源。错误可以提供“前往通用设置更换下载源”,由
|
||||
用户明确选择。
|
||||
10. 现有魔法笔记设置只移动到“魔法笔记”页签,不改变保存、启用或评论行为。
|
||||
10. 魔法笔记设置由应用中心与笔记页共享,不在全局设置中重复提供入口。
|
||||
|
||||
---
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
“平台功能”现已使用两个二级页签:
|
||||
|
||||
- “通用设置”承载全局模型下载源。
|
||||
- “魔法笔记”承载显示入口、AI 评论方式和 AI 评论形式。
|
||||
- “远程项目”承载远程项目开关与 Agent 包管理。
|
||||
|
||||
页签复用共享 `PageTabs`,默认打开“通用设置”,不会把当前页签写入应用设置。
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
- 切换来源时不影响已安装、已选择或正在使用的模型。
|
||||
- 下载失败时知道实际使用了哪个来源。
|
||||
- 所选来源没有模型时得到明确说明,并可以主动前往设置切换。
|
||||
- 在“平台功能”中快速区分通用设置与魔法笔记。
|
||||
- 在“平台功能”的“通用设置”中选择模型下载源。
|
||||
|
||||
### 3.2 产品目标
|
||||
|
||||
@@ -166,10 +166,9 @@
|
||||
└─ 平台功能
|
||||
├─ 通用设置
|
||||
│ └─ 模型下载源
|
||||
└─ 魔法笔记
|
||||
├─ 显示魔法笔记入口
|
||||
├─ AI 评论方式
|
||||
└─ AI 评论形式
|
||||
└─ 远程项目
|
||||
├─ 远程项目开关
|
||||
└─ Agent 包管理
|
||||
```
|
||||
|
||||
分类页只有一个一级标题“平台功能”。页签标题不重复渲染为第二个页面标题;每个面板内部
|
||||
@@ -186,7 +185,7 @@
|
||||
onChange={setActiveSection}
|
||||
tabs={[
|
||||
{ id: 'general', label: '通用设置' },
|
||||
{ id: 'magic-notes', label: '魔法笔记' }
|
||||
{ id: 'remote-projects', label: '远程项目' }
|
||||
]}
|
||||
value={activeSection}
|
||||
variant="segmented"
|
||||
@@ -214,7 +213,7 @@
|
||||
|
||||
未来新增页签时:
|
||||
|
||||
- 必须与“通用设置”和“魔法笔记”处于同级。
|
||||
- 必须与“通用设置”和“远程项目”处于同级。
|
||||
- 页签表示完整、独立的功能设置域。
|
||||
- 通用设置只放跨功能的全局行为,不成为任意设置的杂物区。
|
||||
- 页签超过可读数量时重新组织信息架构,不允许多行堆叠。
|
||||
@@ -230,7 +229,7 @@
|
||||
平台功能
|
||||
管理通用平台行为与可选工作区能力
|
||||
|
||||
[ 通用设置 ] [ 魔法笔记 ]
|
||||
[ 通用设置 ] [ 远程项目 ]
|
||||
|
||||
┌ 模型下载源 ────────────────────────────────────────────┐
|
||||
│ 选择 GoodBuddy 托管本地模型后续下载使用的平台。 │
|
||||
@@ -304,28 +303,11 @@
|
||||
|
||||
---
|
||||
|
||||
## 8. 魔法笔记页签
|
||||
## 8. 魔法笔记设置边界
|
||||
|
||||
现有魔法笔记卡完整移动到“魔法笔记”面板:
|
||||
|
||||
- 显示魔法笔记入口。
|
||||
- AI 评论方式。
|
||||
- AI 评论形式。
|
||||
|
||||
保持以下行为不变:
|
||||
|
||||
- 开关继续使用共享 Switch 和 `role="switch"`。
|
||||
- 评论方式和形式继续使用 `SegmentedControl`。
|
||||
- 每项继续即时保存。
|
||||
- 保存失败保留最后确认设置。
|
||||
- `onMagicNotesEnabledChange` 继续更新应用导航入口。
|
||||
|
||||
页签重构不能:
|
||||
|
||||
- 重置现有设置。
|
||||
- 在切换页签时保存或改变值。
|
||||
- 因魔法笔记入口关闭而隐藏“魔法笔记”设置页签。
|
||||
- 把页签选择误当成启用开关。
|
||||
全局设置不再提供魔法笔记页签或跳转入口。应用中心与笔记页继续共享同一表单和配置,
|
||||
保留已有启用、待办数量显示、评论方式、评论形式及业务数据。共同设置的交互与同步规则见
|
||||
[应用导航 UI 设计](../features/application-tool-navigation/ui-design.md)。
|
||||
|
||||
---
|
||||
|
||||
@@ -899,7 +881,7 @@ Ollama 的模型下载由用户和 Ollama 管理。GoodBuddy 的模型下载源
|
||||
- 默认选中“通用设置”Tab。
|
||||
- Tab 使用 `tablist`、`tab`、`tabpanel` 和 `aria-selected`。
|
||||
- 方向键、Home、End 和焦点恢复正确。
|
||||
- 切换到魔法笔记不改变任何设置。
|
||||
- 切换平台功能页签不改变任何设置,全局设置不显示魔法笔记入口。
|
||||
- Radio Group 具有持久 Label。
|
||||
- 选择来源时保存一次,保存期间防止重复提交。
|
||||
- 保存失败恢复原值并保留可重试错误。
|
||||
@@ -954,7 +936,7 @@ Ollama 的模型下载由用户和 Ollama 管理。GoodBuddy 的模型下载源
|
||||
- 将应用设置版本升级并迁移旧版本。
|
||||
- 在“平台功能”中增加 `PageTabs`。
|
||||
- 增加“通用设置”与模型下载源 Radio Group。
|
||||
- 把现有魔法笔记卡移动到“魔法笔记”面板。
|
||||
- 魔法笔记卡已迁入应用共同设置,全局页签已移除,见第 8 节。
|
||||
|
||||
### 阶段 2:共享目录和来源解析(已完成)
|
||||
|
||||
@@ -983,14 +965,14 @@ Ollama 的模型下载由用户和 Ollama 管理。GoodBuddy 的模型下载源
|
||||
|
||||
### 19.1 产品
|
||||
|
||||
- “平台功能”显示“通用设置”和“魔法笔记”两个可访问页签。
|
||||
- “平台功能”显示“通用设置”和“远程项目”两个可访问页签。
|
||||
- 默认打开“通用设置”。
|
||||
- “通用设置”提供 ModelScope 和 Hugging Face,默认 ModelScope。
|
||||
- 来源选择说明影响范围,并明确已安装模型、ZIP、Ollama 和应用更新不受影响。
|
||||
- 切换来源不会下载、删除、切换或重新验证任何已安装模型。
|
||||
- 当前来源没有模型时,在点击下载前即可看到不可用原因。
|
||||
- 下载失败不会自动请求另一来源。
|
||||
- 魔法笔记的现有设置和入口行为保持不变。
|
||||
- 魔法笔记设置由应用中心与笔记页共享,全局设置不再提供笔记入口;已有配置值和业务数据保持不变。
|
||||
|
||||
### 19.2 数据与下载
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# Runtime Storage And Background Reads
|
||||
|
||||
SQLite owns persisted task, event and conversation evidence. The desktop business
|
||||
connection is long-lived. WAL permits readers alongside a writer; synchronous SQL
|
||||
still blocks the thread that executes it. Memory caches are disposable derived data.
|
||||
|
||||
Startup storage upgrades run in `assistant-storage-worker` before the business
|
||||
connection opens. Runtime readers must not invoke initialization, migrations or
|
||||
recovery. `SubagentProgressStorage` caches event encoding state, not task statistics.
|
||||
|
||||
## Runtime Read Design
|
||||
|
||||
Execution statistics use one lazily started worker with a read-only connection.
|
||||
The worker performs the existing evidence query inside a short read transaction.
|
||||
Main supplies the current reply lease IDs and receives only scalar summaries.
|
||||
There is no worker pool or duplicate persisted summary. The worker is terminated
|
||||
when its owning database closes; errors reject reads, without a synchronous Main
|
||||
fallback. In-memory test databases use the synchronous query because independent
|
||||
connections cannot access the same private in-memory database.
|
||||
|
||||
Derived caches must specify scope, capacity, lifetime and invalidation. Statistics
|
||||
keep at most eight scopes for 30 seconds. SQLite `data_version` detects other
|
||||
connections' commits; `total_changes()` detects writes on the owning connection.
|
||||
Reads inside an existing transaction clear and bypass the cache, so rolled-back
|
||||
values cannot escape. Closing the connection clears its cache. Live leases bypass
|
||||
the cache because elapsed time changes without a database write.
|
||||
|
||||
Read transactions end before replies are posted. Long-lived read transactions
|
||||
would prevent WAL checkpoints from reclaiming old pages. Each reader has its own
|
||||
SQLite page cache; adding workers increases memory usage and requires evidence of
|
||||
parallel query demand. New consumers should reuse the same lifecycle and read-only
|
||||
rules, without introducing a general query protocol before a second use case exists.
|
||||
|
||||
## Renderer Scheduling
|
||||
|
||||
Hidden task panels use React `Activity` to preserve state while scheduling hidden
|
||||
render work below visible updates. Panel factories run inside that boundary.
|
||||
Terminal and browser panels retain their existing mounted effects and sessions.
|
||||
Statistics refresh only when the task center and document are visible. Results
|
||||
must remain scoped and late responses from a previous selection must be discarded.
|
||||
|
||||
The [task statistics design](../features/task-and-job/technical-design.md) owns the
|
||||
query contract and evidence rules. Tests and timings demonstrate only their fixture
|
||||
conditions; they do not establish a latency guarantee for arbitrary retained history.
|
||||
@@ -0,0 +1,137 @@
|
||||
# Desktop 0.13.12 and Agent 0.13.3 Preparation
|
||||
|
||||
Prepared on 2026-09-23 on `main`. This record describes a local candidate, not a
|
||||
published release. Desktop notes are in `resources/release-notes.json`; Agent
|
||||
notes are in `resources/agent-release-notes/0.13.3.md`. Both are bilingual drafts
|
||||
awaiting approval with the exact candidate commit before tagging.
|
||||
|
||||
## Scope
|
||||
|
||||
- Desktop baseline: `v0.13.11`, including all nine subsequent commits through
|
||||
`83a48a4` and the reviewed working-tree changes.
|
||||
- Agent baseline: `agent-v0.13.2`. The Agent changes reclaim idle Runtimes and
|
||||
drain displaced Agents; the Desktop Continue path restores conversation
|
||||
history when reopening a Runtime.
|
||||
- Desktop package and both root lockfile versions are `0.13.12`.
|
||||
`agent-runtime-lock.json` is `0.13.3`. Agent package tooling derives the minimum
|
||||
Desktop version from `package.json`. Node, OpenCode, and Continue locks are
|
||||
unchanged at `24.19.0`, `1.18.29`, and `1.5.47`.
|
||||
- Both feature matrices were reviewed and updated for versions, Supervisor
|
||||
default-off behavior, implemented reviews/graphs/activity/incremental input,
|
||||
remaining supervision work, and release acceptance limits.
|
||||
|
||||
## Local Validation
|
||||
|
||||
`npm run release:notes:verify`, `npm run typecheck`, and `npm run lint` passed
|
||||
after the final source changes. Focused runs cover 35 distinct test files and
|
||||
690 passing tests; 403 tests were excluded by the IPC/App name filter.
|
||||
|
||||
| Group | Files | Passing Tests |
|
||||
| --- | ---: | ---: |
|
||||
| Supervisor, settings, bridge, and release-note suites passing in the initial run | 14 | 157 |
|
||||
| Database, migration, activity I/O, note storage, menu, and sidebar suites after fixes | 6 | 209 |
|
||||
| IPC/App filtered by `supervision\|Supervisor\|heartbeat\|application enable\|graph navigation` | 2 | 16 |
|
||||
| Agent CLI, daemon, Runtime backend, Desktop ACP, transport, and connection manager | 7 | 207 |
|
||||
| Usage, duration, workspace, Markdown, and Supervisor Electron layout | 6 | 101 |
|
||||
|
||||
The initial focused run had 25 failures: 22 synthetic legacy database fixtures
|
||||
retained schema-46 objects, two menu assertions assumed Supervisor was enabled,
|
||||
and one browser close retry left its earlier error visible. Fixtures now remove
|
||||
the new objects before simulating older versions, menu tests follow the saved
|
||||
enabled preference, and a successful browser close clears its scoped error.
|
||||
All affected suites passed on rerun. Production migration behavior was not
|
||||
changed to accommodate synthetic fixtures.
|
||||
|
||||
The Electron layout test uses production components with isolated fixtures,
|
||||
including 1440, 1024, and 390 pixel widths and light/dark themes. It is not a
|
||||
packaged-app probe or a real-user database acceptance test.
|
||||
|
||||
No real model requests were made during this preparation. Existing development
|
||||
evidence records eight requests on the shared Linux x64 Host for remote lifecycle
|
||||
checks and three for incremental review. The remote evidence does not repeat
|
||||
Continue real-Host validation, the complete signed installation flow, or the
|
||||
native architecture matrix. See
|
||||
[remote lifecycle evidence](../features/remote-host/technical-design.md#生命周期验证2026-09-23)
|
||||
and [incremental review evidence](../features/conversation-supervision/progress.md#2026-09-23-自动增量验证).
|
||||
|
||||
## Pending Release Work
|
||||
|
||||
The user requested no further full local suite. Full main-branch CI validation
|
||||
and its production build remain required before tagging; native Desktop and
|
||||
Agent packaging and publication verification remain pending. No local production
|
||||
build or package was run. No push, tag, remote synchronization, or publication
|
||||
was performed during preparation. Approval must identify the exact candidate
|
||||
commit and both bilingual note files before either release tag is created.
|
||||
|
||||
## Approved Candidate CI Attempt
|
||||
|
||||
The user approved `bf8ece05902509411454b27b742ebfec930005ad` and both bilingual
|
||||
note files. On 2026-09-23 that exact commit was fast-forwarded to `main` on
|
||||
`origin` and `github`; both remote refs were verified. GitHub SSH stalled, so
|
||||
the GitHub push used HTTPS with the existing GitHub CLI keyring credentials.
|
||||
No persistent Git configuration was changed. GitHub reported the account's
|
||||
existing pull-request-rule bypass for this requested direct push.
|
||||
|
||||
- [Desktop main CI](https://github.com/mesalogo/goodbuddy/actions/runs/35849362694)
|
||||
failed in the full suite: 4 files failed, 418 passed, 13 skipped; 10 tests
|
||||
failed, 4,997 passed, 43 skipped. Type checks, lint, and the production build
|
||||
did not execute after the test failure.
|
||||
- [Agent main CI](https://github.com/mesalogo/goodbuddy/actions/runs/35849362799)
|
||||
passed source validation and native Linux x64, Linux arm64, and Darwin arm64
|
||||
deterministic compound-package verification with ephemeral test signatures.
|
||||
- Neither release tag was created or pushed. No Desktop or Agent publication
|
||||
workflow was started, and no production release assets were verified.
|
||||
|
||||
The follow-up changes are limited to tests. Two IPC tests clear the startup
|
||||
settings-read count before measuring interactive request lookups. The settings
|
||||
failure UI test now expects Supervisor to remain disabled. OpenCode isolation
|
||||
tests prepare their real configuration dependencies in a dedicated temporary
|
||||
directory instead of relying on a prior local package preparation; connection
|
||||
failures now include their diagnostic detail. The large activity-list and
|
||||
multi-page navigation and external knowledge binding tests have explicit
|
||||
15-second budgets; the binding test also waits up to five seconds for its
|
||||
resulting heading.
|
||||
Its snapshot fixture now selects by library ID, and its instance list remains
|
||||
available during polling instead of assuming a fixed number of asynchronous
|
||||
reads. Both mocks reset between tests; the binding test waits for the
|
||||
selected-library read.
|
||||
These budgets address timeouts observed in the full CI run without removing
|
||||
assertions or changing product behavior.
|
||||
|
||||
The approved source and bilingual notes remain unchanged. These test changes
|
||||
need a revised candidate commit and approval before another release attempt;
|
||||
the failed commit must not be tagged. No local production build, package,
|
||||
external model request, or LoongArch build was performed.
|
||||
|
||||
Follow-up validation: the seven selected IPC/UI regression cases and all seven
|
||||
OpenCode isolation cases passed locally. Type checks and lint passed. Full
|
||||
remote CI and the production build still require a new approved commit; these
|
||||
focused results do not satisfy that release gate.
|
||||
|
||||
## Linux Executable Path Follow-up
|
||||
|
||||
On 2026-09-23, local `main` was fast-forwarded from `163e245` to
|
||||
`073aa45a30efe99fab7a92f8aee2a36087db9c2a` after inspecting the four intervening
|
||||
commits. They update only the community QR codes in the two README files.
|
||||
The user authorized retaining these changes, fixing CI, pushing both remotes,
|
||||
and publishing Desktop `v0.13.12` and Agent `agent-v0.13.3` after successful
|
||||
candidate CI without another approval round. No LoongArch preview is requested.
|
||||
|
||||
[Desktop CI 35854792610](https://github.com/mesalogo/goodbuddy/actions/runs/35854792610)
|
||||
reported 5,004 passing tests, three failures, and 43 skipped tests. All three
|
||||
failures were in the real OpenCode isolation test. It constructed
|
||||
`opencode-ai/bin/opencode` on Linux, but OpenCode 1.18.29 installs its npm
|
||||
executable as `bin/opencode.exe` on every platform. The test now reuses
|
||||
`resolveBundledRuntimePaths`, the existing production resolver, instead of
|
||||
constructing a platform-specific filename. Production Runtime code and both
|
||||
release-note files are unchanged.
|
||||
|
||||
Local follow-up validation passed all nine tests in
|
||||
`opencode-selection-isolation.test.ts` and `bundled-runtimes.test.ts`, including
|
||||
the existing Linux development-path assertion, plus full type checks and lint.
|
||||
Release-note and package/lockfile version validation passed. The isolation
|
||||
tests made two requests to their local mock endpoint and zero real model
|
||||
requests. No full local suite, production build, package, or real-Host check
|
||||
was run for this test-only fix; it changes neither deployed Agent code nor
|
||||
the Desktop-to-Agent production path. Successful Linux main-branch CI and
|
||||
its production build remain required before tagging.
|
||||
@@ -0,0 +1,37 @@
|
||||
# Desktop 0.13.13 Preparation
|
||||
|
||||
Prepared on 2026-09-23 from `b16fc90`, relative to published Desktop `v0.13.12`.
|
||||
This is a Desktop-only release; Agent remains at published `0.13.3`.
|
||||
No LoongArch preview is requested.
|
||||
|
||||
## Scope and isolation
|
||||
|
||||
- `bf8ff7f` reduces repeated activity-history reads during streaming saves.
|
||||
- `b16fc90` restores compact supervision feedback controls and target names.
|
||||
- The candidate is prepared in a separate worktree. The user's uncommitted
|
||||
supervision timeout tests in the original worktree are explicitly excluded.
|
||||
- Desktop and both root lockfile versions are `0.13.13`. Both feature matrices
|
||||
and bilingual release notes were reviewed against this range.
|
||||
- Database schema and Agent/Runtime contracts are unchanged. The activity
|
||||
comparison cache belongs to Desktop persistence; it does not modify the
|
||||
deployed Agent, model bridge, or remote Runtime lifecycle.
|
||||
|
||||
## Validation
|
||||
|
||||
- Six focused files passed 192 tests: activity-history I/O, AssistantDatabase,
|
||||
storage upgrades, sidebar interactions, SupervisionCard, and the Supervisor
|
||||
Electron layout fixture.
|
||||
- Re-running `tests/supervisor-layout.electron.test.ts` with
|
||||
`GOODBUDDY_SUPERVISOR_SIDEBAR=1` passed its additional sidebar scenario:
|
||||
480, 300, and 200 pixel widths in both themes, no horizontal overflow,
|
||||
compact 34px buttons, visible keyboard focus, and no raw target ID in body text.
|
||||
- `npm run release:notes:verify`, `npm run typecheck`, and `npm run lint` passed.
|
||||
- Ten consecutive activity saves performed one full-history scan. WAL and SQL
|
||||
counters are not installed-application physical disk throughput measurements.
|
||||
- No real model requests, local full-suite rerun, production build, packaging,
|
||||
or packaged-app probe were performed. UI validation uses production
|
||||
components with isolated fixtures, not a real user database.
|
||||
|
||||
The candidate must pass complete main-branch CI and its production build before
|
||||
an unused annotated `v0.13.13` tag is pushed to both remotes. Native six-platform
|
||||
packaging and public GitHub/OSS verification remain subsequent release steps.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Desktop 0.13.14 / Agent 0.13.4 发布准备
|
||||
|
||||
## 范围与审批
|
||||
|
||||
- 用户确认从 0.13.13 升至 0.13.14,现有七个测试修改及三个验证辅助文件一起提交。
|
||||
- 不构建实验性 LoongArch 预览,不运行本地生产构建、打包或安装包启动探针。
|
||||
- 准备起点为 `ff746bdc1add6a12227ccb61a7794f654aaaa8ba`。
|
||||
- 最近可达稳定标签为 `v0.13.12`。已公开发布的 `v0.13.13` 指向
|
||||
`7756231`,其发布准备提交不在当前 main 祖先链上;两项产品修复已包含,
|
||||
0.13.13 的包内发布说明也已保留。需同时核对两个范围,不重复宣传已发布修复,
|
||||
不移动标签或重写历史。
|
||||
- 用户随后确认 Agent 也末位加一,目标为 0.13.4。已批准联合发布的双语说明,
|
||||
以及验证通过后一起提交本地候选;最终候选 SHA 仍须另行确认。
|
||||
- Agent 范围为 `agent-v0.13.3..HEAD`,生产改动来自 `7adae97`。
|
||||
- 用户批准提交并推送两个远端、等待 CI;标签仍待精确提交确认。
|
||||
- Fetch 确认两个远端 main 均为 `7756231b3bdfa148f1b2702e4ff3fc3bd0cb057e`。
|
||||
用户批准以普通合并保留双方历史,保留本次版本和已批准说明,
|
||||
补回历史发布文档;不强推、不改动已有标签。
|
||||
- 本地候选提交为 `d23af99`。普通合并解决后,仅新增或修改文档;
|
||||
产品源码、测试、依赖、版本文件及两份已批准说明与该候选完全一致。
|
||||
合并后 `v0.13.13` 恢复为可达发布基线,先前祖先链差异仅记录准备起点状态。
|
||||
|
||||
## 交付与数据边界
|
||||
|
||||
- 本轮联合准备 Desktop 0.13.14 和独立 Agent 0.13.4,不覆盖历史 Agent 包。
|
||||
- 桌面包含远程结果未知后显式发送新消息的恢复修复;Agent 完成确认、
|
||||
空闲回收失败处理和旧安装清理随 0.13.4 交付。
|
||||
- Agent 发布流程读取本次 package.json 作为最低 Desktop 版本,因此需先升级
|
||||
Desktop 至 0.13.14,再下载并更新 Host。Node、OpenCode、Continue 锁定版本不变。
|
||||
- 两个候选分别使用 `v0.13.14`、`agent-v0.13.4`,Agent 不替代 Desktop Latest。
|
||||
- 数据库从 schema 46 升至 47。升级前需完整备份用户数据,包括数据库和
|
||||
相邻 notes/ 目录;旧客户端不能打开升级后的数据库。
|
||||
- 已检查中英文功能矩阵,更新当前版本、监督回顾、待办能力及独立 Agent
|
||||
交付边界。其他平台和功能的既有未验收事项继续保留。
|
||||
|
||||
## 验证
|
||||
|
||||
- 首次 `npm test`:5,125 项通过、74 项跳过、2 项失败。失败均为版本已更新而
|
||||
说明尚未获批写入导致的当前版本缺失,未修改断言规避。
|
||||
- 联合说明写入后的 `npm test`:435 个文件通过、10 个文件跳过;
|
||||
**5,127 项通过、74 项跳过,0 失败**,耗时 685.49 秒。
|
||||
- `npm run typecheck`、`npm run lint`:首轮与联合候选复验均通过。
|
||||
- `npm run release:notes:verify`:联合说明写入后通过;Desktop 三处版本一致,
|
||||
当前说明仅一项,Agent lock 与独立双语标题一致。
|
||||
- 合并后复验:发布说明校验、9 项发布说明测试、完整 typecheck、lint、
|
||||
`git diff --check` 通过。因合并仅恢复文档,未重复运行完整测试和 Host 探针。
|
||||
- 当前源码 Linux x64 完成/历史恢复探针通过:两轮 Ask 均完成,Runtime 重建后
|
||||
保留会话历史,每轮结束及最终退出时所属进程数均为 0。
|
||||
- 隔离旧安装清理探针通过:真实 CLI 回收所属孤儿进程、删除对应旧 payload,
|
||||
保留存活旧 daemon、活动工作、历史及 current/candidate,重复清理无重复删除。
|
||||
- 本轮新增真实模型调用 **2 次**,模拟模型调用 0 次;清理探针模型调用 0 次。
|
||||
- 两个探针从当前源码重新生成临时测试脚本,仅操作共享 Linux x64 Host 的独立
|
||||
`/root/tmp/gb-lifecycle-*` 与 `/root/tmp/gb-upgrade-*` 测试目录,不替换共享安装。
|
||||
探针沿用历史测试身份版本,不是正式签名 0.13.4 包安装验收,也不代替
|
||||
Linux arm64、macOS arm64 原生 CI 或完整 Host 更新界面验证。
|
||||
- 复现命令沿用[远程 Host 技术设计](../features/remote-host/technical-design.md)
|
||||
中 lifecycle-host 和 upgrade-cleanup 脚本;本轮生命周期参数为
|
||||
`driver --completion-only`,使用真实模型而非 `--fake-model`。
|
||||
- 候选主分支 CI、生产构建、六平台原生打包及公开发布验证:尚未执行。
|
||||
@@ -10,6 +10,23 @@ from the authoritative GitHub, OSS, workflow, and Git state, then use the
|
||||
smallest existing idempotent rerun or a new immutable version as specified
|
||||
below.
|
||||
|
||||
## Optional PR Article
|
||||
|
||||
Product publicity is an optional companion to a release, not a release blocker
|
||||
or a replacement for the approved bilingual release notes. When requested,
|
||||
follow the [PR article workflow](../pr-articles/README.md) and choose either the
|
||||
[featured article](../pr-articles/README.md#重点稿模板) or
|
||||
[regular update](../pr-articles/README.md#常规与综合稿模板) template based on user value,
|
||||
not version size or commit count.
|
||||
|
||||
Prepare the draft and real screenshots against the candidate commit using the
|
||||
[isolated screenshot procedure](../pr-articles/README.md#截图执行步骤). This does
|
||||
not waive the build, packaging, or launch restrictions below. Keep missing
|
||||
materials in the article's review record without delaying the software release.
|
||||
Before external publication, verify the final version, download links, upgrade
|
||||
notes, and screenshot accuracy. Preparing materials does not authorize posting
|
||||
to a public channel; follow the user's explicit publication instruction.
|
||||
|
||||
## Release Packaging
|
||||
|
||||
- Unless the user explicitly requests it, never run a local production build,
|
||||
@@ -27,6 +44,11 @@ below.
|
||||
- `.github/workflows/packages.yml` is the canonical cross-platform packaging
|
||||
workflow. It validates and builds `out` once, then packages on six native
|
||||
runners: Windows, macOS, and Linux, each for x64 and arm64.
|
||||
- Linux source validation configures the installed Electron `chrome-sandbox`
|
||||
helper with root ownership and mode `4755`, rehydrates the Electron
|
||||
distribution without cache when the helper is missing, then runs the full
|
||||
test suite under Xvfb. Keep Electron sandboxing enabled; do not skip UI tests
|
||||
or add `--no-sandbox` to work around runner setup failures.
|
||||
- The experimental Linux LoongArch (`loong64`) preview is built separately
|
||||
through `build/loongarch-cross` by following
|
||||
`docs/development/loongarch-preview-build.md`. It is not part of
|
||||
@@ -62,7 +84,7 @@ below.
|
||||
Host.
|
||||
- Each independently published Linux x64, Linux arm64, or macOS arm64 `.gbagent` contains the
|
||||
Agent daemon, pinned Node, and the desktop-maintained compatible OpenCode
|
||||
Runtime. Build all three targets on native GitHub Actions runners from one
|
||||
and Continue Runtimes. Build all three targets on native GitHub Actions runners from one
|
||||
immutable `agent-v<agentVersion>` tag.
|
||||
- Keep the GoodBuddy Agent source, shared protocol/contracts, runtime lock,
|
||||
bundle tooling, and tests in this repository so a desktop commit identifies
|
||||
@@ -72,7 +94,7 @@ below.
|
||||
- `.github/workflows/agents.yml` is the branch and pull-request build
|
||||
verification workflow for GoodBuddy Agent. It must build Linux x64 and
|
||||
arm64 on native runners from the checked-out commit, acquire the locked
|
||||
official Node archive and locked OpenCode npm archive, verify every locked
|
||||
official Node archive and locked OpenCode and Continue npm archives, verify every locked
|
||||
digest, use only an ephemeral in-memory test signing identity, and verify the
|
||||
resulting compound package and deterministic archive. It must not read
|
||||
production signing secrets, publish installable release artifacts, or modify
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
# 应用导航与系统工具入口
|
||||
|
||||
本功能整理 GoodBuddy 主侧栏的入口层级:系统入口保持固定,内置应用通过最底部应用中心
|
||||
访问,并可常驻主导航;系统工具由右侧工作栏 Tab 承载。应用商店是后续功能,首期不提供
|
||||
本功能整理 GoodBuddy 主侧栏的入口层级:系统入口保持固定,四个内置应用共用可调整的保存顺序,知识库始终启用并常驻;
|
||||
智能心跳、魔法笔记和本机推理监控可通过应用管理控制启用和常驻。底部应用中心向上展开轻量锚定菜单,列出已启用应用;底部“管理应用”打开既有管理 Modal。系统工具由右侧工作栏 Tab 承载。应用商店是后续功能,首期不提供
|
||||
商店入口、推荐位或安装流程。
|
||||
|
||||
当前分期见 [PRD 第 8 节](./prd.md#8-分期),实施步骤见[技术设计第 11 节](./technical-design.md#11-实施顺序)。
|
||||
本轮更新实施计划与范围,旧版功能要求、验收和配套设计仍需按新计划同步,此项列为实现前的第一步。
|
||||
本轮 A–D 已接入生产源码:应用菜单、应用管理 Modal、共享应用设置、持久化偏好及本机推理监控。菜单不按打开历史或常驻状态筛选,无模态遮罩;点击应用行关闭菜单,知识库、智能心跳和魔法笔记打开主内容区页面。本机推理监控从菜单、常驻入口或管理 Modal 打开时均使用独立 Modal,保留底层工作区。管理列表、菜单和侧栏按同一保存顺序排列,侧栏按启用和常驻过滤。知识库在管理 Modal 中提供“打开”和排序,不提供启用、常驻开关或通用空设置;原工作区配置及心跳单条计划 enabled 保持原义。验证范围及集成缺口见[实施进度](./progress.md)。
|
||||
|
||||
新增计划包括 [私有化市场与 YAML 交换](./prd.md#fr-15-私有化市场与-yaml-交换后续):
|
||||
通过市场 URL 获取应用,支持 `.yml` 导入导出,未来由 ShareServer 提供私有目录与分发。
|
||||
[本机模型管理入口](./prd.md#fr-16-本机模型管理入口新增计划项)可独立于商店交付,拟作为
|
||||
可常驻左侧的内置管理应用,区分受管进程、外部服务和进程内模型。两项均为需求记录,
|
||||
未实现,相关后续验收见 [User Stories](./user-stories.md#7-后续需求场景)。
|
||||
[本机模型管理入口](./prd.md#fr-16-本机模型管理入口新增计划项)已独立于商店接入,作为
|
||||
可常驻左侧的“本机推理监控”(英文 Local Inference Monitor)内置应用,直接查看语音、OCR、向量等本机服务状态与可用资源指标,不显示任务历史。向量服务显示独立进程的 CPU 和工作集内存;ASR、OCR 与宿主共享进程,逐行说明无法独立统计的原因。
|
||||
本轮已接入 `local-inference` 管理 Modal、类型化 IPC、真实请求登记及引擎管理;实际支持清单见
|
||||
[技术设计](./technical-design.md#111-本机推理任务与执行服务),市场仍为后续需求。
|
||||
相关验收见 [User Stories](./user-stories.md#7-后续需求场景)。
|
||||
|
||||
项目活动汇总独立于上述应用中心计划,范围见 [FR-14](./prd.md#fr-14-项目活动汇总)。
|
||||
计数规则由[活动逻辑](./logic-design.md#9-项目活动汇总)定义,入口与级联菜单见
|
||||
[活动界面](./ui-design.md#10-项目活动汇总),数据加载与会话跳转见
|
||||
[技术设计](./technical-design.md#12-项目活动汇总)。此处“活动”指当前运行或待处理会话,
|
||||
与用于审计的“运行记录”不同;已完成未读面板属于后续范围,本次未实现。
|
||||
[技术设计](./technical-design.md#12-项目活动汇总)。此处“活动”包括当前运行、待处理及本次客户端
|
||||
会话内已完成但未查看的会话;完成提示在实际进入聊天后清除,不累计历史完成总数。
|
||||
|
||||
## 文档导航
|
||||
|
||||
现有 `heartbeat` 入口承载监督者工作回顾与故事线图谱,可管理启用、常驻和排序。应用默认关闭;完整执行边界见[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)。未来市场复用同一应用身份,不增加平级重复入口,目标定义见[监督者产品设计](../conversation-supervision/supervisor-prd.md)。
|
||||
|
||||
| 文档 | 权威职责 |
|
||||
| --- | --- |
|
||||
| [PRD](./prd.md) | 用户问题、产品边界、功能要求与产品验收 |
|
||||
@@ -28,14 +32,15 @@
|
||||
| [功能逻辑设计](./logic-design.md) | 分类规则、入口状态、优先级与失败结果 |
|
||||
| [UI 设计](./ui-design.md) | 侧栏信息架构、控件、文案、键盘和响应式行为 |
|
||||
| [技术设计](./technical-design.md) | 现有模块映射、状态契约、持久化和实施顺序 |
|
||||
| [实施进度](./progress.md) | 项目活动级联的生产实现与验证记录 |
|
||||
| [实施进度](./progress.md) | 应用中心、设置及项目活动级联的实现与验证记录 |
|
||||
| [应用中心交互 Demo](./navigation-demo.html) | 对比六个候选方案;配套 CSS 和 JS 仅服务原型 |
|
||||
|
||||
Demo 可直接在浏览器中打开,支持方案切换、浅深主题、添加、移除、排序和商店模拟。
|
||||
六个方案共用本次页面会话内的应用配置,刷新后恢复默认。商店、资源指标和工具内容均为
|
||||
演示数据,不执行真实安装或本机操作。窄屏上下排列侧栏与输入区,桌面展示完整应用布局。
|
||||
这些方案用于讨论最底部应用中心;其中商店页、商店应用安装与 F 方案属于未来交互参考,
|
||||
不纳入首期实现或验收。首期产品只提供内置应用;Demo 暂保留远期示例以便后续讨论。
|
||||
Demo 可直接在支持原生 dialog、popover 的现代浏览器中打开,无需依赖或构建。
|
||||
六个方案共用本次页面会话内的配置,刷新后全部恢复默认,不持久化或修改真实设置。
|
||||
A–E 仅展示内置应用,没有商店入口、推荐或安装操作;F 单独演示未来私有市场。
|
||||
模型服务、资源指标和工具内容均为演示数据,不执行真实安装、模型加载或进程操作。
|
||||
窄屏上下排列侧栏与主内容,应用页面隐藏输入区,对话页面显示输入区。
|
||||
Demo 的模型管理和 F 方案均使用模拟数据;正式本机推理监控 Modal 的交付范围以技术清单和实施进度为准,旧页面原型不作为 Modal 验收依据。
|
||||
底部输入区共用一个简化候选布局:附件、语音、Runtime、模式和发送位于主操作行,专家、
|
||||
知识范围及 Runtime 专属选项通过“选项”展开,非默认选择显示为可移除标签。可体验文件名
|
||||
附件、模拟语音、模板填入、原生命令选择、8 秒模拟回复、发送排队、立即插入、删除、停止,
|
||||
@@ -47,20 +52,38 @@ Demo 可直接在浏览器中打开,支持方案切换、浅深主题、添加
|
||||
进入会话层,并提供返回按钮。页面上方可切换“有待处理、仅运行中、仅当前项目运行、全部
|
||||
空闲”四种场景。项目与会话均为演示数据;该级联交互已接入正式产品活动入口,范围与
|
||||
验证记录见[实施进度](./progress.md)。
|
||||
按最新讨论,Demo 已移除左侧系统工具快捷区:文件、终端、浏览器和资源监控只在右侧
|
||||
工作栏 Tab 中演示,不列入应用中心。上方正式文档仍保留原设计,待方案确定后同步。
|
||||
Demo 已移除左侧系统工具快捷区:文件、终端、浏览器和资源监控只在右侧
|
||||
工作栏 Tab 中演示,不列入应用中心。旧原型仍直接打开整窗 Modal,其中使用可搜索应用列表。
|
||||
|
||||
Demo 待同步更正:本节仅描述旧原型,不是生产行为或验收依据。原型直接打开 Modal、知识库的启停设置,以及心跳默认启用和关闭仅隐藏入口的说明均已过时;原型的常驻排序也不代表生产中四个应用的共享顺序。生产规则见上述说明及 UI 设计。Demo 已同步“本机推理监控”名称及其默认不常驻状态。
|
||||
|
||||
六个方案共用“我的应用”区:通过 + 或“全部应用”选择常驻应用,编辑时可拖动或使用
|
||||
上下箭头排序、移除入口。未常驻的已安装应用仍可从“全部应用”打开。商店包含“项目管理”
|
||||
和“阅读空间”应用示例,支持仅安装或安装并常驻左侧菜单,常驻不表示后台运行。
|
||||
常驻应用在本次页面会话内跨方案保持一致;“恢复默认应用”重置安装与常驻配置。
|
||||
移除入口不会卸载应用或关闭当前页面,刷新后恢复默认。
|
||||
上下箭头排序、移除入口。内置应用默认全部启用;魔法笔记、知识库、智能心跳默认常驻,计划预览“本机推理监控”默认不常驻。
|
||||
取消常驻仅移除导航捷径,仍可从应用中心打开;关闭应用隐藏业务入口,应用中心保留设置与
|
||||
重新开启入口,并记住常驻偏好。当前应用页关闭后显示不可用状态,重新启用从应用中心卡片设置进入。
|
||||
知识库关闭时同步隐藏侧栏入口、会话选项中的知识范围和检索方式、已选知识标签;重新开启
|
||||
恢复原来的选择。以上操作不删除数据,也不表示停止后台任务或模型服务。
|
||||
“恢复默认应用”重置应用安装、启用、常驻和笔记设置,返回对话页;模型服务模拟状态保留至刷新。
|
||||
|
||||
魔法笔记提供应用中心“设置”、笔记页面“笔记设置”和全局设置演示入口,均打开同一设置
|
||||
面板,共用本次页面会话内的配置。面板沿用现有选项:显示未完成待办数量、AI 评论方式
|
||||
通用应用设置统一从应用中心卡片进入,应用页面和全局设置不提供重复入口,分别控制
|
||||
启用与常驻。魔法笔记面板另含显示未完成待办数量、AI 评论方式
|
||||
(即时/保存后自动/保存后手动)、AI 评论形式(长评 + 要点/长评/要点)。现有“显示
|
||||
魔法笔记入口”在原型中与“常驻左侧菜单”统一,不额外增加功能启停开关。侧栏待办数为
|
||||
固定的 3 项演示数据。修改即时生效于原型,不调用 AI 或写入真实应用设置。
|
||||
魔法笔记入口”不在原型中重复提供。正式迁移保留其业务启用值,常驻使用独立配置。启用、常驻、待办数量显示默认开启;
|
||||
评论方式默认即时,评论形式默认长评 + 要点。侧栏待办数为固定的 3 项演示数据。
|
||||
修改即时生效于原型,不调用 AI 或写入真实应用设置。
|
||||
|
||||
此 Demo 中的任务、进度、使用方与服务内存为旧方案模拟内容;正式 Modal 已收窄为服务状态列表,见 [FR-16](./prd.md#fr-16-本机模型管理入口新增计划项)。
|
||||
引擎和管理方式收进详情;受管进程模拟启动、停止、重启,进程内模型模拟加载与释放。
|
||||
服务停止或重启后任务标为中断,重新启动不自动恢复任务;任务级取消仍待实际引擎接入。
|
||||
影响使用方的操作先显示就地确认,取消保留原状态;隐藏或关闭管理应用不改变服务状态。
|
||||
|
||||
F 提供私有市场 URL、模拟获取目录、模拟不可达与重试,不发起网络请求或切换公共来源。
|
||||
目录包含项目管理和阅读空间示例,可模拟安装或安装并常驻。YAML 文本支持导入预览和导出
|
||||
预览,明确标为 mock schema,仅识别示例中的七个扁平字段,尚非通用 YAML 解析器。
|
||||
导入列出依赖、外部资源和同标识冲突:缺少依赖或资源时不允许安装;内置应用不允许覆盖;
|
||||
已安装扩展须显式确认更新定义,保留启用与常驻偏好。导入新应用在目录中以标识显示,原始名称
|
||||
保留在定义及导出预览中。修改输入会使旧预览失效,取消不修改安装状态。
|
||||
导出仅显示定义文本,不写文件,不包含凭据、会话、笔记、知识数据、程序包或模型权重。
|
||||
|
||||
| 方案 | 讨论重点 |
|
||||
| --- | --- |
|
||||
@@ -69,7 +92,7 @@ Demo 可直接在浏览器中打开,支持方案切换、浅深主题、添加
|
||||
| C:应用预览 | 用卡片说明应用用途 |
|
||||
| D:搜索启动台 | 搜索打开能力,支持 Ctrl / Command K、方向键与 Enter |
|
||||
| E:常驻管理 | 已常驻应用优先排列,可取消常驻或打开 |
|
||||
| F:商店发现(未来) | 默认展开模拟商店,不纳入首期 |
|
||||
| F:私有市场预览(未来) | URL 模拟获取、YAML 导入导出与显式模拟安装,不纳入首期 |
|
||||
|
||||
D 参考 [Raycast](https://www.raycast.com/) 的搜索启动与扩展发现方式。早期方案参考过
|
||||
Notion 收藏和 VS Code Profiles,当前版本已移除场景工具包与快捷区。
|
||||
@@ -81,7 +104,7 @@ Notion 收藏和 VS Code Profiles,当前版本已移除场景工具包与快
|
||||
| 术语 | 定义 |
|
||||
| --- | --- |
|
||||
| 系统入口 | GoodBuddy 工作流程或审计所必需的固定入口,用户不能卸载、隐藏或改变类型 |
|
||||
| 应用 | 在主内容区打开完整一级页面的功能,可显示在“当前对话”所在的主导航区 |
|
||||
| 应用 | 可显示在主导航区的功能;知识库、智能心跳和魔法笔记打开一级页面,本机推理监控打开保留底层工作区的 Modal |
|
||||
| 系统工具 | GoodBuddy 自带且不可卸载的辅助能力,包括文件、终端、浏览器和资源监控,由右侧助手工作栏 Tab 承载 |
|
||||
| 插件应用 | 未来由插件提供的一级页面入口 |
|
||||
| 插件工具 | 未来由插件提供的辅助面板或命令入口 |
|
||||
@@ -94,3 +117,4 @@ Notion 收藏和 VS Code Profiles,当前版本已移除场景工具包与快
|
||||
- [助手工作栏多终端页签 PRD](../assistant-workbar/terminal-tabs-prd.md)
|
||||
- [智能心跳 PRD](../smart-heartbeat/prd.md)
|
||||
- [知识库功能文档](../knowledge-base/README.md)
|
||||
- [文档解析入口与职责](../document-processing/ui-design.md#11-入口与职责):全局 OCR 配置、附件与知识文档操作的目标分工;不新增应用中心成员,本机推理监控不管理 HTTP OCR 服务。
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 状态 | 设计中 |
|
||||
| 版本 | 0.2 |
|
||||
| 日期 | 2026-09-08 |
|
||||
| 状态 | 应用中心与本机推理监控已接入源码,整体验收待完成 |
|
||||
| 版本 | 0.4 |
|
||||
| 日期 | 2026-09-17 |
|
||||
| 关联 PRD | [应用导航与系统工具入口 PRD](./prd.md) |
|
||||
| 关联 User Stories | [User Stories](./user-stories.md) |
|
||||
|
||||
@@ -15,9 +15,11 @@
|
||||
1. “当前对话”“运行记录”和“设置”始终是系统入口。
|
||||
2. 运行记录不进入工具注册表、应用显隐设置或插件市场。
|
||||
3. 文件浏览器、终端、浏览器和资源监控始终是系统工具,不能卸载或被插件替换。
|
||||
4. 左下角只提供快捷入口,工具内容仍由助手工作栏承载。
|
||||
5. 隐藏应用不等于停用功能,也不删除、迁移或清理业务数据。
|
||||
6. 快捷入口不能扩大执行空间、文件、PTY、网络或浏览器权限。
|
||||
4. 左下角提供应用中心和独立固定设置入口;文件、终端、浏览器、资源监控只通过右侧工作栏 Tab 和“+”访问,不进入应用中心。
|
||||
5. 四个内置应用共享保存顺序。知识库始终启用并常驻,位置可调整;智能心跳、魔法笔记、本机推理监控可独立设置启用和常驻。取消常驻不关闭功能,关闭不删除数据或导航偏好。
|
||||
6. 入口不能扩大执行空间、文件、PTY、网络或浏览器权限。
|
||||
7. 应用中心始终可达,点击向上展开无模态遮罩的锚定菜单,只列已启用应用,不使用打开历史或常驻过滤。菜单底部“管理应用”打开既有 Modal,关闭的可选应用在其中仍可搜索、配置和重新开启。
|
||||
8. 所有入口和新请求的功能判断读取同一份保存成功的权威设置;保存中的目标值不提前生效。
|
||||
|
||||
## 2. 入口模型
|
||||
|
||||
@@ -25,14 +27,21 @@
|
||||
type NavigationContribution =
|
||||
| {
|
||||
type: 'system-entry'
|
||||
id: 'chat' | 'activity' | 'settings'
|
||||
id: 'chat' | 'activity' | 'application-center' | 'settings'
|
||||
placement: 'primary-start' | 'primary-end' | 'footer'
|
||||
}
|
||||
| {
|
||||
type: 'application'
|
||||
id: 'magic-notes' | 'knowledge' | 'heartbeat'
|
||||
id: 'knowledge'
|
||||
source: 'builtin'
|
||||
visible: boolean
|
||||
alwaysShown: true
|
||||
}
|
||||
| {
|
||||
type: 'application'
|
||||
id: 'magic-notes' | 'heartbeat' | 'local-inference'
|
||||
source: 'builtin'
|
||||
enabled: boolean
|
||||
pinned: boolean
|
||||
}
|
||||
| {
|
||||
type: 'system-tool'
|
||||
@@ -41,7 +50,8 @@ type NavigationContribution =
|
||||
}
|
||||
```
|
||||
|
||||
该模型只覆盖首期入口。插件贡献模型随插件 Host、权限和生命周期另行设计。
|
||||
该模型是首期的逻辑投影,启用值来自各应用的唯一业务配置,常驻及顺序来自导航偏好。
|
||||
应用清单为魔法笔记、知识库、智能心跳、本机推理监控,打开无需安装。市场不加入本轮清单。
|
||||
|
||||
## 3. 主导航组合规则
|
||||
|
||||
@@ -49,35 +59,60 @@ type NavigationContribution =
|
||||
|
||||
```text
|
||||
当前对话
|
||||
可见的内置应用
|
||||
应用(按保存顺序;知识库始终显示,可选应用仅显示启用且常驻项)
|
||||
运行记录
|
||||
```
|
||||
|
||||
当前对话和运行记录不参与排序。首期内置应用默认顺序为魔法笔记、知识库、智能心跳;显隐
|
||||
设置只从中移除对应项,不改变固定入口的位置。
|
||||
当前对话、运行记录和设置不参与应用排序。默认应用顺序为知识库、智能心跳、魔法笔记、本机推理监控;魔法笔记默认常驻,本机推理监控默认不常驻。过滤可选项不留下空白。
|
||||
常驻、取消常驻和排序按设备保存,重启后恢复。排序只发生在应用区,不移动固定系统入口。
|
||||
管理列表、应用菜单和主导航共用完整应用顺序,分别按搜索、启用、启用且常驻过滤;过滤不删除顺序记录,重新开启或常驻时按保存顺序恢复。
|
||||
每个管理行均可排序,包括关闭或未常驻的应用。上下移动以完整保存顺序中的相邻应用为目标,拖动使用同一移动规则。搜索时禁用拖动,箭头仍按完整顺序移动,保留未命中应用及其相对顺序。
|
||||
|
||||
### 3.1 应用可见性与业务状态
|
||||
|
||||
| 导航可见 | 功能启用 | 结果 |
|
||||
| --- | --- | --- |
|
||||
| 是 | 是 | 显示入口并允许进入 |
|
||||
| 否 | 是 | 隐藏入口,后台和数据保持原状 |
|
||||
| 是 | 否 | 不显示不可用入口;设置页保留启用路径 |
|
||||
| 否 | 否 | 不显示入口,保留符合该功能自身规则的数据 |
|
||||
下表适用于智能心跳、魔法笔记和本机推理监控。知识库始终可用,管理 Modal 中提供“打开”和排序,保留原工作区实际配置,不增加启用、常驻开关或通用空设置页。心跳应用的默认值与执行条件见[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)。
|
||||
|
||||
“功能启用”由各应用现有产品设置负责。例如魔法笔记启停仍使用原设置;导航偏好不能覆盖它。
|
||||
| 常驻偏好 | 功能启用 | 结果 |
|
||||
| --- | --- | --- |
|
||||
| 是 | 是 | 主导航显示,可从主导航或应用中心打开 |
|
||||
| 否 | 是 | 主导航隐藏,仍可从应用中心打开,后台和数据保持原状 |
|
||||
| 是 | 否 | 主导航、应用菜单和业务入口隐藏,管理 Modal 保留设置,记住常驻及顺序 |
|
||||
| 否 | 否 | 主导航、应用菜单和业务入口隐藏,管理 Modal 保留设置,记住未常驻状态 |
|
||||
|
||||
管理 Modal 浏览和搜索包含关闭的可选应用,搜索不修改启用或常驻。轻量菜单仅显示已启用项,点击行先关闭菜单;本机推理监控打开独立 Modal,其余应用请求主内容区导航。管理 Modal 的“打开”和常驻入口遵守同一规则。打开已启用应用不自动常驻;
|
||||
取消当前应用的常驻不关闭当前页面或本机推理监控 Modal。关闭应用后不能从旧入口继续执行新的业务操作。
|
||||
停用时仍允许管理常驻偏好,界面说明重新开启后才会显示入口。
|
||||
|
||||
### 3.2 关闭应用与恢复
|
||||
|
||||
关闭与恢复适用于可选应用,知识库页面、会话控件及关联不受影响。智能心跳关闭后的调度、手动请求、侧栏与在途运行按[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)处理,保留单条计划 enabled。
|
||||
|
||||
| 对象 | 关闭后的结果 | 重新开启后的结果 |
|
||||
| --- | --- | --- |
|
||||
| 当前已打开应用页或本机推理监控 Modal | 原位置显示“应用已关闭”的不可用状态;保留内容状态及草稿,不强制跳转。重新启用从应用中心对应行的设置进入,监控 Modal 需先关闭 | 恢复可用性;仍打开时恢复内容,已关闭或离开则不自动打开 |
|
||||
| 已在执行的请求及后台操作 | 沿用功能自身的取消、完成和清理生命周期;本次应用开关不新增取消策略 | 不自动恢复、中断后重跑或补发任务 |
|
||||
| 数据、常驻及顺序 | 保留原值,业务入口按启用状态过滤 | 按保存的常驻及顺序恢复入口,不把未常驻应用自动常驻 |
|
||||
|
||||
知识请求及单个知识库实例停用、配置修改或删除继续遵守知识库自身规则,没有应用级禁用判断。
|
||||
|
||||
### 3.3 魔法笔记设置迁移
|
||||
|
||||
魔法笔记设置迁入应用中心对应行的设置页;笔记页面和全局设置均不提供重复入口,
|
||||
编辑同一配置。启用、常驻、显示未完成待办数量分别控制功能、导航和徽标,不保留重复的
|
||||
“显示魔法笔记入口”开关。
|
||||
|
||||
未配置的魔法笔记、本机推理监控启用及魔法笔记相关布尔项默认开启;心跳应用默认关闭。魔法笔记和心跳默认常驻,本机推理监控默认不常驻;已启用应用仍可从应用菜单及管理 Modal 打开。已有保存的 `true` 和 `false` 均保留。
|
||||
AI 评论方式和评论形式继续为单选,沿用现有默认值“即时”和“长评 + 要点”,不能解释为
|
||||
全部模式同时启用。迁移保留既有单选值及业务数据。关闭数量显示只隐藏徽标,不修改待办。
|
||||
|
||||
## 4. 系统工具打开规则
|
||||
|
||||
### 4.1 通用流程
|
||||
|
||||
```text
|
||||
用户激活工具按钮
|
||||
-> 校验按钮仍可用
|
||||
-> 向助手工作栏发送打开意图
|
||||
-> 工作栏按应用实例策略查找或创建实例
|
||||
-> 展开工作栏
|
||||
-> 激活目标实例
|
||||
用户展开右侧工作栏
|
||||
-> 选择已有 Tab,或从“+”目录选择工具
|
||||
-> 已有 Tab 按实例 ID 激活;目录按单实例或多实例策略创建或激活
|
||||
-> 把焦点移入面板标题或首个主要控件
|
||||
```
|
||||
|
||||
@@ -85,31 +120,27 @@ type NavigationContribution =
|
||||
|
||||
### 4.2 决策表
|
||||
|
||||
| 工具 | 匹配实例存在 | 匹配实例不存在 | 上下文不可用 |
|
||||
| 工具 | 选择已有 Tab | 从“+”目录选择 | 上下文不可用 |
|
||||
| --- | --- | --- | --- |
|
||||
| 文件 | 激活不可关闭的单实例并保留其跟随或固定状态 | 仅在迁移或布局修复时补回 `workspace` | 打开面板并显示目录修复入口 |
|
||||
| 终端 | 激活当前执行空间最近的 `starting` 或 `running` 实例 | 创建一个绑定当前执行空间的实例 | 没有项目时使用本机 Home |
|
||||
| 浏览器 | 激活绑定当前 Conversation 的最近使用实例 | 创建绑定当前 Conversation 的新实例 | 激活最近使用实例;不存在时创建待绑定实例 |
|
||||
| 资源监控 | 激活 `resources` 单实例 | 创建本机 `resources` 单实例 | 部分指标不可用时显示原因并保留其余指标 |
|
||||
| 文件 | 激活不可关闭单实例并保留跟随或固定状态 | 聚焦 `workspace`;仅迁移或布局修复时补回 | 跟随模式无有效项目目录或默认工作区时显示修复入口 |
|
||||
| 终端 | 激活选中的确切实例,保留执行空间和历史状态 | 每次创建绑定当前执行空间的额外实例 | 新建时无项目使用本机 Home;SSH 失败保留远端目标与诊断 |
|
||||
| 浏览器 | 激活选中的确切实例,保留绑定目标和页面 | 每次创建绑定当前 Conversation 的额外实例 | 新建时无 Conversation 创建待绑定空实例 |
|
||||
| 资源监控 | 激活本机 `resources` 单实例 | 已存在则聚焦,否则创建 | 部分指标不可用时显示原因并保留其余指标 |
|
||||
|
||||
文件保持单一实例原有绑定。浏览器快捷入口按当前 Conversation 匹配最近使用实例,而工作栏
|
||||
“+”始终新建浏览器实例。固定目标失效时显示该目标的修复状态,不回退到当前项目、默认
|
||||
工作区或当前会话。
|
||||
|
||||
`closing`、`exited`、`interrupted` 和 `failed` 终端不属于可复用实例。当前执行空间只有这些
|
||||
终端时,快捷按钮创建新实例并保留原 Tab 的历史状态。用户需要额外的并行终端时使用工作栏
|
||||
“+”或终端面板中的既有创建命令。
|
||||
选择已有 Tab 不按当前项目或 Conversation 查找替代实例,也不重绑目标。文件的跟随模式
|
||||
仍按工作栏自身规则更新上下文;固定目标失效时显示该目标的修复状态,不静默回退。
|
||||
终端 `closing`、`exited`、`interrupted`、`failed` Tab 保留原状态,选择它们不自动重建。
|
||||
终端和浏览器通过“+”新建时不按目标去重,不恢复旧快捷按钮的最近匹配实例复用行为。
|
||||
|
||||
### 4.3 选中与状态
|
||||
|
||||
工具按钮满足以下条件时显示选中:助手工作栏已展开,且当前活动实例的 `appId` 与工具映射
|
||||
一致。只有实例存在但未激活时不显示选中。
|
||||
工作栏按确切实例 ID 表达 Tab 选中状态,同类型其他实例不能同时显示为选中。“+”是普通
|
||||
按钮,不是 Tab。主侧栏和应用中心不显示工具选中状态或工具徽标。
|
||||
|
||||
徽标优先级为:等待用户处理 > 错误 > 运行中 > 无徽标。同一按钮只显示一个视觉徽标,
|
||||
徽标优先级为:等待用户处理 > 错误 > 运行中 > 无徽标。同一 Tab 只显示一个视觉徽标,
|
||||
可访问名称包含完整状态。
|
||||
|
||||
资源监控首期不在后台采样,因此不显示负载告警徽标。只有资源面板是工作栏当前活动实例且
|
||||
工作栏已展开时,资源按钮才显示选中状态。
|
||||
资源监控首期不在后台采样,因此不显示负载告警徽标。后台事件不自动展开工作栏或切换 Tab。
|
||||
|
||||
### 4.4 资源采样状态
|
||||
|
||||
@@ -134,44 +165,74 @@ warming/loading/live/partial/error -> inactive
|
||||
|
||||
## 5. 设置保存规则
|
||||
|
||||
应用导航偏好与应用业务启停分开保存。Switch 每次切换立即提交完整目标集合;请求完成前禁用
|
||||
三个应用显隐 Switch,下一次操作基于最新确认结果提交,防止并发更新互相覆盖。
|
||||
应用导航偏好与业务启停使用独立字段,通过同一应用设置保存链路提交。常驻、排序、启用和
|
||||
魔法笔记设置更新串行执行;前一请求确认前,所有入口中会修改这组设置的控件暂不可操作。
|
||||
每次只提交本次变更,Main 在最新已保存设置上合并并原子写入;顺序提交完整目标顺序。
|
||||
持久化成功后由 Store 发布完整权威快照,应用中心、主导航、当前应用页、会话和所有设置入口同步采用,工具配置写入也走同一通知链路。每次重开中心先刷新并锁定修改;较新的设置事件使在途旧读取及旧保存响应失效,不能覆盖新快照。
|
||||
|
||||
保存失败时:
|
||||
|
||||
- 主导航和 Switch 恢复最后一次成功保存的偏好。
|
||||
- 设置行显示可重试错误。
|
||||
- 不回滚或修改各应用的业务状态。
|
||||
- 所有界面保留最后一次确认的配置,包括启用、常驻、顺序、徽标和会话入口;不提前运行关闭或恢复副作用。
|
||||
- 发起操作的设置行或排序区域显示就地可重试错误,保留焦点及编辑上下文,不重复显示同一错误通知。
|
||||
- 若响应中断导致保存结果未知,先重读权威设置再开放修改;不能把本地旧值当成回滚成功,也不自动重发变更。
|
||||
|
||||
## 6. 与需求的对应关系
|
||||
|
||||
| 逻辑 | PRD | User Stories |
|
||||
| --- | --- | --- |
|
||||
| 固定入口不变量 | FR-1、FR-2 | US-A1、US-A2 |
|
||||
| 应用显隐和失败恢复 | FR-3、FR-4、FR-11、FR-12 | US-B1、US-B2、US-B4、US-B5 |
|
||||
| 工具打开决策表 | FR-5 至 FR-10 | US-C1 至 US-C7 |
|
||||
| 应用常驻和失败恢复 | FR-3、FR-4、FR-11、FR-12 | US-B1、US-B2、US-B4、US-B5 |
|
||||
| 应用中心、常驻顺序 | FR-3、FR-4、FR-5、FR-11、FR-12;PRD 第 8 节 | US-B6、US-B7 |
|
||||
| 可选应用启用恢复、知识库固定可用 | FR-3、FR-4、FR-12;PRD 第 8 节补充要求 | US-B8 |
|
||||
| 魔法笔记迁移及共同配置 | FR-11、FR-12;PRD 第 8 节补充要求 | US-B9 |
|
||||
| 工具打开决策表 | FR-6 至 FR-10 | US-C1 至 US-C7 |
|
||||
| 资源采样状态 | FR-9 | US-C6 |
|
||||
| 无障碍与窄窗口 | FR-13 | US-D1、US-D2 |
|
||||
| 活动聚合、完成通知与查看清除 | FR-14 | US-E1、US-E2、US-E3 |
|
||||
| 后续私有市场与 YAML | FR-15 | US-F1、US-F2 |
|
||||
| 服务状态、资源可用性、服务生命周期 | FR-16 | US-F3、US-F5;US-F4 入口已移除 |
|
||||
|
||||
稳定 ID 保留原编号。US-B6 为应用中心,US-B7 为顺序,US-B8 为启用联动,US-B9 为笔记迁移;
|
||||
US-F3 为服务概览,US-F4 为已移除的任务取消入口,US-F5 为服务生命周期。PRD 旧 FR-5 至 FR-10 和
|
||||
User Stories 已同步上述入口规则,工具实例行为沿用右侧工作栏契约。
|
||||
|
||||
## 7. 逻辑完整性评估
|
||||
|
||||
首期规则已覆盖固定入口、应用显隐、保存失败、系统工具匹配与显式多实例差异、本机 Home、
|
||||
托管 SSH、无活动会话、目录失效、资源部分可用、采样停止、权限边界、键盘和窄窗口。其他
|
||||
工具面板内部的取消、超时、
|
||||
连接恢复和固定目标失效继续由助手工作栏负责,本功能不复制这些状态机。
|
||||
首期覆盖应用中心访问、启用与常驻组合、顺序恢复、默认值与旧值迁移、共同设置、保存失败、
|
||||
知识库固定入口、心跳应用启停、可选应用已打开页面恢复,以及右侧实例选择、本机 Home、托管 SSH、无会话、
|
||||
目录失效、资源部分可用与采样停止。工具内部取消、超时、连接恢复及业务后台生命周期由
|
||||
相应功能负责,本功能不复制状态机。底部入口打开轻量菜单,“管理应用”才进入与设置相同的整窗 Modal。实现与验证的剩余范围见[实施进度](./progress.md)。
|
||||
|
||||
## 8. 后续产品决策
|
||||
|
||||
- 插件工具上线后,快捷栏允许固定的最大数量及溢出规则。
|
||||
- 插件应用是否允许用户排序,以及固定系统入口之间允许的排序范围。
|
||||
后续市场 URL、YAML 交换、ShareServer 目录与分发、插件注册及安装不进入首期。其协议、
|
||||
扩展身份与安装生命周期由市场功能另行确定;内置应用常驻及排序不依赖这些决策。
|
||||
|
||||
这些决策不影响首期四个系统工具的固定入口和内置应用显隐实现。
|
||||
### 8.1 本机推理管理
|
||||
|
||||
FR-16 独立于市场排期,作为可常驻的管理 Modal。所有打开入口只改变 Modal 打开状态,不改变底层工作区、项目、会话或草稿;关闭时卸载 Modal,服务生命周期独立。US-F3 只有服务列表,展示名称、状态、引擎、模型及错误。共享服务只展示一次;资源按独立服务进程归属,CPU 预热与指标不可用分别给出原因,不填 `0`,不将宿主进程占用分摊给共享线程。采样与单位见[技术设计](./technical-design.md)。ASR 没有 Worker 就绪观测,显示按需运行且状态未知;活动请求不覆盖服务状态。OCR 状态来自 Renderer Worker,向量状态来自 Broker。
|
||||
|
||||
US-F4 的 Modal 单任务取消入口已移除。后端取消行为保留:Worker 确认或退出前仍计入活动任务和停止影响,即使调用方已收到取消或超时错误;任务先完成则保留真实完成结果,不通过终止共享服务模拟单任务取消。
|
||||
|
||||
| 服务管理方式 | 可提供操作 | 结果要求(US-F5) |
|
||||
| --- | --- | --- |
|
||||
| GoodBuddy 受管子进程 | 引擎已支持的启动、停止、重启 | 共用同一服务实例;操作成功由真实进程和服务就绪状态确认 |
|
||||
| 外部服务 | 在模型与连接设置中管理 | 不列入本机服务,不推断本机归属 |
|
||||
| 进程内模型 | 引擎实际支持的加载、释放 | 不显示虚构 PID 或进程启动、停止状态 |
|
||||
|
||||
停止、重启或释放均先打开紧凑的嵌套影响确认,列出任务和影响;无已登记活动任务时说明执行前再次核对。取消确认不改变运行状态。
|
||||
确认存在时禁止关闭底层管理 Modal;提交期间两层均不能关闭,重复操作被拒绝。未提交时 Escape 只关闭确认,确认关闭后才可用 Escape 关闭管理 Modal。焦点规则由 [UI 设计](./ui-design.md#91-本机推理管理)定义。
|
||||
实际操作失败关闭旧确认、显示错误并刷新快照;刷新期间或失败时禁用服务操作。再次操作重新取得当前任务清单并要求确认,避免重复提交旧清单。
|
||||
再次启动不重放已中断任务;关闭管理 Modal、关闭管理应用或取消常驻不停止服务,卸载模型文件为独立操作。
|
||||
当前向量服务允许确认影响后停止,并拒绝确认清单未包含的新任务;OCR 仅空闲时允许释放。
|
||||
ASR 按请求加载与释放,支持独立取消;TTS 尚无执行服务。具体适配边界以
|
||||
[技术清单](./technical-design.md#111-本机推理任务与执行服务)为准。活动请求仅在影响确认中展示,主列表不展示历史。应用退出清理沿用各引擎原有路径。
|
||||
|
||||
## 9. 项目活动汇总
|
||||
|
||||
对应 [FR-14](./prd.md#fr-14-项目活动汇总),按 Conversation ID 聚合,每条会话最多一行。
|
||||
数据来源为活动会话 ID、会话中的流式消息及待回答/待审批请求,以及全部可见的运行中或
|
||||
等待处理 Task;不能只取当前项目、当前搜索结果或每条会话的最新 Task。
|
||||
等待处理 Task,以及当前应用会话内的已完成未查看集合;不能只取当前项目、当前搜索结果或每条会话的最新 Task。
|
||||
|
||||
| 优先级(高到低) | 条件 | 汇总类别 |
|
||||
| --- | --- | --- |
|
||||
@@ -179,18 +240,30 @@ warming/loading/live/partial/error -> inactive
|
||||
| 待审批 | 消息有待审批请求 | 待处理 |
|
||||
| Task 等待处理 | Task 为 `waiting_approval`,尚无更具体的消息状态 | 待处理 |
|
||||
| 运行中 | 活动会话 ID、`streaming` 消息或运行中 Task | 运行中 |
|
||||
| 已完成 | 会话在已完成未查看集合中,且无更高优先级实时状态 | 已完成 |
|
||||
|
||||
同一会话只采用最高优先级状态;Task 的 `waiting_approval` 也可能对应提问,不能直接
|
||||
标成待审批。“待处理”和“运行中”数量互斥,全局总数为两者之和;项目计数使用相同结果。
|
||||
标成待审批。“待处理”“运行中”和“已完成”数量互斥,全局总数为三者之和;项目计数使用相同结果。
|
||||
项目归属优先取会话元数据,缺少会话时取 Task 元数据;无项目归属的活动只计入全局汇总。
|
||||
|
||||
历史排队项、仅待调度的 Task、终态 Task 和未读标记本身不构成活动。活动结束且没有其他
|
||||
运行或等待条件时移除该行,不能因历史任务或未读状态继续计数。
|
||||
历史排队项、仅待调度的 Task、终态 Task 和普通未读标记本身不构成活动。完成通知规则如下:
|
||||
|
||||
- 本地运行收到成功 `done`,或当前活动运行从持久化快照确认成功完成时,记录会话完成通知。
|
||||
- 后台顶层 Task 只有在本次应用会话中已观察到该任务,随后状态转为 `completed` 时才记录通知;
|
||||
初次载入或首次出现的已完成历史任务只建立基线,子任务不单独产生通知。
|
||||
- 失败、取消不产生完成通知。实时待处理或运行中状态优先;开始新运行时清除旧完成通知,
|
||||
已观察 Task 转为 `running` 或 `waiting_approval` 时也清除对应会话的旧通知。
|
||||
- 通知只保存在 Renderer 内存,不跨重载或重启恢复,也不从普通未读状态推导。
|
||||
- 仅当目标会话实际成为当前聊天、设置未打开且文档未隐藏时清除通知;普通侧栏导航和活动菜单
|
||||
导航采用同一查看条件。已在查看的会话完成后不保留通知,恢复可见聊天时也清除通知。
|
||||
- 展开活动项目、查看菜单、设置覆盖下保留挂载的聊天、隐藏文档以及取消设置离开都不算查看。
|
||||
|
||||
没有实时状态且没有已完成未查看通知时才移除活动行。
|
||||
|
||||
级联菜单先按项目归属聚合活动行,沿用本地、SSH Host、通道的稳定项目顺序;缺少项目
|
||||
元数据的归属组及“未归属项目”组放在已知项目之后。无归属组只用于访问这些会话,不增加
|
||||
项目计数。每个项目内先列待处理会话,再列运行中会话,同类别保留来源顺序。
|
||||
项目计数。每个项目内依次列待处理、运行中、已完成会话,同类别保留来源顺序。
|
||||
|
||||
展开项目只改变菜单层级,不导航。选中项目的活动全部结束后返回项目列表;全局活动结束后
|
||||
展开项目只改变菜单层级,不导航。选中项目的活动行全部移除后返回项目列表;全局无活动行后
|
||||
关闭菜单并隐藏摘要入口。后台更新保留仍存在的焦点项,删除焦点项时将焦点
|
||||
移至当前层可用项。关闭菜单后,新的活动只恢复摘要,不会自动重新打开菜单。
|
||||
|
||||
@@ -122,6 +122,27 @@ dialog[data-variant="grid"] #catalog-items { display: grid; grid-template-column
|
||||
.sidebar > * { flex-shrink: 0; }.sidebar .history { flex: 1 1 80px; min-height: 0; }.sidebar .shortcuts { flex-shrink: 1; min-height: 80px; }
|
||||
.sidebar .resident-apps { margin-top: 12px; min-height: 96px; flex-shrink: 1; display: flex; flex-direction: column; max-height: 240px; }.resident-apps .section-label { margin-bottom: 4px; flex-shrink: 0; }#resident-list { overflow: auto; min-height: 0; }.resident-row { display: flex; align-items: center; }.resident-row > button { display: flex; align-items: center; gap: 10px; padding: 8px 10px; flex: 1; min-width: 0; text-align: left; }.resident-row > button span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }.resident-row [aria-current="page"] { background: var(--accent-subtle); color: var(--accent); }.resident-row[draggable="true"] { cursor: grab; }.resident-row .edit-actions { flex-shrink: 0; }.all-apps { display: flex; justify-content: space-between; padding: 6px 10px; font-size: 11px; color: var(--text-secondary); flex-shrink: 0; }.empty-apps { padding: 8px 10px; font-size: 11px; color: var(--text-secondary); }.app-placement { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 8px; }.app-placement button { font-size: 11px; padding: 6px 8px; }.app-placement button[aria-pressed="true"] { border: 1px solid var(--accent-selected); }.app-catalog-row { align-items: flex-start; }.app-catalog-row > svg { margin-top: 4px; }
|
||||
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
|
||||
.workspace { min-height: 0; }
|
||||
.workspace-header { flex: 0 0 auto; flex-wrap: wrap; gap: 8px; align-items: center; }
|
||||
#page-label { overflow-wrap: anywhere; }
|
||||
.conversation { min-height: 0; overflow: auto; }
|
||||
.conversation.app-page { justify-content: flex-start; padding: 24px; max-width: 960px; gap: 12px; }
|
||||
.app-page h1 { font-size: 24px; }
|
||||
.model-list { width: 100%; }
|
||||
.model-service { padding: 16px 0; border-bottom: 1px solid var(--border-default); }
|
||||
.model-service p, .model-service details { color: var(--text-secondary); font-size: 12px; }
|
||||
.model-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 8px 0; }
|
||||
.model-actions button, .inline-confirm button { border: 1px solid var(--border-default); }
|
||||
.inline-confirm { padding: 12px; margin: 8px 0; background: var(--accent-subtle); border: 1px solid var(--accent-selected); border-radius: var(--radius-control); }
|
||||
#market-preview { padding: 12px 0; border-bottom: 1px solid var(--border-default); }
|
||||
#market-preview label { display: block; margin: 8px 0; }
|
||||
#market-preview textarea, #market-preview select { width: 100%; padding: 8px; font: inherit; background: var(--surface-subtle); color: var(--text-primary); border: 1px solid var(--border-default); border-radius: var(--radius-control); }
|
||||
#market-preview textarea { resize: vertical; }
|
||||
#market-preview p, #import-preview { font-size: 12px; overflow-wrap: anywhere; }
|
||||
#market-preview summary, .model-service summary { cursor: pointer; }
|
||||
.note-segments, .center-switch, .tool-tabs { flex: 0 0 auto; }
|
||||
.center-app-info { min-width: 0; overflow-wrap: anywhere; }
|
||||
dialog, dialog::backdrop { -webkit-app-region: no-drag; }
|
||||
.launcher-trigger { display: flex; align-items: center; gap: 8px; width: 100%; padding: 12px 8px; border: 1px solid var(--border-default); background: var(--surface-raised); margin-bottom: 12px; font-size: 12px; }.launcher-trigger kbd { margin-left: auto; font: 10px var(--font-family-ui); color: var(--text-secondary); white-space: nowrap; }
|
||||
[data-variant="launcher"] .shortcut-list { grid-template-columns: repeat(4, minmax(0, 1fr)); }[data-variant="launcher"] .shortcut-button { flex-direction: column; padding: 8px 0; }[data-variant="launcher"] .icon-tile { height: 24px; }[data-variant="launcher"] .store-link { border-top: 1px solid var(--border-default); border-radius: 0; }
|
||||
[data-variant="favorites"] .section-label { padding-left: 0; }[data-variant="favorites"] .shortcut-list { gap: 0; }[data-variant="favorites"] .shortcut-button { justify-content: flex-start; padding: 8px 12px; }[data-variant="favorites"] .item-name { font-size: 12px; }[data-variant="favorites"] .shortcut-item { display: flex; align-items: center; }[data-variant="favorites"] .edit-actions { margin-left: auto; }[data-variant="favorites"] .store-link { margin-top: 12px; border: 1px dashed var(--border-default); }.count { padding: 0 6px; color: var(--text-secondary); }
|
||||
@@ -130,4 +151,4 @@ dialog[data-variant="grid"] #catalog-items { display: grid; grid-template-column
|
||||
.catalog-pack { padding: 20px 0; border-bottom: 1px solid var(--border-default); }.catalog-pack h3 { margin: 8px 0; font-size: 18px; font-weight: 550; }.pack-art { display: grid; place-items: center; float: right; width: 56px; height: 56px; border: 1px solid var(--accent-selected); background: var(--accent-subtle); color: var(--accent); border-radius: var(--radius-card); }.pack-art svg { width: 28px; height: 28px; }.pack-includes { display: flex; flex-wrap: wrap; gap: 8px; margin: 16px 0; }.pack-includes span { padding: 4px 8px; font-size: 11px; background: var(--surface-subtle); border-radius: var(--radius-control); }.pack-includes svg { width: 14px; height: 14px; }.catalog-pack > button { border: 1px solid var(--accent-selected); color: var(--accent); background: var(--accent-subtle); }
|
||||
dialog[data-mode="launch"] { width: min(640px, calc(100% - 32px)); }dialog[data-mode="launch"] .catalog-row:focus-within { background: var(--accent-subtle); }.catalog-row { padding-left: 8px; padding-right: 8px; }
|
||||
@media (max-width: 1080px) { body { padding: 16px; }.app { position: relative; }.workbar { position: absolute; right: 0; top: 52px; bottom: 0; width: min(320px, 100%); box-shadow: -8px 0 24px var(--border-default); }.lab-header { flex-wrap: wrap; gap: 12px; }.design-note > span:last-child { display: none; }.conversation { padding: 32px 24px; } }
|
||||
@media (max-width: 640px) { body { padding: 12px; }.lab-header > div:first-child { flex: 1; }.variants { order: 3; width: 100%; }.variants button { flex: 1; padding: 8px 4px; font-size: 12px; }.design-note { align-items: flex-start; gap: 12px; }.app { height: auto; min-height: 0; }.app .sidebar { width: 100%; flex: 0 0 auto; border: 0; padding-top: 12px; }.brand { padding-bottom: 12px; }.app .history { display: none; }.app .workspace { display: flex; min-height: 320px; border-top: 1px solid var(--border-default); }.app .workspace .composer { display: block; }.composer-area { margin: 12px 12px 4px; margin-top: auto; }.composer-toolbar { gap: 2px; }.composer-select select { max-width: 108px; font-size: 11px; }.composer-toolbar button { font-size: 11px; padding: 6px; }.composer-toolbar .composer-icon { width: 28px; }.composer-meta { flex-wrap: wrap; gap: 0; }.shortcut-list { max-height: 300px; overflow: auto; }.lab-footer { align-items: flex-start; }.lab-footer button { white-space: nowrap; }.workbar { top: 0; width: 100%; }.design-note p { font-size: 11px; } }
|
||||
@media (max-width: 640px) { body { padding: 12px; }.lab-header > div:first-child { flex: 1; }.variants { order: 3; width: 100%; }.variants button { flex: 1; padding: 8px 4px; font-size: 12px; }.design-note { align-items: flex-start; gap: 12px; }.app { height: auto; min-height: 0; }.app .sidebar { width: 100%; flex: 0 0 auto; border: 0; padding-top: 12px; }.brand { padding-bottom: 12px; }.app .history { display: none; }.app .workspace { display: flex; min-height: 320px; border-top: 1px solid var(--border-default); }.app .workspace .conversation { display: flex; overflow: visible; padding: 16px; }.app .workspace .composer { display: block; }.composer-area { margin: 12px 12px 4px; margin-top: auto; }.composer-toolbar { gap: 2px; }.composer-select select { max-width: 108px; font-size: 11px; }.composer-toolbar button { font-size: 11px; padding: 6px; }.composer-toolbar .composer-icon { width: 28px; }.composer-meta { flex-wrap: wrap; gap: 0; }.shortcut-list { max-height: 300px; overflow: auto; }.lab-footer { align-items: flex-start; }.lab-footer button { white-space: nowrap; }.workbar { top: 0; width: 100%; }.design-note p { font-size: 11px; } }
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
<button data-variant="cards" aria-pressed="false">C · 应用预览</button>
|
||||
<button data-variant="launcher" aria-pressed="false">D · 搜索启动台</button>
|
||||
<button data-variant="favorites" aria-pressed="false">E · 常驻管理</button>
|
||||
<button data-variant="scenes" aria-pressed="false">F · 商店发现</button>
|
||||
<button data-variant="scenes" aria-pressed="false">F · 私有市场预览</button>
|
||||
</div>
|
||||
<button id="theme">切换深色</button>
|
||||
</header>
|
||||
<div class="design-note"><span id="variant-label">A / 紧凑 Dock</span><p id="variant-description"></p><span>交互原型 · 商店与指标均为演示数据</span></div>
|
||||
<div class="design-note"><span id="variant-label">A / 紧凑 Dock</span><p id="variant-description"></p><span>交互原型 · 全部状态仅在内存中,刷新重置</span></div>
|
||||
<div class="project-lab"><label>项目活动演示 <select id="project-scenario"><option value="waiting">有待处理</option><option value="running">仅运行中</option><option value="current">仅当前项目运行</option><option value="idle">全部空闲</option></select></label><span>有活动时显示摘要,悬停或点击项目向右展开会话。</span></div>
|
||||
<main class="app" data-variant="dock">
|
||||
<aside class="sidebar" aria-label="主侧栏">
|
||||
@@ -63,8 +63,8 @@
|
||||
<div id="composer-options" popover aria-label="会话选项">
|
||||
<header><strong>会话选项</strong><button popovertarget="composer-options" popovertargetaction="hide" aria-label="关闭会话选项">×</button></header>
|
||||
<label class="option-row">专家<select id="composer-expert"><option value="">默认助手</option><option>产品顾问</option><option>代码审查专家</option></select></label>
|
||||
<fieldset><legend>知识范围</legend><label><input type="checkbox" data-knowledge="产品文档"> 产品文档</label><label><input type="checkbox" data-knowledge="团队知识"> 团队知识</label></fieldset>
|
||||
<label class="option-row">检索方式<select id="composer-retrieval"><option value="auto">自动检索</option><option value="always">每次检索</option></select></label>
|
||||
<fieldset data-knowledge-control><legend>知识范围</legend><label><input type="checkbox" data-knowledge="产品文档"> 产品文档</label><label><input type="checkbox" data-knowledge="团队知识"> 团队知识</label></fieldset>
|
||||
<label class="option-row" data-knowledge-control>检索方式<select id="composer-retrieval"><option value="auto">自动检索</option><option value="always">每次检索</option></select></label>
|
||||
<div id="runtime-options"><label class="option-row"><span id="agent-label">Agent</span><select id="composer-agent"><option value="">默认</option><option value="plan">Plan</option><option value="review">Review</option></select></label><label class="option-row">快捷操作<select id="composer-command"><option value="">无</option><option value="prompt">填入总结模板</option><option value="command">原生命令 /init</option></select></label></div>
|
||||
<p class="muted">非默认选项会显示在输入框上方。</p>
|
||||
</div>
|
||||
@@ -77,7 +77,16 @@
|
||||
</main>
|
||||
<footer class="lab-footer"><span id="design-tradeoff"></span><button id="reset">恢复默认应用</button></footer>
|
||||
<div class="reference-note">设计参考:<a href="https://www.raycast.com/" target="_blank" rel="noreferrer">Raycast · 搜索与扩展</a><a href="https://www.notion.com/help/intro-to-workspaces" target="_blank" rel="noreferrer">Notion · 收藏与折叠分区</a><a href="https://code.visualstudio.com/docs/configure/profiles" target="_blank" rel="noreferrer">VS Code · Profiles</a><span>借鉴交互思路,保留 GoodBuddy 的视觉与入口分类。</span></div>
|
||||
<dialog id="catalog" aria-labelledby="catalog-title"><button id="back-to-apps" hidden>‹ 返回应用中心</button><header><div><span class="eyebrow">GOODBUDDY COLLECTION</span><h2 id="catalog-title">应用中心</h2></div><button id="close-catalog" aria-label="关闭">×</button></header><p class="muted" id="catalog-description"></p><div class="center-switch" role="group" aria-label="应用中心视图"><button data-catalog="apps">我的应用</button><button data-catalog="store">商店</button></div><input id="search" type="search" aria-label="搜索应用" placeholder="搜索应用…"><div id="catalog-items"></div><section id="app-settings" aria-label="魔法笔记设置" hidden></section><footer>常驻只表示菜单入口,不表示后台运行。安装与内容均为演示。</footer></dialog>
|
||||
<dialog id="catalog" aria-labelledby="catalog-title"><button id="back-to-apps" hidden>‹ 返回应用中心</button><header><div><span class="eyebrow">GOODBUDDY COLLECTION</span><h2 id="catalog-title">应用中心</h2></div><button id="close-catalog" aria-label="关闭">×</button></header><p class="muted" id="catalog-description"></p><div class="center-switch" role="group" aria-label="应用中心视图" hidden><button data-catalog="apps">我的应用</button><button data-catalog="store">未来私有市场</button></div><section id="market-preview" hidden>
|
||||
<label>私有市场 URL<input id="market-url" type="url" value="https://market.example.invalid" aria-describedby="market-status"></label><button id="market-fetch">模拟获取目录</button><button id="market-fail">模拟不可达</button><p id="market-status" role="status">未来预览,不发送网络请求;来源仅用于市场。</p>
|
||||
<details><summary>YAML 导入 / 导出预览(mock schema)</summary><p class="muted">仅支持下列扁平示例字段,非正式 Schema。支持 .yml / .yaml 的文本预览;不包含凭据或业务数据,不替代程序包或模型权重。</p><label>应用定义<textarea id="yaml-input" rows="9" spellcheck="false">schema: mock-v1
|
||||
id: reading
|
||||
name: 阅读空间
|
||||
version: 1.0
|
||||
entry: main
|
||||
dependencies: builtin:knowledge
|
||||
resources: bundled</textarea></label><button id="yaml-preview">预览导入</button><label>导出应用<select id="yaml-app"></select></label><button id="yaml-export">预览导出 YAML</button><pre id="yaml-output" tabindex="0" hidden></pre><div id="import-preview" hidden></div></details>
|
||||
</section><input id="search" type="search" aria-label="搜索应用" placeholder="搜索应用…"><div id="catalog-items"></div><section id="app-settings" aria-label="应用设置" hidden></section><footer>取消常驻仅隐藏捷径;关闭应用隐藏业务入口,保留数据与后台状态。此处全部为模拟。</footer></dialog>
|
||||
<div id="toast" role="status" aria-live="polite"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -14,6 +14,7 @@ const items = [
|
||||
{ id: 'notes', name: '魔法笔记', icon: 'note', detail: '记录想法、整理待办', summary: '把刚才的灵感留在这里', builtin: true },
|
||||
{ id: 'knowledge', name: '知识库', icon: 'book', detail: '整理资料、检索知识', summary: '下一次提问,从已有知识开始', builtin: true },
|
||||
{ id: 'heartbeat', name: '智能心跳', icon: 'activity', detail: '管理定时任务与主动提醒', summary: '让例行任务按计划发生', builtin: true },
|
||||
{ id: 'models', name: '本机推理监控', icon: 'layers', detail: '语音、OCR、向量任务统一管理 · 计划预览', summary: '查看本机推理任务、使用方和资源占用', builtin: true },
|
||||
{ id: 'projects', name: '项目管理', icon: 'layers', detail: '跟踪项目、任务与进度', summary: '把一个想法变成可跟进的项目', builtin: false },
|
||||
{ id: 'reading', name: '阅读空间', icon: 'book', detail: '集中阅读、批注与摘录', summary: '给长文和深度阅读留一个空间', builtin: false }
|
||||
];
|
||||
@@ -23,7 +24,7 @@ const variants = {
|
||||
cards: ['C / 应用预览', '入口展示内置应用图标,展开后用卡片说明每个应用的用途。', '适合通过内容理解应用,卡片不代表后台持续运行。'],
|
||||
launcher: ['D / 搜索启动台', '最底部就是搜索入口,也可以按 Ctrl / ⌘ K 打开。', '输入名称,用方向键与 Enter 打开已安装应用。'],
|
||||
favorites: ['E / 常驻管理', '从底部管理哪些应用常驻菜单,未常驻应用也可直接打开。', '常驻应用优先排列;入口显隐不影响安装和运行状态。'],
|
||||
scenes: ['F / 商店发现', '底部入口强调应用发现,展开后查看商店应用与安装位置。', '先模拟安装,再决定是否常驻;我的应用始终可切换访问。']
|
||||
scenes: ['F / 私有市场预览', '未来实验:私有市场 URL、模拟目录获取与 YAML 交换。', '不联网、不写入文件;mock schema 不代表正式协议。']
|
||||
};
|
||||
const $ = selector => document.querySelector(selector);
|
||||
const demoProjects = [
|
||||
@@ -134,7 +135,20 @@ window.addEventListener('scroll', () => { if ($('#project-activity').matches(':p
|
||||
renderProjects();
|
||||
let variant = 'dock';
|
||||
let catalogMode = 'apps';
|
||||
let residentApps = ['notes', 'knowledge'];
|
||||
let residentApps = items.filter(item => item.builtin && item.id !== 'models').map(item => item.id);
|
||||
let enabledApps = new Set(items.filter(item => item.builtin).map(item => item.id));
|
||||
let currentPage = '当前对话';
|
||||
let settingsApp = 'notes';
|
||||
let marketReady = false;
|
||||
let importDraft = null;
|
||||
const definitions = new Map();
|
||||
const modelServices = [
|
||||
{ id: 'ocr', name: 'OCR 文字识别', purpose: '文档入库 #12 · 正在识别第 8 / 24 页', engine: 'PaddleOCR worker', management: '受管进程', running: true, usage: '420 MB', users: '知识库 / 文档入库任务 #12' },
|
||||
{ id: 'speech', name: '语音识别', purpose: '语音输入 #18 · 正在转写', engine: '本机语音识别引擎', management: '受管进程', running: true, usage: '310 MB', users: '当前会话 / 语音输入 #18' },
|
||||
{ id: 'tts', name: '语音合成', purpose: '回复朗读 #19 · 正在合成', engine: '本机语音合成引擎', management: '进程内', running: true, usage: '180 MB', users: '当前会话 / 回复朗读 #19' },
|
||||
{ id: 'embedding', name: '向量生成', purpose: '文档向量化 #7 · 已处理 32 / 80 个片段', engine: 'BGE-small / ONNX Runtime', management: '进程内', running: true, usage: '128 MB', users: '知识库 / 文档向量化任务 #7' }
|
||||
];
|
||||
let modelConfirmation = null;
|
||||
let editingApps = false;
|
||||
let draggedApp = '';
|
||||
let installed = new Set(items.filter(item => item.builtin).map(item => item.id));
|
||||
@@ -143,23 +157,28 @@ let settingsOpen = false;
|
||||
const noteSettings = { showCount: true, mode: 'immediate', format: 'combined' };
|
||||
const noteModes = { immediate: '即时', 'after-save-auto': '保存后自动', 'after-save-manual': '保存后手动' };
|
||||
const noteFormats = { combined: '长评 + 要点', narrative: '长评', structured: '要点' };
|
||||
function renderNoteSettings() {
|
||||
$('#app-settings').innerHTML = `<div class="note-setting-row"><div><strong>常驻左侧菜单</strong><p>对应“显示魔法笔记入口”,移除后仍可从应用中心打开。</p></div><button class="demo-switch" role="switch" aria-label="常驻左侧菜单" aria-checked="${residentApps.includes('notes')}" data-note-toggle="resident"></button></div>
|
||||
function renderAppSettings() {
|
||||
const item = items.find(entry => entry.id === settingsApp);
|
||||
$('#app-settings').innerHTML = `<div class="note-setting-row"><div><strong>启用应用</strong><p>关闭后隐藏业务入口,保留常驻偏好、数据和后台状态。可在此重新开启。</p></div><button class="demo-switch" role="switch" aria-label="启用${item.name}" aria-checked="${enabledApps.has(item.id)}" data-app-toggle="enabled"></button></div>
|
||||
<div class="note-setting-row"><div><strong>常驻左侧菜单</strong><p>仅控制主导航捷径;应用关闭时记住此偏好。</p></div><button class="demo-switch" role="switch" aria-label="常驻左侧菜单" aria-checked="${residentApps.includes(item.id)}" data-app-toggle="pinned"></button></div>`;
|
||||
if (settingsApp === 'notes') $('#app-settings').innerHTML += `
|
||||
<div class="note-setting-row"><div><strong>显示未完成待办数量</strong><p>在左侧入口显示数量,超过 99 项显示 99+。本 Demo 使用 3 项示例。</p></div><button class="demo-switch" role="switch" aria-label="显示未完成待办数量" aria-checked="${noteSettings.showCount}" data-note-toggle="showCount"></button></div>
|
||||
<fieldset class="note-setting-options"><legend>AI 评论方式</legend><div class="note-segments" role="group" aria-label="AI 评论方式">${Object.entries(noteModes).map(([value, label]) => `<button data-note-mode="${value}" aria-pressed="${noteSettings.mode === value}">${label}</button>`).join('')}</div><p>即时:按回车并停止输入 5 秒后评论未保存草稿。保存后自动:保存后评论。保存后手动:点击 AI 分析后评论。</p></fieldset>
|
||||
<fieldset class="note-setting-options"><legend>AI 评论形式</legend><div class="note-segments" role="group" aria-label="AI 评论形式">${Object.entries(noteFormats).map(([value, label]) => `<button data-note-format="${value}" aria-pressed="${noteSettings.format === value}">${label}</button>`).join('')}</div><p>默认同时生成流式长评和结构化要点,也可只保留其中一种。</p></fieldset>
|
||||
<div class="note-settings-preview"><span class="eyebrow">当前配置预览</span><p>${noteModes[noteSettings.mode]} · ${noteFormats[noteSettings.format]}</p><small>选择后即时保留于本次 Demo 会话,不调用 AI、不修改真实设置。</small></div>`;
|
||||
if ($('#note-settings-summary')) $('#note-settings-summary').textContent = `当前设置:${noteModes[noteSettings.mode]} · ${noteFormats[noteSettings.format]}。待办数量${noteSettings.showCount ? '显示' : '隐藏'}。`;
|
||||
}
|
||||
function showNoteSettings() {
|
||||
function showAppSettings(id = 'notes') {
|
||||
settingsApp = id;
|
||||
const item = items.find(entry => entry.id === id);
|
||||
if (!$('#catalog').open) { catalogMode = 'apps'; $('#search').value = ''; }
|
||||
settingsOpen = true;
|
||||
$('#catalog').dataset.variant = variant;
|
||||
$('#catalog-title').textContent = '魔法笔记设置';
|
||||
$('#catalog-description').textContent = '调整入口与 AI 评论行为。应用中心和笔记页面共享这份演示配置。';
|
||||
$('#catalog-title').textContent = `${item.name}设置`;
|
||||
$('#catalog-description').textContent = '应用中心、应用页面和全局设置共用本次演示配置。';
|
||||
$('#back-to-apps').hidden = false; $('#app-settings').hidden = false;
|
||||
for (const selector of ['.center-switch', '#search', '#catalog-items']) $(selector).hidden = true;
|
||||
renderNoteSettings();
|
||||
for (const selector of ['.center-switch', '#search', '#catalog-items', '#market-preview']) $(selector).hidden = true;
|
||||
renderAppSettings();
|
||||
if (!$('#catalog').open) $('#catalog').showModal();
|
||||
$('#back-to-apps').focus();
|
||||
}
|
||||
@@ -169,54 +188,73 @@ function notify(text) {
|
||||
}
|
||||
function renderApps() {
|
||||
$('#edit-apps').textContent = editingApps ? '完成' : '编辑';
|
||||
$('#resident-list').innerHTML = residentApps.map((id, index) => {
|
||||
$('#resident-list').innerHTML = residentApps.filter(id => enabledApps.has(id) && (variant === 'scenes' || items.find(item => item.id === id).builtin)).map(id => {
|
||||
const index = residentApps.indexOf(id);
|
||||
const item = items.find(entry => entry.id === id);
|
||||
return `<div class="resident-row" data-resident="${id}" draggable="${editingApps}"><button data-open="${id}" ${$('#page-label').textContent === item.name ? 'aria-current="page"' : ''}>${icon(item.icon)}<span>${item.name}</span>${id === 'notes' && noteSettings.showCount ? '<small class="note-count" aria-label="3 项未完成待办(示例)">3</small>' : ''}</button>${editingApps ? `<div class="edit-actions"><button data-move="${id}" data-direction="-1" aria-label="上移${item.name}" ${index === 0 ? 'disabled' : ''}>↑</button><button data-pin="${id}" aria-label="移除${item.name}">×</button><button data-move="${id}" data-direction="1" aria-label="下移${item.name}" ${index === residentApps.length - 1 ? 'disabled' : ''}>↓</button></div>` : ''}</div>`;
|
||||
}).join('') || '<p class="empty-apps">点击 +,选择常驻应用。</p>';
|
||||
const subtitles = { dock: '', grid: `${installed.size} 个已安装应用`, cards: '笔记 · 知识 · 自动化', launcher: 'Ctrl / ⌘ K', favorites: `${residentApps.length} 个应用常驻菜单`, scenes: '发现你的下一个好应用' };
|
||||
const subtitles = { dock: '', grid: '内置应用', cards: '笔记 · 知识 · 自动化 · 模型', launcher: 'Ctrl / ⌘ K', favorites: '管理常驻偏好', scenes: '未来私有市场 · 模拟' };
|
||||
$('#center-entry').innerHTML = `<button id="open-center" aria-haspopup="dialog"><span class="brand-mark">${variant === 'launcher' ? icon('search') : 'GB'}</span><span><strong>${variant === 'launcher' ? '搜索应用…' : '应用中心'}</strong>${subtitles[variant] ? `<small>${subtitles[variant]}</small>` : ''}</span><span class="entry-arrow">${variant === 'scenes' ? '↗' : '⌃'}</span></button>`;
|
||||
}
|
||||
function renderCatalog() {
|
||||
const query = $('#search').value.trim().toLowerCase();
|
||||
const matches = items.filter(item => (catalogMode === 'store' ? !item.builtin : installed.has(item.id)) && `${item.name}${item.detail}`.toLowerCase().includes(query));
|
||||
const matches = items.filter(item => (catalogMode === 'store' ? marketReady && !item.builtin : installed.has(item.id) && (variant === 'scenes' || item.builtin)) && `${item.name}${item.detail}`.toLowerCase().includes(query));
|
||||
if (variant === 'favorites') matches.sort((a, b) => Number(residentApps.includes(b.id)) - Number(residentApps.includes(a.id)));
|
||||
$('#catalog-items').innerHTML = matches.map(item => {
|
||||
const ready = installed.has(item.id); const pinned = residentApps.includes(item.id);
|
||||
return `<article class="center-app">${icon(item.icon)}<div class="center-app-info"><strong>${item.name}</strong><p>${item.builtin ? '内置应用' : '商店应用 · 示例'}</p><p class="app-detail">${variant === 'cards' ? item.summary : item.detail}</p></div><div class="center-actions">${ready ? `<button data-open="${item.id}">打开</button>${item.id === 'notes' ? '<button data-note-settings aria-label="魔法笔记设置">设置</button>' : ''}<button data-pin="${item.id}" aria-pressed="${pinned}">${pinned ? '取消常驻' : '常驻左侧'}</button>` : `<button data-install="${item.id}">模拟安装</button><button data-install-pin="${item.id}">安装并常驻</button>`}</div></article>`;
|
||||
}).join('') || '<p class="empty-apps">没有匹配的应用。可以换个关键词,或切换到商店。</p>';
|
||||
return `<article class="center-app">${icon(item.icon)}<div class="center-app-info"><strong>${item.name}</strong><p>${item.builtin ? '内置应用' : '私有市场 · 示例'}${item.id === 'models' ? ' · 计划预览' : ''}${ready ? enabledApps.has(item.id) ? ' · 已启用' : ' · 已关闭' : ''}</p><p class="app-detail">${variant === 'cards' ? item.summary : item.detail}</p></div><div class="center-actions">${ready ? `${enabledApps.has(item.id) ? `<button data-open="${item.id}">打开</button>` : ''}<button data-app-settings="${item.id}">${enabledApps.has(item.id) ? '设置' : '设置 / 重新开启'}</button><button data-pin="${item.id}" aria-pressed="${pinned}">${pinned ? '取消常驻' : '常驻左侧'}</button>` : `<button data-install="${item.id}">模拟安装</button><button data-install-pin="${item.id}">模拟安装并常驻</button>`}</div></article>`;
|
||||
}).join('') || `<p class="empty-apps">${catalogMode === 'store' && !marketReady ? '请先模拟获取私有市场目录。' : '没有匹配的应用,请换个关键词。'}</p>`;
|
||||
$('#yaml-app').innerHTML = items.filter(item => installed.has(item.id)).map(item => `<option value="${item.id}">${item.name}</option>`).join('');
|
||||
document.querySelectorAll('.center-switch button').forEach(button => button.setAttribute('aria-pressed', String(button.dataset.catalog === catalogMode)));
|
||||
}
|
||||
function openCatalog(mode = 'apps') {
|
||||
importDraft = null; $('#import-preview').hidden = true;
|
||||
if (variant !== 'scenes') mode = 'apps';
|
||||
settingsOpen = false; $('#back-to-apps').hidden = true; $('#app-settings').hidden = true;
|
||||
for (const selector of ['.center-switch', '#search', '#catalog-items']) $(selector).hidden = false;
|
||||
$('.center-switch').hidden = variant !== 'scenes';
|
||||
$('#market-preview').hidden = mode !== 'store';
|
||||
catalogMode = mode; $('#catalog').dataset.variant = variant;
|
||||
$('#catalog-title').textContent = mode === 'store' ? '发现新应用' : '应用中心';
|
||||
$('#catalog-description').textContent = mode === 'store' ? '安装你需要的应用,自主决定是否常驻左侧菜单。' : '打开应用,或把常用应用留在左侧。系统工具请使用右侧工作栏。';
|
||||
$('#catalog-title').textContent = mode === 'store' ? '私有市场 · 未来预览' : '应用中心';
|
||||
$('#catalog-description').textContent = mode === 'store' ? '目录和安装均为模拟,未接入 ShareServer。导入不会启动模型或任务。' : '内置应用无需安装;启用与常驻分别设置。系统工具请使用右侧工作栏。';
|
||||
$('#search').value = ''; renderCatalog();
|
||||
if (!$('#catalog').open) $('#catalog').showModal();
|
||||
$('#search').focus();
|
||||
}
|
||||
function openPage(name) {
|
||||
if ($('#catalog').open) $('#catalog').close();
|
||||
function openPage(name, keepSettings = false) {
|
||||
currentPage = name;
|
||||
modelConfirmation = null;
|
||||
if ($('#catalog').open && !keepSettings) $('#catalog').close();
|
||||
$('#page-label').textContent = name;
|
||||
$('#page-content').replaceChildren();
|
||||
const title = document.createElement('h1'); title.textContent = name;
|
||||
const description = document.createElement('p'); description.className = 'muted'; description.textContent = '主内容区页面预览。右侧工作栏保持当前工具状态。';
|
||||
$('#page-content').append(title, description);
|
||||
$('#page-actions').innerHTML = name === '魔法笔记' ? `<button data-note-settings>${icon('settings')} 笔记设置</button>` : '';
|
||||
const app = items.find(item => item.name === name);
|
||||
$('.composer-area').hidden = Boolean(app) || name === '设置' || name === '运行记录';
|
||||
$('#page-content').classList.toggle('app-page', $('.composer-area').hidden);
|
||||
for (const panel of ['#composer-options', '#context-details']) if ($(panel).matches(':popover-open')) $(panel).hidePopover();
|
||||
$('#page-actions').replaceChildren();
|
||||
if (app && !enabledApps.has(app.id)) {
|
||||
description.textContent = '此应用已关闭,当前页面不可用。已有数据和选择保留;此操作不停止后台任务或模型服务。';
|
||||
renderApps(); return;
|
||||
}
|
||||
if (app?.id === 'models') renderModels();
|
||||
if (name === '魔法笔记' || name === '设置') {
|
||||
const summary = document.createElement('p'); summary.id = 'note-settings-summary'; summary.className = 'muted'; $('#page-content').append(summary);
|
||||
if (name === '设置') { const button = document.createElement('button'); button.dataset.noteSettings = ''; button.textContent = '魔法笔记设置'; $('#page-content').append(button); }
|
||||
renderNoteSettings();
|
||||
renderAppSettings();
|
||||
}
|
||||
document.querySelectorAll('.navigation button').forEach(button => button.classList.toggle('selected', button.dataset.page === name));
|
||||
renderApps(); notify(`已打开${name}`);
|
||||
renderApps(); if (!keepSettings) notify(`已打开${name}`);
|
||||
}
|
||||
function selectVariant(name) {
|
||||
variant = name; $('.app').dataset.variant = name;
|
||||
document.querySelectorAll('button[data-variant]').forEach(button => button.setAttribute('aria-pressed', String(button.dataset.variant === name)));
|
||||
[$('#variant-label').textContent, $('#variant-description').textContent, $('#design-tradeoff').textContent] = variants[name];
|
||||
history.replaceState(null, '', `#${name}`); renderApps();
|
||||
if ($('#catalog').open) openCatalog();
|
||||
const pageApp = items.find(item => item.name === currentPage);
|
||||
if (name !== 'scenes' && pageApp && !pageApp.builtin) openPage('当前对话');
|
||||
}
|
||||
function selectTool(id) {
|
||||
const content = {
|
||||
@@ -228,6 +266,76 @@ function selectTool(id) {
|
||||
$('#tool-content').innerHTML = content[id]; $('#tool-content').setAttribute('aria-labelledby', `tab-${id}`);
|
||||
document.querySelectorAll('[data-tool]').forEach(button => { button.setAttribute('aria-selected', String(button.dataset.tool === id)); button.tabIndex = button.dataset.tool === id ? 0 : -1; });
|
||||
}
|
||||
function renderModels() {
|
||||
$('#page-content').innerHTML = `<h1>本机推理监控</h1><p class="muted">统一查看语音、OCR、向量等本机推理任务与执行服务。任务进度、引擎及占用均为模拟数据。</p><h2>推理任务与服务</h2><p class="muted">下方操作作用于执行服务;启动或加载服务不会自动恢复已中断的任务。</p><div class="model-list">${modelServices.map(service => {
|
||||
const external = service.management === '外部管理';
|
||||
const active = service.management === '进程内' ? '已加载' : '运行中';
|
||||
const actions = external ? ['config'] : service.management === '进程内' ? [service.running ? 'release' : 'load'] : service.running ? ['stop', 'restart'] : ['start'];
|
||||
return `<article class="model-service"><strong>${service.name}</strong><p>${service.interrupted ? '任务已中断,需由原应用重新发起' : service.purpose}</p><p>执行服务:${service.running ? active : '未运行'} · 内存:${service.running ? service.usage : '已释放(模拟)'}</p><p>使用方:${service.interrupted ? '暂无活动使用方' : service.users}</p><div class="model-actions">${actions.map(action => `<button data-model="${service.id}" data-model-action="${action}">${modelActionLabels[action]}</button>`).join('')}</div>${modelConfirmation?.id === service.id ? `<div class="inline-confirm" role="group" aria-label="推理服务操作确认"><p>${modelActionLabels[modelConfirmation.action]} ${service.name} 将影响 ${service.interrupted ? '该执行服务,当前没有活动任务' : service.users}。仅模拟状态变化。</p><button data-model-cancel>取消</button><button data-model-confirm>确认${modelActionLabels[modelConfirmation.action]}</button></div>` : ''}<details><summary>引擎与错误详情</summary><p>${service.engine} · ${service.management}</p><p>最近错误:无(示例)。操作仅更新本页内存状态。</p></details></article>`;
|
||||
}).join('')}</div>`;
|
||||
}
|
||||
const modelActionLabels = { start: '启动', stop: '停止', restart: '重启', load: '加载', release: '释放', config: '连接配置' };
|
||||
function applyModelAction(id, action) {
|
||||
const service = modelServices.find(entry => entry.id === id);
|
||||
service.running = !['stop', 'release'].includes(action);
|
||||
if (['stop', 'release', 'restart'].includes(action)) service.interrupted = true;
|
||||
modelConfirmation = null;
|
||||
renderModels();
|
||||
$(`[data-model="${id}"]`)?.focus();
|
||||
notify(`已模拟${modelActionLabels[action]} ${service.name}`);
|
||||
}
|
||||
function previewImport() {
|
||||
importDraft = null;
|
||||
const preview = $('#import-preview'); preview.hidden = false; preview.replaceChildren();
|
||||
const values = {};
|
||||
const fields = ['schema', 'id', 'name', 'version', 'entry', 'dependencies', 'resources'];
|
||||
for (const line of $('#yaml-input').value.split(/\r?\n/).filter(line => line.trim() && !line.trim().startsWith('#'))) {
|
||||
const match = line.match(/^([a-z]+):\s*(.+)$/);
|
||||
if (!match || !fields.includes(match[1]) || Object.hasOwn(values, match[1])) { preview.textContent = '仅支持示例中的七个扁平字段,不接受重复或未知字段;请修正后重新预览。'; return; }
|
||||
values[match[1]] = match[2].trim();
|
||||
}
|
||||
if (fields.some(field => !values[field]) || values.schema !== 'mock-v1' || !/^[a-z][a-z0-9-]*$/.test(values.id) || values.entry !== 'main') { preview.textContent = '请填写完整 mock-v1 示例,id 使用小写字母、数字或连字符,entry 为 main。'; return; }
|
||||
const existing = items.find(item => item.id === values.id);
|
||||
const dependencies = values.dependencies === 'none' ? [] : values.dependencies.split(',').map(value => value.trim());
|
||||
const missing = dependencies.filter(value => !value.startsWith('builtin:') || !installed.has(value.slice(8)) || !enabledApps.has(value.slice(8)));
|
||||
const blocked = existing?.builtin || missing.length || values.resources !== 'bundled';
|
||||
const conflict = installed.has(values.id);
|
||||
const text = document.createElement('p');
|
||||
text.textContent = `${values.name} · ${values.id} · v${values.version} · 主内容区入口。依赖:${values.dependencies}。${missing.length ? `缺失或关闭:${missing.join('、')}。` : '依赖已具备(模拟)。'}资源:${values.resources}。${values.resources !== 'bundled' ? '需补充外部资源,不能安装为可用。' : ''}${existing?.builtin ? '与内置应用冲突,不允许覆盖。' : conflict ? '同标识冲突:需显式更新定义,保留启用与常驻偏好。' : '没有同标识冲突。'}`;
|
||||
preview.append(text);
|
||||
const cancel = document.createElement('button'); cancel.textContent = '取消'; cancel.onclick = () => { importDraft = null; preview.hidden = true; $('#yaml-preview').focus(); }; preview.append(cancel);
|
||||
if (!blocked) {
|
||||
importDraft = values;
|
||||
const confirm = document.createElement('button'); confirm.id = 'import-confirm'; confirm.textContent = conflict ? '确认模拟更新定义' : '确认模拟安装';
|
||||
confirm.onclick = () => {
|
||||
if (!importDraft) return;
|
||||
const draft = importDraft;
|
||||
const item = items.find(entry => entry.id === draft.id);
|
||||
if (!item) items.push({ id: draft.id, name: draft.id, icon: 'layers', detail: 'YAML 导入 · 未来示例', summary: '导入的应用定义预览', builtin: false });
|
||||
// Imported text stays in textContent/textarea; catalog markup uses the validated identifier.
|
||||
definitions.set(draft.id, { ...draft });
|
||||
if (!installed.has(draft.id)) { installed.add(draft.id); enabledApps.add(draft.id); }
|
||||
importDraft = null; preview.hidden = true; renderCatalog(); renderApps(); $('#yaml-preview').focus(); notify('已模拟安装 / 更新定义,未启动模型或任务');
|
||||
};
|
||||
preview.append(confirm);
|
||||
}
|
||||
}
|
||||
$('#market-fetch').onclick = () => {
|
||||
const valid = /^https?:\/\/[^\s/]+(?:\/[^\s]*)?$/.test($('#market-url').value.trim());
|
||||
marketReady = valid;
|
||||
$('#market-status').textContent = valid ? `已模拟获取目录:${$('#market-url').value.trim()},无网络请求。` : '请输入 http(s) 市场 URL 后重试。';
|
||||
renderCatalog();
|
||||
};
|
||||
$('#market-fail').onclick = () => { marketReady = false; $('#market-status').textContent = '模拟来源不可达,请点击模拟获取目录重试。未切换公共市场,已安装应用仍可使用。'; renderCatalog(); };
|
||||
$('#market-url').oninput = () => { marketReady = false; $('#market-status').textContent = '来源已修改,请重新模拟获取目录。'; renderCatalog(); };
|
||||
$('#yaml-preview').onclick = previewImport;
|
||||
$('#yaml-input').oninput = () => { importDraft = null; $('#import-preview').hidden = true; };
|
||||
$('#yaml-export').onclick = () => {
|
||||
const item = items.find(entry => entry.id === $('#yaml-app').value);
|
||||
const definition = definitions.get(item.id) || { schema: 'mock-v1', id: item.id, name: item.name, version: '1.0', entry: 'main', dependencies: 'none', resources: 'bundled' };
|
||||
$('#yaml-output').hidden = false;
|
||||
$('#yaml-output').textContent = '# Mock schema only; no credentials or business data\n' + Object.entries(definition).map(([key, value]) => `${key}: ${value}`).join('\n');
|
||||
};
|
||||
document.addEventListener('click', event => {
|
||||
const button = event.target.closest('button'); if (!button) return;
|
||||
const data = button.dataset;
|
||||
@@ -246,13 +354,35 @@ document.addEventListener('click', event => {
|
||||
if (data.page) openPage(data.page);
|
||||
if (data.open) openPage(items.find(item => item.id === data.open).name);
|
||||
if (data.tool) selectTool(data.tool);
|
||||
if (button.hasAttribute('data-note-settings')) showNoteSettings();
|
||||
if (data.appSettings) showAppSettings(data.appSettings);
|
||||
if (data.appToggle) {
|
||||
const id = settingsApp;
|
||||
if (data.appToggle === 'enabled') enabledApps.has(id) ? enabledApps.delete(id) : enabledApps.add(id);
|
||||
else residentApps = residentApps.includes(id) ? residentApps.filter(entry => entry !== id) : [...residentApps, id];
|
||||
renderApps(); renderComposer();
|
||||
if (currentPage === items.find(item => item.id === id).name) {
|
||||
openPage(currentPage, true);
|
||||
}
|
||||
renderAppSettings(); $(`[data-app-toggle="${data.appToggle}"]`).focus();
|
||||
}
|
||||
if (data.modelAction) {
|
||||
if (data.modelAction === 'config') {
|
||||
const row = button.closest('.model-service');
|
||||
let config = row.querySelector('.model-config');
|
||||
if (!config) { config = document.createElement('p'); config.className = 'model-config'; config.textContent = '连接配置预览:Ollama · http://localhost:11434 · 外部管理。仅展示示例,不连接或保存配置。'; row.append(config); }
|
||||
} else {
|
||||
const service = modelServices.find(entry => entry.id === data.model);
|
||||
if (service.running && service.users) { modelConfirmation = { id: data.model, action: data.modelAction }; renderModels(); $('[data-model-cancel]').focus(); }
|
||||
else applyModelAction(data.model, data.modelAction);
|
||||
}
|
||||
}
|
||||
if (button.hasAttribute('data-model-cancel')) { const id = modelConfirmation.id; modelConfirmation = null; renderModels(); $(`[data-model="${id}"]`).focus(); }
|
||||
if (button.hasAttribute('data-model-confirm') && modelConfirmation) applyModelAction(modelConfirmation.id, modelConfirmation.action);
|
||||
if (data.noteToggle || data.noteMode || data.noteFormat) {
|
||||
if (data.noteToggle === 'resident') residentApps = residentApps.includes('notes') ? residentApps.filter(id => id !== 'notes') : [...residentApps, 'notes'];
|
||||
if (data.noteToggle === 'showCount') noteSettings.showCount = !noteSettings.showCount;
|
||||
if (data.noteMode) noteSettings.mode = data.noteMode;
|
||||
if (data.noteFormat) noteSettings.format = data.noteFormat;
|
||||
renderApps(); renderNoteSettings();
|
||||
renderApps(); renderAppSettings();
|
||||
const selector = data.noteToggle ? `[data-note-toggle="${data.noteToggle}"]` : data.noteMode ? `[data-note-mode="${data.noteMode}"]` : `[data-note-format="${data.noteFormat}"]`;
|
||||
$(selector).focus();
|
||||
}
|
||||
@@ -260,7 +390,7 @@ document.addEventListener('click', event => {
|
||||
if (button.id === 'edit-apps') { editingApps = !editingApps; renderApps(); $('#edit-apps').focus(); }
|
||||
if (data.pin || data.install || data.installPin) {
|
||||
const id = data.pin || data.install || data.installPin;
|
||||
if (data.install || data.installPin) installed.add(id);
|
||||
if (data.install || data.installPin) { importDraft = null; $('#import-preview').hidden = true; installed.add(id); enabledApps.add(id); notify('已模拟安装,未启动任务或模型'); }
|
||||
if (data.pin || data.installPin) residentApps = residentApps.includes(id) ? residentApps.filter(entry => entry !== id) : [...residentApps, id];
|
||||
renderApps();
|
||||
if ($('#catalog').open) { renderCatalog(); ($(`#catalog [data-pin="${id}"]`) || $('#search')).focus(); }
|
||||
@@ -282,7 +412,15 @@ $('#resident-list').addEventListener('drop', event => {
|
||||
$('#resident-list').addEventListener('dragend', () => { draggedApp = ''; });
|
||||
$('#search').addEventListener('input', renderCatalog);
|
||||
$('#close-catalog').onclick = () => $('#catalog').close();
|
||||
$('#back-to-apps').onclick = () => { const query = $('#search').value; openCatalog(catalogMode); $('#search').value = query; renderCatalog(); ($('#catalog [data-note-settings]') || $('#search')).focus(); };
|
||||
$('#back-to-apps').onclick = () => { const query = $('#search').value; openCatalog(catalogMode); $('#search').value = query; renderCatalog(); ($(`#catalog [data-app-settings="${settingsApp}"]`) || $('#search')).focus(); };
|
||||
$('#app-settings').addEventListener('keydown', event => {
|
||||
const group = event.target.closest('.note-segments');
|
||||
if (!group || !['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return;
|
||||
event.preventDefault();
|
||||
const buttons = [...group.querySelectorAll('button')];
|
||||
const index = buttons.indexOf(event.target);
|
||||
buttons[event.key === 'Home' ? 0 : event.key === 'End' ? buttons.length - 1 : (index + (event.key === 'ArrowRight' ? 1 : -1) + buttons.length) % buttons.length].click();
|
||||
});
|
||||
function toggleWorkbar(visible) { $('.workbar').hidden = !visible; $('#toggle-workbar').setAttribute('aria-expanded', String(visible)); }
|
||||
$('#toggle-workbar').onclick = () => toggleWorkbar($('.workbar').hidden);
|
||||
$('#close-tool').onclick = () => { toggleWorkbar(false); $('#toggle-workbar').focus(); };
|
||||
@@ -300,7 +438,7 @@ document.addEventListener('keydown', event => {
|
||||
if (event.key === 'Enter' && document.activeElement === $('#search')) { event.preventDefault(); actions[0].click(); }
|
||||
});
|
||||
$('#theme').onclick = () => { const dark = document.documentElement.dataset.theme !== 'dark'; document.documentElement.dataset.theme = dark ? 'dark' : 'light'; $('#theme').textContent = dark ? '切换浅色' : '切换深色'; };
|
||||
$('#reset').onclick = () => { installed = new Set(items.filter(item => item.builtin).map(item => item.id)); residentApps = ['notes', 'knowledge']; editingApps = false; renderApps(); notify('应用安装与常驻配置已恢复默认'); };
|
||||
$('#reset').onclick = () => { installed = new Set(items.filter(item => item.builtin).map(item => item.id)); residentApps = [...installed].filter(id => id !== 'models'); enabledApps = new Set(installed); Object.assign(noteSettings, { showCount: true, mode: 'immediate', format: 'combined' }); editingApps = false; definitions.clear(); importDraft = null; $('#import-preview').hidden = true; renderApps(); renderComposer(); openPage('当前对话'); notify('应用启用、常驻和笔记设置已恢复默认'); };
|
||||
let demoRunning = false;
|
||||
let demoRecording = false;
|
||||
let demoAttachments = [];
|
||||
@@ -317,8 +455,11 @@ function renderComposer() {
|
||||
const select = $(`#${id}`);
|
||||
if (select.value) addTag(select.selectedOptions[0].textContent, () => { select.value = ''; });
|
||||
}
|
||||
document.querySelectorAll('[data-knowledge]:checked').forEach(input => addTag(input.dataset.knowledge, () => { input.checked = false; }));
|
||||
if ($('#composer-retrieval').value === 'always') addTag('每次检索', () => { $('#composer-retrieval').value = 'auto'; });
|
||||
document.querySelectorAll('[data-knowledge-control]').forEach(element => { element.hidden = !enabledApps.has('knowledge'); });
|
||||
if (enabledApps.has('knowledge')) {
|
||||
document.querySelectorAll('[data-knowledge]:checked').forEach(input => addTag(input.dataset.knowledge, () => { input.checked = false; }));
|
||||
if ($('#composer-retrieval').value === 'always') addTag('每次检索', () => { $('#composer-retrieval').value = 'auto'; });
|
||||
}
|
||||
tags.hidden = !tags.childElementCount;
|
||||
$('#stop').hidden = !demoRunning;
|
||||
$('#send').disabled = !$('#message').value.trim() && $('#composer-command').value !== 'command';
|
||||
|
||||
@@ -4,20 +4,19 @@
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 状态 | 设计中 |
|
||||
| 版本 | 0.2 |
|
||||
| 日期 | 2026-09-08 |
|
||||
| 状态 | 应用中心与本机推理监控已接入源码,整体验收待完成 |
|
||||
| 版本 | 0.4 |
|
||||
| 日期 | 2026-09-17 |
|
||||
| 适用产品 | GoodBuddy 桌面端 |
|
||||
| 关联设计 | [统一界面设计系统](../../../UI-DESIGN.md)、[助手工作栏 PRD](../assistant-workbar/prd.md) |
|
||||
|
||||
## 1. 背景
|
||||
|
||||
计划修订说明:当前实施范围以[第 8 节](#8-分期)为准。本文其余章节仍包含 v0.2 的
|
||||
左下角系统工具方案;启动实现前须同步功能要求、验收及配套设计,旧快捷工具条不再列入实施。
|
||||
本版已同步应用中心方案,分期见[第 8 节](#8-分期)。左下角用于访问和管理应用,系统工具保留在右侧工作栏。
|
||||
|
||||
主侧栏当前把“当前对话”“魔法笔记”“知识库”“智能心跳”和“运行记录”放在同一导航组,
|
||||
底部长期显示“GB 本地工作区”和平台架构。底部信息不能反映用户可以执行的操作,文件、终端
|
||||
和浏览器则需要先找到右侧助手工作栏后才能打开。
|
||||
原主侧栏把“当前对话”“魔法笔记”“知识库”“智能心跳”和“运行记录”放在同一导航组,
|
||||
底部显示“GB 本地工作区”和平台架构。当前已将底部改为应用中心及独立设置入口,文件、终端
|
||||
和浏览器继续从右侧助手工作栏打开。
|
||||
|
||||
后续插件可能提供完整页面,也可能只提供辅助面板或命令。若不先区分系统入口、应用和工具,
|
||||
插件接入后会同时争用主导航、侧栏底部和助手工作栏,用户也无法判断入口会切换页面还是打开
|
||||
@@ -25,16 +24,17 @@
|
||||
|
||||
## 2. 产品定义
|
||||
|
||||
### 2.1 三类入口
|
||||
### 2.1 入口分类
|
||||
|
||||
| 类型 | 首期成员 | 默认位置 | 用户控制范围 |
|
||||
| --- | --- | --- | --- |
|
||||
| 系统入口 | 当前对话、运行记录、设置 | 主导航固定位置或侧栏底部 | 不可隐藏、卸载或改变类型 |
|
||||
| 应用 | 魔法笔记、知识库、智能心跳 | 主导航应用区 | 可控制是否显示;隐藏不删除数据 |
|
||||
| 系统工具 | 文件浏览器、终端、浏览器、资源监控 | 侧栏底部快捷工具栏 | 首期固定提供,不可卸载 |
|
||||
| 始终显示的内置应用 | 知识库 | 主导航应用区及应用中心 | 始终启用并常驻,可排序,不提供启用或常驻开关;保留工作区实际配置 |
|
||||
| 可选内置应用 | 智能心跳、魔法笔记、本机推理监控 | 应用中心及主导航常驻区 | 独立控制启用、常驻与排序;保留数据 |
|
||||
| 系统工具 | 文件浏览器、终端、浏览器、资源监控 | 右侧助手工作栏 Tab 和“+”目录 | 自带且不可卸载,按工作栏规则管理实例 |
|
||||
|
||||
运行记录是系统审计页面,不属于工具。文件浏览器、终端、浏览器和资源监控是系统工具;它们
|
||||
的快捷入口位于左下角,实际界面继续由右侧助手工作栏承载。
|
||||
的入口与实际界面均由右侧助手工作栏承载,不列入应用中心。
|
||||
|
||||
### 2.2 后续插件类型
|
||||
|
||||
@@ -43,10 +43,10 @@
|
||||
|
||||
## 3. 目标
|
||||
|
||||
- 用户从侧栏底部一次点击即可打开文件、终端、浏览器或资源监控。
|
||||
- 主导航只承载完整页面,辅助工具不占用一级页面位置。
|
||||
- 用户从侧栏底部进入应用中心,查找、打开和配置内置应用。
|
||||
- 主导航承载应用入口,本机推理监控打开 Modal,其余内置应用打开完整页面;辅助工具由工作栏承载。
|
||||
- 运行记录始终可发现,不受应用显示设置和插件状态影响。
|
||||
- 内置应用可以从一个设置位置控制主导航可见性,隐藏后仍可恢复。
|
||||
- 四个内置应用均支持排序;智能心跳、魔法笔记和本机推理监控可独立设置启用与常驻,知识库始终显示,未常驻的已启用应用仍可从应用菜单打开。
|
||||
- 为插件应用和插件工具保留明确的入口契约,不提前建设插件安装能力。
|
||||
|
||||
## 4. 非目标
|
||||
@@ -56,7 +56,7 @@
|
||||
- 不把文件树、终端或浏览器重新实现到左侧栏。
|
||||
- 不改变助手工作栏的面板实例、停靠、执行空间和安全边界。
|
||||
- 不允许隐藏运行记录、当前对话或设置。
|
||||
- 不因隐藏应用而删除魔法笔记、知识库或智能心跳数据。
|
||||
- 不因隐藏可选应用而删除业务数据。
|
||||
- 不把“显示在侧边栏”解释为停止智能心跳计划或后台任务。
|
||||
- 不提供任意本机程序启动器。
|
||||
- 首期资源监控不采集远程主机、GPU、显存或网络吞吐,不提供后台告警和历史持久化。
|
||||
@@ -75,42 +75,43 @@
|
||||
|
||||
### FR-3 内置应用区
|
||||
|
||||
魔法笔记、知识库和智能心跳显示在当前对话与运行记录之间。应用入口打开对应一级页面,保留
|
||||
现有徽标、范围和页面状态。用户可以在设置中控制其主导航可见性。
|
||||
主导航在当前对话之后按共享保存顺序显示应用,知识库始终显示,可选应用仅显示已启用且常驻项;运行记录位于应用区之后。四个应用均可排序。本机推理监控入口打开独立 Modal,其余应用打开对应一级页面,保留
|
||||
现有徽标、范围和页面状态。管理 Modal 列出全部内置应用,知识库只提供“打开”和排序。
|
||||
|
||||
### FR-4 隐藏应用
|
||||
|
||||
隐藏应用只移除主导航入口,不删除数据、不修改业务启停状态,也不终止正在进行的操作。显隐
|
||||
Switch 更新成功后主导航立即生效;更新失败时恢复原状态并允许重试。用户可从设置恢复入口。
|
||||
取消常驻只移除主导航捷径,不删除数据、不修改启用状态,也不终止正在进行的操作。
|
||||
关闭可选应用则同步移除业务入口,保留常驻偏好;知识库不受这些偏好影响。心跳关闭后的执行边界见[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)。
|
||||
配置保存成功后各入口使用同一已确认状态,失败时保持原状态并允许重试。
|
||||
|
||||
### FR-5 系统工具快捷栏
|
||||
### FR-5 应用中心入口
|
||||
|
||||
侧栏底部提供文件浏览器、终端、浏览器和资源监控四个图标按钮,并保留独立设置按钮。每个
|
||||
按钮具有可见工具提示和可访问名称。首期系统工具顺序固定为文件、终端、浏览器、资源监控。
|
||||
侧栏底部提供应用中心和独立设置入口。点击应用中心向上展开锚定按钮的轻量菜单,无模态遮罩。菜单列出已启用应用,不按是否打开过、历史记录或常驻状态筛选;点击应用行关闭菜单,本机推理监控打开 Modal,其余应用在主内容区打开页面。
|
||||
菜单底部“管理应用”关闭菜单并打开既有管理 Modal,在其中浏览、搜索全部内置应用,为已关闭的可选应用提供设置与重新开启入口。无搜索结果时显示空状态并可清除搜索。
|
||||
首期不展示商店、安装、推荐或系统工具清单。入口具有可访问名称,Escape 关闭后恢复触发位置的焦点。
|
||||
|
||||
### FR-6 文件浏览器入口
|
||||
|
||||
点击文件按钮时展开助手工作栏,并聚焦默认存在且不可关闭的“工作区”单实例。该实例保持
|
||||
在右侧选择“工作区”Tab 时聚焦默认存在且不可关闭的单实例。该实例保持
|
||||
“跟随当前上下文”或“固定到指定对象”状态;迁移或布局修复发现实例缺失时补回,并默认跟随
|
||||
当前项目。没有项目时使用已配置的默认工作区。跟随上下文所需的路径不可用时,面板显示修复
|
||||
入口;已有有效固定目标不受当前项目和默认工作区状态影响。
|
||||
入口;已有有效固定目标不受当前项目和默认工作区状态影响。固定目标失效时修复该目标,不静默回退到其他目录。
|
||||
|
||||
### FR-7 终端入口
|
||||
|
||||
点击终端按钮时展开助手工作栏。当前执行空间有正在启动或运行中的终端时聚焦最近使用的实例;
|
||||
没有时创建一个绑定当前执行空间的新终端,已退出、已中断或失败的历史 Tab 保持不变。没有
|
||||
项目时当前执行空间为本机 Home。创建额外并行终端继续使用工作栏现有的“+”目录入口。
|
||||
选择已有终端 Tab 仅激活该实例,保持目标、会话和历史状态。通过工作栏“+”选择终端时
|
||||
创建绑定当前执行空间的新实例,不按目标复用已有实例;无项目时使用本机 Home。
|
||||
远程项目的新实例绑定其托管 SSH 执行空间和权威目录。创建失败保留当前主页面与其他实例。
|
||||
|
||||
### FR-8 浏览器入口
|
||||
|
||||
点击浏览器按钮时展开助手工作栏,并优先聚焦绑定当前 Conversation 的最近使用浏览器实例;
|
||||
没有匹配实例时创建一个。没有活动 Conversation 时聚焦最近使用的浏览器实例;完全不存在时
|
||||
创建待绑定空实例。该快捷入口不会因连续点击创建重复实例;用户通过工作栏“+”选择浏览器时
|
||||
则始终创建新实例。入口不打开系统默认浏览器,也不创建旁路 WebView。
|
||||
选择已有浏览器 Tab 保留其绑定目标、页面和导航状态;通过工作栏“+”选择浏览器时创建新实例,
|
||||
有活动 Conversation 时绑定该会话,否则创建待绑定空实例。同一 Conversation 的实例共享隔离
|
||||
登录上下文,页面与导航状态独立。入口不打开系统默认浏览器,也不创建旁路 WebView。
|
||||
|
||||
### FR-9 资源监控入口
|
||||
|
||||
点击资源监控按钮时展开助手工作栏,并打开或聚焦单实例的本机“资源监控”面板。面板显示
|
||||
通过右侧工作栏“+”打开或聚焦单实例的本机“资源监控”,已有面板直接选择其 Tab。面板显示
|
||||
整机 CPU 使用率、内存使用量与总量、磁盘读写速率,以及 GoodBuddy 桌面进程的 CPU、内存
|
||||
和进程数摘要。指标不可用时显示具体项目和原因,不能显示为 `0`。
|
||||
|
||||
@@ -119,38 +120,43 @@ Renderer 内存中最多保留最近 5 分钟。任一条件不满足时停止
|
||||
也不据此产生侧栏告警。首次采样及停止后恢复的首次采样只建立 CPU 和磁盘 IO 速率基线,
|
||||
不显示伪造的 `0` 值。
|
||||
|
||||
### FR-10 快捷入口状态
|
||||
### FR-10 工作栏入口状态
|
||||
|
||||
工具面板已激活时,对应按钮显示选中状态;面板存在但未激活时保持普通状态。运行中、错误或
|
||||
工具面板已激活时,对应 Tab 显示选中状态;面板存在但未激活时保持普通状态。运行中、错误或
|
||||
等待用户处理可以显示徽标,但必须有可访问文字,且不得自动抢占当前面板。
|
||||
|
||||
### FR-11 应用导航设置
|
||||
|
||||
设置页提供“应用与工具”管理入口。首期“应用”区域列出三个内置应用并提供“显示在侧边栏”
|
||||
Switch,切换后立即持久化。系统工具区域列出四项内置固定能力,不提供虚假的卸载或停用按钮。
|
||||
管理 Modal 的四个应用行均提供上下移动和拖动排序,不受启用或常驻状态限制;管理列表、菜单和侧栏共用一份顺序。箭头边界按完整顺序计算,搜索过滤不丢失隐藏项。智能心跳、魔法笔记和本机推理监控另提供应用设置、常驻管理;知识库提供“打开”和排序,不添加通用空设置页或开关。原工作区配置入口及心跳单条计划 enabled 保持不变。设置中“启用应用”与“常驻左侧菜单”分别保存,
|
||||
修改即时提交;请求期间串行处理并禁用会修改同一配置的控件,失败就地反馈。
|
||||
魔法笔记设置统一从应用中心对应行进入,笔记页与全局设置不再提供重复入口。持久化成功后的设置变更同步覆盖工具配置写入;每次重开中心先刷新并锁定修改,较新的确认快照优先于迟到的读取结果。
|
||||
|
||||
### FR-12 偏好持久化
|
||||
|
||||
应用可见性按设备持久化。升级时保留当前行为:已有可用内置应用继续显示;魔法笔记原有功能
|
||||
启停设置保持原意,不由新的导航可见性覆盖。
|
||||
四个应用的顺序及可选应用启用、常驻按设备持久化,重启后恢复。默认顺序为知识库、智能心跳、魔法笔记、本机推理监控。心跳应用默认关闭,其余应用默认启用;心跳和魔法笔记默认常驻,本机推理监控默认不常驻;
|
||||
魔法笔记待办数量默认显示,评论方式与形式沿用现有默认值。迁移保留已保存的启用、常驻和排序选择,
|
||||
旧魔法笔记入口开关绑定业务启用值,迁为“启用应用”并保留原值;常驻使用独立配置,
|
||||
只有已发布的独立显隐偏好才映射到常驻,不能由常驻推导业务启停。旧可选应用顺序补入知识库、智能心跳默认位置并保留已有相对顺序,具体补齐规则见[技术设计](./technical-design.md#43-持久化与-ipc)。`heartbeatEnabled` 的缺失值与保存值按[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)处理;不读取未发布草案中的 `knowledgeEnabled`。
|
||||
|
||||
### FR-13 窄窗口行为
|
||||
|
||||
窄窗口下主侧栏仍按现有抽屉方式显示。系统工具按钮保持单行且不与设置按钮重叠;空间不足时
|
||||
收起文字和非必要分隔,不隐藏四个系统工具或设置。
|
||||
窄窗口下主侧栏仍按现有抽屉方式显示,应用中心和设置保持可操作且不重叠。应用列表可滚动,
|
||||
搜索、设置及键盘排序不依赖拖拽;工具沿用右侧工作栏响应式规则,不搬到左侧。
|
||||
|
||||
### FR-14 项目活动汇总
|
||||
|
||||
用户在任一项目中都能查看跨项目的运行中与待处理会话,并从活动列表直接进入所选会话。
|
||||
汇总入口仅在有运行中或待处理会话时显示在项目选择器下方,无活动时整行隐藏。点击后先显示有活动会话的项目,
|
||||
用户在任一项目中都能查看跨项目的待处理、运行中与已完成未查看会话,并从活动列表直接进入所选会话。
|
||||
汇总入口仅在有上述活动行时显示在项目选择器下方,无活动行时整行隐藏。点击后先显示有活动会话的项目,
|
||||
悬停或点击项目展开会话子菜单,点击会话进入该条对话。项目菜单保留原有分组与顺序,并显示
|
||||
各项目的活动数量;级联菜单内的项目顺序不随待处理或运行中数量变化。
|
||||
各项目的活动数量;级联菜单内的项目顺序不随各类活动数量变化。
|
||||
汇总不受当前项目、搜索条件或最近历史条数限制。计数与排除规则见
|
||||
[活动逻辑](./logic-design.md#9-项目活动汇总),交互见[活动界面](./ui-design.md#10-项目活动汇总)。
|
||||
|
||||
验收覆盖跨项目后台运行、同会话多任务去重、提问与审批优先、较早的仍在运行或等待处理的
|
||||
会话可见,以及从带搜索条件的页面或有未保存修改的设置页跳转。结束后仅有未读标记的
|
||||
会话不保留在活动汇总中;已完成未读面板不属于本项交付。
|
||||
会话可见,以及从带搜索条件的页面或有未保存修改的设置页跳转。成功完成后保留“已完成”
|
||||
通知,直到用户实际查看该会话;从活动菜单或普通侧栏进入均可清除。设置覆盖下的聊天及
|
||||
隐藏文档不算查看。完成通知仅保留在当前应用会话内,初始任务历史不补发通知,失败和取消
|
||||
不产生完成通知;普通未读标记本身不构成活动。
|
||||
|
||||
## 6. 权限与安全
|
||||
|
||||
@@ -167,49 +173,46 @@ Switch,切换后立即持久化。系统工具区域列出四项内置固定
|
||||
- 主导航能明确区分固定系统入口和可配置内置应用。
|
||||
- 运行记录在所有应用显隐组合下保持可见并可打开。
|
||||
- 左下角不再用“本地工作区”与系统架构占据主要空间。
|
||||
- 文件、终端、浏览器和资源监控均可从左下角打开正确的工作栏面板,且不产生第二套内容界面。
|
||||
- 连续点击系统工具不会无意创建重复的浏览器或终端实例;工作区始终复用不可关闭的固定实例。
|
||||
用户通过工作栏“+”可以显式创建额外浏览器或终端实例。
|
||||
- 应用菜单只列已启用应用,点击行关闭菜单;本机推理监控打开保留底层工作区的 Modal,其余应用进入主内容区。“管理应用”打开可搜索的既有 Modal。四个应用均可排序,知识库始终可用,三个可选应用可管理启用与常驻,首期无商店入口。
|
||||
- 系统工具仅从右侧 Tab 与“+”访问;选择 Tab 不创建或改绑实例,“+”可显式创建额外浏览器或终端。
|
||||
- 资源监控以 2 秒间隔更新,保留不超过 150 个样本;面板不活动、工作栏收起或窗口隐藏、
|
||||
最小化后停止请求新样本。
|
||||
- CPU、内存、磁盘 IO 或 GoodBuddy 指标不可用时,页面区分部分可用与整体失败。
|
||||
- 隐藏内置应用不删除数据、不停止后台计划,并能从设置恢复;保存失败时入口保持原状。
|
||||
- 取消常驻不删除数据、不停止后台计划;关闭应用按统一规则联动业务入口,保存失败时各处保持原状。
|
||||
- 魔法笔记设置迁移保留已有选择,应用中心与笔记页共用同一配置。
|
||||
- 键盘、屏幕阅读器、浅色主题、深色主题和窄窗口均可完成相同操作。
|
||||
|
||||
## 8. 分期
|
||||
|
||||
### 首期
|
||||
|
||||
- 将侧栏最底部改为内置应用中心入口,设置按钮保持独立;具体列表、宫格或卡片样式从
|
||||
Demo 中收敛确定。
|
||||
- 应用中心提供魔法笔记、知识库、智能心跳的浏览、搜索与打开,不要求安装。
|
||||
- 支持应用常驻主导航、取消常驻和排序;未常驻应用仍可从应用中心打开,隐藏不删除数据或停止后台任务。
|
||||
- 应用中心和应用页面就近提供已有功能设置,共享真实设置来源;已有全局设置入口定位到同一界面。
|
||||
- 侧栏最底部应用中心打开向上锚定的轻量菜单,无模态遮罩;底部“管理应用”进入与设置相同的整窗 Modal 和可搜索应用列表,设置按钮保持独立。
|
||||
- 应用中心提供魔法笔记、知识库、智能心跳及本轮追加的本机推理监控入口,不要求安装。本机推理监控 Modal 与服务单独实现和验证。
|
||||
- 四个应用共享排序;支持智能心跳、魔法笔记、本机推理监控常驻主导航及取消常驻,知识库始终显示。已启用未常驻应用仍可从菜单打开,取消常驻不删除数据或停止后台任务。
|
||||
- 通用应用设置统一从应用中心对应行进入;应用页面保留实际业务配置,本机推理监控保留“模型与连接设置”。
|
||||
魔法笔记优先接入待办数量、AI 评论方式和评论形式,入口显示与常驻控制统一,避免重复开关。
|
||||
- 文件、终端、浏览器保留在右侧工作栏 Tab,不在左下角复制入口,也不纳入应用中心。
|
||||
- 原计划的本机资源监控仍作为系统工具交付项,在右侧增加单实例面板;与应用中心分别实现和验证。
|
||||
- 保持运行记录为固定系统入口。
|
||||
|
||||
首期验收需覆盖:无商店入口时可以访问全部内置应用;取消常驻后仍可重新打开和配置;
|
||||
首期验收需覆盖:无商店入口时可从菜单访问已启用应用,并从管理 Modal 恢复关闭的可选应用;取消常驻后仍可重新打开和配置;
|
||||
不同设置入口状态一致;常驻排序重启后保留;右侧工具切换不改变当前应用页面。
|
||||
本节是范围调整,Demo 交互通过不代表正式功能已经实现。
|
||||
|
||||
### 补充要求:魔法笔记迁移与入口联动
|
||||
|
||||
- 将现有魔法笔记设置迁入应用中心的应用设置界面,笔记页面内的设置入口访问同一份配置。
|
||||
魔法笔记可常驻主导航;原全局设置位置如保留入口,应定位到共同设置界面。
|
||||
- 将现有魔法笔记设置迁入应用中心对应行的设置界面,移除笔记页面内的重复入口。
|
||||
魔法笔记可常驻主导航;移除原全局设置入口,保留已有配置值和业务数据。
|
||||
- 魔法笔记相关布尔开关默认全部开启,包括入口显示(常驻)和未完成待办数量显示。
|
||||
AI 评论方式、评论形式是单选项,沿用现有默认值,不把“全部开启”解释为同时选中多个模式。
|
||||
新默认值适用于未配置状态,迁移时保留用户已明确保存的选择。
|
||||
- 应用关闭后的入口联动必须覆盖主导航和会话页面,不能只更新应用中心卡片或一个菜单。
|
||||
知识库关闭后,左侧知识库菜单及会话中的知识库入口按钮同步移除;重新开启后按配置恢复。
|
||||
- “取消常驻”只移除主导航捷径;“关闭应用”移除该应用在主导航、会话等业务界面的入口。
|
||||
应用中心保留管理和重新开启入口。原型中仅有常驻控制,正式设计需补齐这两种状态,避免混用。
|
||||
- 入口变化不删除已有笔记或知识数据。知识库已打开页面、会话已关联知识以及后台任务的处理,
|
||||
在详细逻辑同步时分别定义,不能仅凭隐藏按钮推断功能或后台行为已经停用。
|
||||
- 知识库侧栏入口始终存在且可调整位置;会话知识范围、检索方式、标签及请求行为继续遵守知识库自身规则。心跳入口按启用和常驻显示,单条计划 enabled 仍由心跳页面管理。
|
||||
- 可选应用“取消常驻”只移除主导航捷径;“关闭应用”移除该应用在业务界面的入口。
|
||||
应用中心保留管理和重新开启入口,重新开启沿用常驻偏好,不自动导航。
|
||||
- 入口变化不删除已有数据。关闭后已打开的可选应用页显示不可用状态及恢复设置入口。
|
||||
已发出的请求和后台任务沿用各功能的生命周期规则,关闭入口不新增强制取消或重放行为。
|
||||
|
||||
补充验收:从任一配置入口关闭知识库,主导航和当前会话按钮立即同步移除,切换会话、
|
||||
重启后也不重新出现;重新开启后恢复相应入口。验证魔法笔记未配置时的默认开启,以及
|
||||
补充验收:关闭全部可选应用后,知识库侧栏入口仍可用,知识会话入口及心跳单条计划状态不变;管理 Modal 的知识库行提供“打开”和排序,不能启停或取消常驻。心跳执行边界按[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)验收。验证魔法笔记未配置时的默认开启,以及
|
||||
已有关闭选择在迁移后保留。设置保存失败时各入口保持原状态,不能出现互相矛盾的显示。
|
||||
|
||||
### 后续:应用商店与扩展
|
||||
@@ -227,12 +230,13 @@ Switch,切换后立即持久化。系统工具区域列出四项内置固定
|
||||
市场面向组织私有化部署。用户配置市场 URL 后即可获取该来源的应用目录与详情,并按需获取
|
||||
应用;需要认证的来源复用其登录流程。市场不可达时说明原因并允许重试,不影响已安装应用
|
||||
和内置应用使用,也不自动切换到公共市场。市场 URL 只影响应用市场,不改变模型、桌面更新
|
||||
或 Runtime 的下载源。
|
||||
或 Runtime 的下载源。市场 URL 按设备保存,重启后保留;认证失效时提示重新认证。
|
||||
|
||||
应用支持以 `.yml` 导入、导出,兼容 `.yaml` 扩展名,用于组织内部共享、备份和迁移。
|
||||
导出内容为应用定义及可迁移配置,包含格式版本、应用标识、应用版本、入口声明和依赖说明;
|
||||
凭据、会话、笔记及知识库业务数据不随应用定义导出。导入前展示应用摘要、缺失依赖和与已有
|
||||
应用的冲突;同标识应用由用户选择更新或取消,不静默覆盖本机配置。
|
||||
应用的冲突;同标识应用由用户选择更新或取消,不静默覆盖本机配置。成功后展示实际版本与可用状态;
|
||||
获取、校验或更新失败保留原已安装版本与配置,并显示可重试原因。
|
||||
|
||||
YAML 描述文件不能代替模型权重或插件程序包。有外部依赖时应明确资源引用;离线导入仅在
|
||||
所需资源已具备时可完成使用,否则列出需要补充的资源,不显示为可运行。导入应用不会自动
|
||||
@@ -245,19 +249,18 @@ YAML 描述文件不能代替模型权重或插件程序包。有外部依赖时
|
||||
|
||||
### FR-16 本机模型管理入口(新增计划项)
|
||||
|
||||
应用中心提供“本机模型”内置管理应用,可选择常驻左侧,打开主内容区管理页。该入口统一
|
||||
查看各功能使用的本机模型及服务状态,包括已接入的 OCR、向量、语音或推理引擎;具体支持
|
||||
清单需核对现有模型服务后确定,不因提供入口而假定全部引擎均已支持进程控制。
|
||||
应用中心提供“本机推理监控”内置管理应用,可选择常驻左侧。从常驻入口、应用菜单或管理 Modal 的“打开”均进入同一独立管理 Modal;来源菜单或管理 Modal 关闭,底层工作区、当前项目、会话及草稿保持不变。该入口统一
|
||||
查看本机语音识别、OCR、向量生成等服务的运行状态与可用资源指标。
|
||||
|
||||
页面展示模型名称、用途、所属引擎、管理方式、运行状态、占用情况及使用它的应用或任务。
|
||||
GoodBuddy 管理的进程提供启动、停止、重启和错误查看;外部启动的模型服务标明“外部管理”,
|
||||
提供连接状态与配置入口,未接入其管理接口时不提供停止或重启。进程内运行的模型按引擎实际
|
||||
能力提供加载与释放,不伪造独立进程。
|
||||
Modal 只有服务列表,显示名称、简短状态、引擎、模型、资源和错误,不显示任务历史、任务页签、单任务取消或外部连接列表。独立向量进程显示 CPU 与工作集内存,并标明进程范围;CPU 预热、采样失败和共享进程 worker 无法独立统计时说明原因,不能用零或整个应用进程占用代替服务指标。活动请求可能仍在排队或加载,不能据此宣称服务正在运行。ASR 当前仅显示按需运行且状态未知;OCR 和向量服务使用各自执行模块的状态。
|
||||
|
||||
停止或重启前展示正在使用该服务的任务和影响,由用户决定是否继续;取消时保留运行状态,
|
||||
失败时显示实际状态和原因。多个应用共用同一模型服务时展示同一个服务及其使用方,不重复
|
||||
启动进程。隐藏管理入口、关闭管理页面或取消应用常驻均不停止模型;卸载模型文件是独立操作。
|
||||
服务行保留实际支持的启动、停止、重启或模型加载、释放,工具栏保留刷新和模型设置入口。停止后重新启动不自动重放已中断任务。
|
||||
|
||||
资源监控仍是只读系统工具,本机模型管理负责模型生命周期,两者共享可用的状态来源。
|
||||
该项可独立于商店交付;当前只纳入计划,具体分期、任务使用期间的停止策略和各引擎管理能力
|
||||
在详细设计中确定。原首期资源监控的 GoodBuddy 桌面进程统计范围不因此扩大。
|
||||
停止、重启或释放前,在管理 Modal 上方打开紧凑影响确认,展示正在使用该服务的任务和影响;没有已登记活动任务时也说明执行前会再次核对。取消时保留运行状态,
|
||||
失败时关闭旧确认、显示原因并刷新状态,再次操作须重新确认当前影响。多个应用共用同一模型服务时只展示同一个服务,不重复
|
||||
启动进程。隐藏管理入口、关闭管理 Modal 或取消应用常驻均不停止模型;卸载模型文件是独立操作。
|
||||
确认打开时不能关闭底层管理 Modal;Escape 只关闭最上层,提交期间禁止关闭及重复执行。关闭后恢复可用入口焦点,具体规则见 [UI 设计](./ui-design.md#91-本机推理管理)。
|
||||
|
||||
资源监控仍是只读系统工具,本机推理监控聚焦本地服务状态。该项独立于商店接入生产源码,支持 OCR 空闲加载与释放、内置向量进程启动/停止/重启;TTS 显示不可用。后端保留请求登记及原有取消能力,供执行路径和服务影响校验使用。各引擎边界见
|
||||
[技术清单](./technical-design.md#111-本机推理任务与执行服务),验证范围见[实施进度](./progress.md)。
|
||||
原首期资源监控的 GoodBuddy 桌面进程统计范围不因此扩大。
|
||||
|
||||
@@ -1,4 +1,315 @@
|
||||
# 项目活动级联实施进度
|
||||
# 应用导航实施进度
|
||||
|
||||
2026-09-23 文档校正:当前 `heartbeat` 已作为可选应用管理,默认值与执行边界见[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行),设置与导航契约见[技术设计](./technical-design.md#4-应用导航偏好)。下方日期记录保留当时实现与验证结果;其中“心跳始终显示”“无应用级开关”和旧字段删除说明不再代表当前行为。本次仅核对源码并更新文档,未重新执行功能测试。
|
||||
|
||||
## 2026-09-18:窄幅单列应用列表
|
||||
|
||||
对应 FR-11、US-B6、US-B7、US-D1。管理概览改为紧凑单列横向列表,概览与详情共用 `640px` 最大宽度;左侧显示图标、名称、状态与可换行用途,右侧放置打开、可用的设置及成组排序箭头。行间使用弱分隔线,窄窗口操作换行。保留四应用保存顺序、箭头和拖动规则、知识库与智能心跳始终显示、本机推理监控默认不常驻。现行尺寸与交互见 [UI 设计](./ui-design.md#4-应用中心与共同设置),下方历史记录保留当时布局。
|
||||
|
||||
验证:ApplicationCenter、ApplicationMenu、WorkspacePrimitives 共 54 项测试通过;`npm run typecheck`、`npm run lint`、`git diff --check` 通过。全量 `npm test` 超过 60 秒未取得汇总,不记为通过。
|
||||
|
||||
Windows Electron fixture 重新编译当前组件、生产 CSS 与中文翻译,使用真实 ApplicationSettingsStore 及临时 IPC;浅深主题分别检查 1280、960、720、640、375px 目标窗口,实际 CSS 视口因缩放约多 1px。计算布局均为单列且无横向溢出,桌面 Modal 宽度为 640px,四行共八个箭头且均可拖动。原生键盘排序、磁盘顺序读取、详情返回及关闭后的焦点恢复通过。证据为临时目录 `application-rows-Hl3X55/result.json`。截图捕获持续报 `UnknownVizError`,禁用硬件加速后仍失败,因此未完成截图审阅;此 fixture 不作为完整 App、安装包或原生指针拖放验收。
|
||||
|
||||
中文改动按 deai-writing 自查清单复核。未修改模型、OCR、任务中心或 Agent 执行逻辑,未调用模型,未创建提交。
|
||||
|
||||
## 2026-09-18:收拢通用应用设置入口
|
||||
|
||||
对应 FR-11、US-B9。移除魔法笔记页面及本机推理监控停用状态的通用“应用设置”按钮,启用、常驻和笔记评论配置统一从应用中心卡片进入。保留管理应用、设置详情返回导航、模型与连接设置及原业务控件。删除仅用于页面快捷入口的 Context、跳转回调和初始详情参数;停用时仍保留页面挂载状态。现行入口规则见 [UI 设计](./ui-design.md#41-共同应用设置页),Demo 已移除页面、停用恢复及全局设置中的重复快捷入口。
|
||||
|
||||
验证:App 入口与状态同步定向测试 14 项通过;ApplicationCenter、ApplicationMenu、LocalInferencePage、SettingsPanel 共 128 项通过。首次包含完整 App 文件的运行出现两项既有任务审批失败,以及新增笔记测试未启用 fixture 的失败;修正 fixture 后相关定向测试通过。两项审批失败与上次记录一致,本轮未修改。类型检查、全仓 lint、Demo 语法及 diff 检查通过。本轮未运行全量测试或 Electron 窗口验收,未调用模型,未创建提交;保留已有四应用排序、监控名称和默认不常驻改动。
|
||||
|
||||
## 2026-09-18:四应用共享顺序
|
||||
|
||||
对应 FR-3、FR-11、FR-12、US-B7。四张管理卡片均提供上下移动和拖动,包含关闭、未常驻以及始终显示的应用;卡片、菜单、侧栏共用 `applicationNavigation.order`。知识库和智能心跳保留无开关的始终启用、常驻行为,位置可调整,标签改为“始终显示”/`Always shown`。当前对话、运行记录、设置仍保持系统位置。搜索中箭头按完整顺序移动,拖动禁用。保留此前 `260px` 自适应网格、本机推理监控名称及默认不常驻。
|
||||
|
||||
权威设置返回及写入 schema 接受四个 ID 各一次,拒绝重复、未知和缺失项。仅存储读取补齐旧可选应用顺序及部分默认值,保留已有顺序、显式布尔值和无关设置;规则见[持久化与 IPC](./technical-design.md#43-持久化与-ipc)。没有新增存储版本、应用启用字段或第二份导航状态。
|
||||
|
||||
验证:`npm test -- src/main/application-settings-store.test.ts src/shared/goodbuddy-config-contracts.test.ts src/main/goodbuddy-config-service.test.ts src/renderer/src/ApplicationCenter.test.tsx src/renderer/src/ApplicationMenu.test.tsx src/renderer/src/LocalInferencePage.test.tsx src/renderer/src/WorkspacePrimitives.test.tsx` 共 127 项通过;App 排序保存、菜单与侧栏同步、外部事件、重开及入口行为的 9 项定向测试通过。包含完整 `App.test.tsx` 的八文件运行共 390 项通过、2 项失败,失败项是任务中心审批测试在折叠列表中找不到任务行(`App.test.tsx` 的 scheduled approvals 场景),本次没有修改该行为。`npm run typecheck`、`npm run lint` 和 `git diff --check` 通过。全量 `npm test` 运行约七分钟仍未得到汇总,已停止本次测试进程;日志复现上述两项失败,不能宣称全量通过。
|
||||
|
||||
Windows Electron fixture 使用当前 ApplicationCenter、生产 CSS/中文翻译、真实 ApplicationSettingsStore 和临时 IPC,从旧两项顺序读取后,移动知识库以及关闭且未常驻的本机推理监控,并用新 Store 实例确认磁盘结果。浅深主题各检查 1280、960、640、375px 目标宽度,实际 CSS 视口因缩放多约 1px;均显示八个排序箭头、四个可拖动卡片,无横向溢出,列数分别为三、三、二、一。详情不含排序,返回搜索焦点和重开顺序通过。最终证据目录为临时 `all-app-order-t0hDya`;已查看桌面浅色及窄屏深色截图。此项是组件与存储验证,未验收完整生产 Main/Preload 启动或安装包,原生指针拖放由组件事件测试之外另行验收。
|
||||
|
||||
中文文档运行 deai-writing 扫描,无阻断项;对本次改动的复核候选人工检查,保留明确的产品状态与验收限制。未调用模型,未修改模型管理、GPU 指标或 Agent 执行路径,未创建提交。下方记录保留各次实施时的行为,旧“固定位置”“仅常驻项排序”规则已由本条及现行设计替代。
|
||||
|
||||
## 2026-09-18:卡片排序与自适应列数
|
||||
|
||||
对应 FR-11、US-B7、US-D1、US-D2。上下移动从设置详情移到常驻可选应用卡片,箭头名称及提示包含应用名;首尾、唯一常驻项和保存锁定时禁用对应操作。拖动也仅允许常驻可选应用,固定应用及未常驻项不参与排序。卡片改用 `auto-fit/minmax`,最小宽度 `260px`,Modal 最大宽度仍为 `960px`;启用、常驻及笔记设置继续使用原详情表单。保留本机推理监控名称及默认不常驻改动。
|
||||
|
||||
Windows Electron 组件 fixture 使用当前 ApplicationCenter、生产 CSS/中文 i18n、真实 ApplicationSettingsStore 及临时 IPC。浅深主题各检查 1280、960、640、375px 目标窗口宽度,实际 CSS 视口受缩放约多 1px;分别为三、三、二、一列,均无横向溢出。检查排序持久化、详情无排序及返回搜索焦点,并人工查看桌面浅色、窄屏深色截图。证据位于临时目录 `cards-order-fixture-tucKjv`。完整 App 配合已有 Main/Preload 的两次启动尝试未产生窗口,此次 fixture 不作为完整 App 或安装包验收。
|
||||
|
||||
`npm run typecheck`、`npm run lint`、`git diff --check` 通过;`npm test -- src/renderer/src/ApplicationCenter.test.tsx src/renderer/src/ApplicationMenu.test.tsx src/renderer/src/LocalInferencePage.test.tsx src/main/application-settings-store.test.ts` 共 66 项通过,覆盖固定/未常驻项、边界、保存锁定、搜索排序及设置入口。全量 `npm test` 在 60 秒后超时,未取得完整结果。中文改动按 deai-writing 自查清单人工复核。未调用模型、修改模型管理或 GPU 指标,未创建提交。
|
||||
|
||||
## 2026-09-18:本机推理监控默认不常驻
|
||||
|
||||
对应 FR-11、US-B 的默认值与迁移场景。共享导航默认值改为本机推理监控不常驻;新安装和旧设置缺失整个导航配置时采用此值,应用仍默认启用,可从应用菜单及管理 Modal 打开。默认可选顺序及知识库、智能心跳的固定入口规则保持不变。沿用存储版本 12,已有常驻 `true`/`false` 均保留;旧默认值与主动固定没有来源标记,无法区分,不强制取消已有常驻。Demo 初始状态和恢复默认同步。
|
||||
|
||||
验证:设置存储、应用中心、菜单、推理 Modal 共 62 项测试通过,App 入口及设置同步 8 项定向测试通过;`npm run typecheck`、`npm run lint`、Demo 的 `node --check` 和 `git diff --check` 通过。全量 `npm test` 在 60 秒后超时,未取得完整结果。中文改动按 deai-writing 自查清单人工复核。本次未启动 Electron 或调用模型,不涉及 GoodBuddy Agent 或远程执行路径;未创建提交,保留此前未提交的名称调整。
|
||||
|
||||
## 2026-09-18:统一名称为本机推理监控
|
||||
|
||||
对应 FR-16、US-F3。导航、应用中心、应用设置、Modal 标题及关闭按钮统一使用“本机推理监控”,英文应用名为 Local Inference Monitor。现行功能文档与 Demo 名称同步;`local-inference`、API、组件名及服务行为保持不变,下方历史记录保留当时称谓。
|
||||
|
||||
验证:应用菜单、应用中心、Modal 的 27 项测试及 App 入口、设置同步相关的 8 项测试通过;`npm run typecheck`、修改源码 ESLint、Demo 的 `node --check` 和 `git diff --check` 通过。Demo 被 ESLint 既有规则排除。中文改动按 deai-writing 自查清单人工复核,仅同步名称及本条验证事实。本次未运行全量测试或启动 Electron 验收窗口排版。
|
||||
|
||||
## 2026-09-18:独立推理进程资源采样
|
||||
|
||||
对应 FR-16、US-F3。向量 utility process 已按 PID 接入 Electron 真实 CPU 和工作集内存,服务行显示进程范围、PID、百分比及 MiB。ASR 为主进程线程,OCR 为渲染进程 Web Worker,分别显示不能独立统计的原因。采样口径及基线规则见[技术设计](./technical-design.md)。服务 Modal 继续保持单一列表。
|
||||
|
||||
本轮验证:资源采样器、Main 服务、向量 Broker、Modal 共 39 项定向测试通过;IPC 与 OCR Bridge 另有 130 项通过,共 169 项。类型检查、修改范围 ESLint、`git diff --check` 通过。全仓 lint 仅报告未修改的 story-graph Demo 的 22 个 `document` 错误;`npm test` 超过 60 秒未完成,不记为通过。中文功能文档 deai-writing 扫描阻断项为 0,复核项中的采样限制和验证边界按事实保留。
|
||||
|
||||
真实 Windows Electron 验证直接调用生产 `createEmbeddingUtilityTransport` 和采样器,使用临时 CPU 工作负载:PID 5892 的工作集从 57,769,984 字节变为 63,090,688 字节,区间 CPU 为 98.71%;退出后返回不可用,重启得到 PID 20532 并重新预热。进程刚 spawn 时指标可能尚未登记,返回等待数据。此验证没有加载推理模型,没有调用模型 API,也没有重新验收完整 App 窗口;macOS、Linux 尚未实机验证。改动仅涉及桌面本机服务监视,不影响 GoodBuddy Agent 或远程执行。
|
||||
|
||||
## 2026-09-18:本机推理服务监视简化
|
||||
|
||||
对应 FR-16、US-F3、US-F5;US-F4 的 Modal 单任务取消入口退役。`LocalInferencePage` 直接显示服务列表,移除任务页签、历史、外部连接和长说明,保留刷新、模型设置及受支持的服务操作。现行定义见 [UI 设计](./ui-design.md#91-本机推理管理)。
|
||||
|
||||
当时没有服务级 CPU/内存采样,统一显示“暂不可用”;这一限制已由上方资源采样实现修正。ASR 模型可用时返回 `unknown`,登记活动请求不再将状态覆盖为 `running`。OCR/向量仍使用各自执行模块的状态。操作失败关闭旧确认并刷新,重试时重新确认当前影响;后端任务清单校验和 OCR 忙碌保护保留。
|
||||
|
||||
验证结果:
|
||||
|
||||
- 定向运行 LocalInferencePage、Main 服务、向量 Broker、OCR Bridge 和 IPC,5 个文件共 165 项通过;App 的本机推理入口和停用恢复场景 4 项通过。
|
||||
- `npm run typecheck` 和修改范围 ESLint 通过。`npm run lint` 仍报告无关 `docs/features/story-graph/demo.js` 的 22 个 `document` 错误。
|
||||
- `npm test` 首次超过 120 秒,再次超过 600 秒,均未完成。第二次报告的 App 失败来自旧页签断言,已改为服务列表并定向复验通过;不将全量测试记为通过。
|
||||
- 中文功能文档按 deai-writing 扫描,阻断项为 0;复核项中的状态限制和操作清单按产品事实保留。
|
||||
|
||||
本轮未启动真实 Electron 或调用模型,未重新验证原生窗口排版。改动仅影响桌面本机服务监视,不涉及 GoodBuddy Agent 或远程执行路径。未创建提交,下方历史记录保留当时的 UI 与验证边界。
|
||||
|
||||
## 2026-09-17:应用卡片与紧凑详情
|
||||
|
||||
对应 US-B6 至 US-B9、US-F3 至 US-F5。已核对 `ApplicationCenter`、`LocalInferencePage`
|
||||
及其样式:管理改为桌面双列、窄窗口单列卡片,固定应用仅有打开;可选应用提供打开和设置,
|
||||
关闭时禁用打开。启用、常驻及上下移动位于最大 `640px` 的详情,卡片拖动排序保留。
|
||||
详情使用紧凑返回标题栏,进入及返回时滚动归顶。本机推理使用内容高度 Modal,列表状态与操作
|
||||
分区并响应式排列。现行规则见 [UI 设计](./ui-design.md#4-应用中心与共同设置)。
|
||||
|
||||
本轮实现验证结果由实现方提供,本次仅更新文档,未重跑产品测试:
|
||||
|
||||
- 59 项组件测试及 8 项选定 App 测试通过;`npm run typecheck`、修改范围 ESLint 和
|
||||
`git diff --check` 通过。
|
||||
- 真实 Electron 使用当前 Renderer、已有 Main/Preload、真实 IPC 和隔离配置,四种尺寸、浅深
|
||||
主题共 32 个视图无横向溢出;导航、焦点、Escape、背景隔离及详情滚动重置已检查。
|
||||
- 实机任务列表为空态;有任务及长名称由组件测试覆盖。模型调用为 0,未重跑全量测试,
|
||||
未创建提交;此前全量测试与全仓 lint 的失败记录仍保留,不将整体验收标为完成。
|
||||
|
||||
实机报告已核对:`C:/Users/jiang/AppData/Local/Temp/opencode/application-cards-review-cUiVAc/result.json`,
|
||||
截图位于同目录。下方记录保留各轮当时的布局及验证范围,旧单列管理行和旧 Modal 尺寸不作为现行定义。
|
||||
|
||||
## 2026-09-17:本机推理改为独立 Modal
|
||||
|
||||
对应 FR-3、FR-5、FR-16、US-B1、US-B6、US-F3 至 US-F5。已核对当前 `App.tsx`、
|
||||
`LocalInferencePage.tsx`、`local-inference.css` 和 `dialog-focus.ts`:常驻入口、应用菜单及管理
|
||||
Modal 的“打开”均打开独立本机推理 Modal,保留底层工作区;其他内置应用仍打开主内容区页面。
|
||||
停止、重启和释放使用嵌套紧凑影响确认,复用背景隔离、焦点循环与恢复,按当前层处理 Escape,
|
||||
操作提交期间禁止关闭及重复执行。具体行为见 [UI 设计](./ui-design.md#91-本机推理管理)。
|
||||
|
||||
以下结果由实现代理提供,本次文档更新未重新运行代码测试:
|
||||
|
||||
- 180 项定向测试通过。
|
||||
- `npm run typecheck` 与修改范围的 ESLint 通过。
|
||||
- 完整 App 测试运行超时,未取得完整通过结果。
|
||||
- 全仓 lint 仍有 `docs/features/story-graph/demo.js` 的既有 22 个 `document` 全局声明错误。
|
||||
|
||||
随后完成真实 Electron 验证:当前 Renderer 源码配合当日已有 Main/Preload 构建,使用隔离配置,
|
||||
未模拟 IPC。三个入口均保留同一知识库工作区节点;背景隔离、Escape、焦点恢复及窄窗口侧栏
|
||||
开关回退通过。使用已有本机资产完成 OCR 模型加载与确认释放,推理请求和模型下载均为零。
|
||||
浅深主题及窄窗口未出现横向溢出,释放确认框约为 480 × 182 CSS 像素,内边距 16px、按钮高 36px。
|
||||
修正窄窗口关闭按钮换行后再次通过实机检查和 43 项相关测试。证据位于临时目录
|
||||
`inference-modal-ui-8KJFgQ/result.json` 及同目录截图;未验证重新打包后的应用。
|
||||
|
||||
`npm test` 在 240 秒边界停止,未取得全量通过结果;独立复跑心跳数据库测试为 6 项通过、
|
||||
2 项失败,均为已有的 `duplicate column name: pinned`。下方旧页面截图不作为 Modal 验收证据。
|
||||
本轮已同步源码与文档,未修改 Demo,未创建提交。
|
||||
|
||||
下方保留各轮当时的实现与验证记录,旧主内容区页面及无 props 描述不作为当前 Modal 的行为定义。
|
||||
|
||||
## 2026-09-17:生产界面密度与响应式复核
|
||||
|
||||
对应 US-B5 至 US-B9、US-F3。已审阅 `abcd-production-08rUQA` 中 A、B、D 的原生产截图。
|
||||
应用管理从全宽纵向块改为最大 `960px` 的内容高度 Modal:名称旁标明固定/可选状态,桌面操作
|
||||
同行,窄屏自然换行且排序箭头成组。共同设置统一标签、说明和开关对齐;本机推理改为弱分隔列表。
|
||||
菜单只调整共享字体和图标层级。展示规则见 [UI 设计](./ui-design.md#4-应用中心与共同设置)。
|
||||
|
||||
最终源码验证:
|
||||
|
||||
- 应用中心、应用菜单、本机推理、设置面板和共享 UI 的 5 个定向文件共 148 项通过。
|
||||
- `npm run typecheck`、改动 Renderer 文件的定向 ESLint、`git diff --check` 通过。
|
||||
- `npm run build:bundle` 成功;最后一处布局修正后,`npx electron-vite build --logLevel error` 再次成功。
|
||||
- 全仓 `npm run lint` 仍有 `docs/features/story-graph/demo.js` 的 22 个 `document` 全局声明错误,未改动该文件。
|
||||
- 完整 `npm test` 用时 `648.80s`:4,454 项通过、67 项跳过、2 项失败。失败仍为心跳数据库
|
||||
迁移的 `duplicate column name: pinned`,位于未改动的 `assistant-database.ts:9988`;全量测试
|
||||
未通过。完整日志位于临时目录 `abcd-polish-tests-VxXZ34/test.log`。
|
||||
|
||||
真实 Electron 使用仓库 package 入口、当前生产 Main/Preload/App 和独立用户目录,未替换 IPC。
|
||||
最终截图与测量位于临时目录 `abcd-production-m9I4Vy`:`1200×775` 下四行约 `75px` 高,管理
|
||||
内容区 `scrollHeight` 与 `clientHeight` 均为 `420px`,操作同行;`520×740`、`375×667` 下
|
||||
管理、共同设置和推理内容无横向溢出。已审阅桌面与窄屏浅深色截图、375px 管理和设置截图。
|
||||
管理行按钮为 `34px`/`36px`,Switch 标签点击区高 `36px`,评论选项保留共享紧凑分段控件。
|
||||
生产验证覆盖菜单 End/Escape、Modal 焦点保持、页签方向键,以及 Space 切换常驻后重载 Renderer
|
||||
确认保存。运行报告为同目录 `result.json`,截图为 `A-menu*.png`、`B-manage*.png`、
|
||||
`C-settings*.png`、`D-inference*.png` 和 `D-tasks.png`。
|
||||
|
||||
此次为界面调整,未改后端生命周期;隔离目录没有安装模型,模型请求为 0,仅检查真实服务快照与任务空态。
|
||||
日志仍有既有字体 CSP 拦截,未捕获 JavaScript 异常。未验证模型启停、安装包或整个项目的功能验收。
|
||||
工作区原有 Runtime 与 story-graph 修改保留,未创建提交。
|
||||
|
||||
## 2026-09-17:轻量应用菜单与两个固定应用
|
||||
|
||||
对应 FR-3、FR-5、FR-11、FR-12、US-B5 至 US-B8、US-D1/US-D2。当前生产源码的底部应用中心向上展开轻量锚定菜单,无模态遮罩,只列已启用应用,不按打开历史或常驻状态筛选。点击应用行关闭菜单并打开主内容区;底部“管理应用”打开既有管理 Modal。
|
||||
|
||||
[ApplicationMenu](../../../src/renderer/src/ApplicationMenu.tsx) 与 [AnchoredMenu](../../../src/renderer/src/AnchoredMenu.tsx) 承担菜单及键盘、定位;[App](../../../src/renderer/src/App.tsx) 固定依次显示知识库、智能心跳,再显示已启用且常驻的可选应用。[管理 Modal](../../../src/renderer/src/ApplicationCenter.tsx) 中两个固定应用仅提供“打开”,不提供启用、常驻、排序或通用空设置;原工作区配置及心跳单条计划 enabled 不变。
|
||||
|
||||
[设置契约](../../../src/shared/application-settings-contracts.ts) 的可选偏好仅包含 `magic-notes`、`local-inference`。未发布草案中的 `knowledgeEnabled`、`heartbeatEnabled` 已删除,不增加迁移或兼容读取。[Store](../../../src/main/application-settings-store.ts) 持久化后发布权威快照的链路保留,包含配置工具写入;重开刷新期间锁定修改,较新快照使迟到的旧读取和保存响应失效。
|
||||
|
||||
本节收录本轮提供的最新验证结果;此次文档编辑未重新运行产品测试或模型请求:
|
||||
|
||||
- `npm run typecheck` 通过,`npx eslint src` 通过。
|
||||
- 完整 `npm test` 已结束:4,448 项通过、67 项跳过、2 项失败。两项仍为心跳数据库迁移的 `duplicate column name: pinned`,相关迁移未改动;全量测试未通过。
|
||||
- 全仓 `npm run lint` 仍有 `docs/features/story-graph/demo.js` 的 22 个 `document` 全局声明错误,属于无关文档 Demo;全仓 lint 未通过。
|
||||
- Electron fixture 共 8 个主题/尺寸场景通过,覆盖原生键盘、焦点、菜单几何、原生视图遮挡及实际 `ApplicationSettingsStore` 持久化。此证据验证组件 fixture 和真实设置存储,不是完整生产 App 的端到端验收,也不是安装包验收。
|
||||
|
||||
完整生产 App 补充验证:`npm run build:bundle` 成功后,以隔离用户目录启动仓库 package 入口,
|
||||
使用实际 `out/main/index.js`、生产 Preload 和 App,未替换 IPC handlers。已验证上拉菜单、
|
||||
“管理应用”Modal、两个固定应用仅有“打开”、笔记常驻切换,以及刷新 Renderer 和磁盘配置的
|
||||
持久化结果;本机推理页通过真实注册 IPC 取得 ASR、OCR、向量和 TTS 状态。已审阅菜单与管理截图。
|
||||
证据位于临时目录 `abcd-production-08rUQA` 的 `result.json`、`A-menu.png`、`B-manage.png`、
|
||||
`D-inference.png` 和 `runtime.log`。生产启动日志有字体被 CSP 阻止的消息,未捕获 JavaScript 异常。
|
||||
此轮未重启整个应用验证持久化;隔离目录没有安装模型,未执行模型启停或模型请求。
|
||||
|
||||
A–D 已接入源码并通过上述生产 App 冒烟检查;不将全量测试、安装包及整个计划标为通过。
|
||||
D 的真实 ASR、OCR、向量引擎证据及最小 Main 接线限制保留在下方原记录中,本次状态读取不扩大
|
||||
其模型操作验证范围。本机资源监控仍待实现。Demo 的直接 Modal 和知识库/心跳可选设置已过时,本轮未编辑 Demo 文件。
|
||||
|
||||
下方为历史记录,保留当时实现、测试结果及限制,旧应用级开关和旧入口描述不作为现行行为定义。
|
||||
|
||||
## 2026-09-16:知识库固定入口与设置同步更正
|
||||
|
||||
本节更正下方早期记录的现行行为,历史实现和验证记录原文保留,不作为最新修订已重新通过验证的证据。
|
||||
A–D 的应用中心 Modal、可选应用共同设置、常驻排序及本机推理均已接入生产源码,整体验收边界仍见各次记录。
|
||||
|
||||
本次文档核对的源码证据:
|
||||
|
||||
- [共享设置契约](../../../src/shared/application-settings-contracts.ts) 将可编辑 ID 限为魔法笔记、智能心跳、本机推理;知识库不参与启用、常驻或排序。版本 12 为未发布分支格式,`knowledgeEnabled` 已移除,无兼容旧草案要求。
|
||||
- [应用中心](../../../src/renderer/src/ApplicationCenter.tsx) 的知识库行只有“打开”;[App](../../../src/renderer/src/App.tsx) 在首个可选槽位后插入固定知识库,再过滤可选应用。知识库始终显示,原工作区实际配置保留;旧的禁用知识库请求守卫说明已从现行设计中删除。
|
||||
- [设置 Store](../../../src/main/application-settings-store.ts) 持久化成功后发布 `onChanged`;[IPC](../../../src/main/ipc.ts) 转发完整快照,覆盖[配置工具服务](../../../src/main/goodbuddy-config-service.ts) 的写入。App 重开中心时刷新并锁定修改,较新的事件使在途旧读取和保存响应失效。
|
||||
- [向量 Broker](../../../src/main/knowledge/embedding-inference-broker.ts) 区分调用方返回与 Worker 执行结束;取消或超时后,执行记录在 Worker 确认或退出前仍占用在途名额并计入活动任务和服务停止影响。
|
||||
|
||||
本次仅更新文档并核对源码、链接和文本一致性,未运行产品测试或实机验证,也未发起模型请求。
|
||||
README 已标明 Demo 中知识库启停、常驻及通用设置描述待独立同步;本次未修改 Demo 文件。
|
||||
|
||||
## 2026-09-16:D 本机推理生产接入
|
||||
|
||||
对应 FR-16、US-F3 至 US-F5。`LocalInferencePage` 默认导出、无 props,已与中心代理的
|
||||
`local-inference` 页面入口对接,未修改 App 生产入口或应用启用契约。真实支持范围见
|
||||
[技术清单](./technical-design.md#111-本机推理任务与执行服务)。ASR、OCR、向量生产请求均登记
|
||||
生命周期,服务操作和任务取消独立。内置向量 Broker 按模型目录共享;OCR 直接控制持有模型的
|
||||
Renderer Worker。没有新增任务持久化、虚构进度、资源值或 PID。
|
||||
|
||||
验证证据:
|
||||
|
||||
- 原始 Vitest 配置运行 App、IPC、应用中心、设置存储、推理服务、ASR、OCR、向量 Broker、
|
||||
Worker 和引擎,共 12 个文件、452 项通过。完整 App 测试使用真实页面模块,没有 resolve 替身。
|
||||
- 收尾新增 OCR 加载失败恢复用例后,11 个定向文件 202 项通过;加上已通过的完整 App 251 项,
|
||||
当前覆盖合计 453 项。最终 `npm run typecheck`、D 源码及测试定向 ESLint、`git diff --check`
|
||||
均通过;diff 检查仅有工作区已有换行符提示。验证生成的临时 bundle 已清理。
|
||||
- Windows Electron 隔离用户目录,加载当前页面与生产 CSS、生产 Preload、实际引擎模块,
|
||||
通过临时最小 Main IPC 接线运行已安装的 SenseVoice、PaddleOCR、Granite 模型,未修改模型配置。
|
||||
最终一轮:3 次向量请求(含完成/取消竞态)、1 次 ASR 静音采样请求、1 次 OCR 图片请求。
|
||||
向量返回 384 维;UI 停止、启动后再次推理成功;隐藏窗口保持服务运行;OCR 识别 `HELLO`,
|
||||
UI 加载与释放成功;1080px 和 520px 窗口无横向溢出。未调用外部付费模型。
|
||||
- 实机取消竞态中执行方自然完成,管理页正确保留完成结果;取消确认和共享进程不被终止的分支
|
||||
另由 Broker 回归测试验证。停止失败、启动失败重试、新任务影响清单变化由定向测试覆盖。
|
||||
- 真实向量验证发现浏览器 ONNX `/wasm` 入口无法 fetch 本地资源;Main 改为 Node 条件入口后
|
||||
实机推理通过,具体执行错误可回传任务列表。
|
||||
|
||||
限制与阻塞:本轮不是打包安装包验收,也未驱动完整生产 App 的所有入口与原生浏览器视图。
|
||||
实机测试 Main 使用最小接线,完整生产 IPC 注册和 App 接入由上述原始测试及类型检查覆盖。
|
||||
全量 `npm test` 在 200 秒预算内未结束;已复现心跳迁移两项 `duplicate column name: pinned`,
|
||||
独立重跑仍失败,和应用设置 v12 期望值无关,未修改该数据库迁移。全仓 lint 仍被并行工作目录
|
||||
`docs/features/story-graph/demo.js` 的 22 个 `document` 全局声明错误阻塞。不将全量测试或 lint
|
||||
标为通过。D 不修改 GoodBuddy Agent 或远程执行链路,无远程主机验证要求;没有创建提交。
|
||||
|
||||
本节取代下方早期记录中“缺少 LocalInferencePage”的当前阻塞描述,早期验证范围仍保留供追溯。
|
||||
|
||||
## 2026-09-16:应用中心、共同设置与知识联动
|
||||
|
||||
对应 US-A2、US-B5 至 US-B9。当前源码接入左下角应用中心和独立设置按钮;中心使用设置 Modal
|
||||
壳层,包含搜索、打开、常驻、拖动及上下移动。四个稳定 ID 为 `magic-notes`、`knowledge`、
|
||||
`heartbeat`、`local-inference`。应用启用、常驻和顺序独立持久化,固定入口及右侧工具不参与排序。
|
||||
|
||||
`ApplicationSettingsStore` 升为版本 12,保留已有 `false` 和评论单选值,为缺失项补默认值。
|
||||
笔记页与中心使用 `ApplicationSettingsView`;全局笔记入口已移除。保存期间共用锁,失败后读取
|
||||
确认值,无法确认时锁定修改并允许重新读取。关闭当前应用保留页面挂载及草稿,显示恢复入口。
|
||||
|
||||
知识关闭后移除会话知识控件与摘要,保留各会话关联;Main 交互请求入口在授予知识能力及
|
||||
检索前裁剪新请求范围,不取消已开始的请求。队列派发重新经过同一入口,本机和托管 SSH
|
||||
分支消费同一个请求对象。未修改 Skills、MCP、专家或本机推理页面与后端实现。
|
||||
|
||||
验证记录:
|
||||
|
||||
- 合并定向运行 App、IPC、配置与 Renderer 共 10 个 suite,599 项全部通过;随后新增本机推理
|
||||
主内容区入口用例,完整 App suite 251 项通过,合计覆盖 600 项;
|
||||
App 仍使用下述临时页面契约替身,其余生产代码未替换。
|
||||
- `ApplicationCenter.test.tsx`、设置、笔记、更新设置、共享界面与浏览器遮挡及配置契约的
|
||||
定向回归共 227 项通过;随后增加的保存失败、缺失笔记配置回归与配置契约共 39 项通过。
|
||||
- `ipc.test.ts` 和中心回归共 127 项通过,包含新请求禁用、在飞请求继续及重新开启的范围恢复。
|
||||
- `App.test.tsx` 共 251 项通过;因独立负责的 `LocalInferencePage.tsx` 尚未到位,使用临时
|
||||
Vitest resolve 插件提供无 props 空组件,仅用于验证 App 壳层,未替代或写入生产页面。
|
||||
- Windows Electron 使用当前中心组件、生产 CSS、真实 `ApplicationSettingsStore` 与临时
|
||||
最小 IPC 桥接验证常驻、排序、启用及重新读取持久化结果。浅深主题各验证 `1200 × 850` 和
|
||||
`680 × 560` 窗口,无水平溢出;搜索初始焦点、Escape、关闭焦点恢复及背景隔离通过。
|
||||
已审阅窄浅色和宽深色截图;证据位于本轮临时目录的 `application-center-*.png`。
|
||||
- 全量 `npm test` 的 JSON 报告为 4,153 项通过、4 项失败、67 项跳过,另有 suite 加载失败。
|
||||
其中应用配置契约旧快照已修正并定向通过;其余失败包括心跳旧库迁移两项、工作栏终端标题一项。
|
||||
终端标题用例随后单独复跑通过,未修改其实现。
|
||||
App suite 因缺少 `LocalInferencePage` 无法在原始配置下加载,不能将全量检查记为通过。
|
||||
- `npm run typecheck` 当前仅报告缺少 `./LocalInferencePage`;`npm run lint` 当前仅报告
|
||||
并行工作目录 `docs/features/story-graph/demo.js` 的 22 个 `document` 全局声明错误。
|
||||
本轮修改的生产源文件定向 Lint 通过。
|
||||
|
||||
剩余集成验证:接入另一实现负责的默认导出 `LocalInferencePage` 后重跑原始 App 测试与类型
|
||||
检查;完整应用的原生浏览器遮挡和共享 Linux Host 真实请求尚未实测。本轮真实模型调用为 0。
|
||||
本节不宣称本机推理后端、管理页或整个计划已交付,也没有生成提交。
|
||||
|
||||
## 2026-09-16:设置改为大 Modal
|
||||
|
||||
对应 [US-A2](./user-stories.md#us-a2-从固定入口进入设置)。设置入口保留底层工作区,关闭后卸载
|
||||
设置内容;布局、关闭与分类直达规则见 [设置中心 UI 规范](../../../UI-DESIGN.md#138-设置中心)。
|
||||
|
||||
`App.test.tsx`、`SettingsPanel.test.tsx`、`WorkspacePrimitives.test.tsx` 和
|
||||
`browser-viewport-occlusion.test.ts` 定向回归共 389 项通过。`npm run lint` 通过。
|
||||
全量 `npm test` 完成时为 4,354 项通过、9 项失败、67 项跳过;失败涉及本地 Runtime 复用超时、
|
||||
心跳旧库迁移、远程恢复及 ProjectSwitcher。共享工作区存在并行修改,最终 `npm run typecheck`
|
||||
报错位于非本次修改的 `use-unviewed-completions.test.tsx`,未将全量检查记为通过。
|
||||
|
||||
Windows Electron 使用临时用户目录、当前 Renderer 源码和已有 Main/Preload 输出,验证窗口
|
||||
尺寸 `1440 × 960`、`1024 × 700`、`680 × 560`(系统缩放使实际 CSS 高宽略有偏差)。
|
||||
浅色与深色检查通过:整窗背景隔离、左右分栏、无内容横向溢出、遮罩点击不关闭、原生 Tab 与
|
||||
Escape、关闭后保留原知识库组件。截图保存在本轮临时验证目录;没有运行打包构建,也未单独
|
||||
驱动原生 `WebContentsView`,其遮挡规则通过共享检测测试验证。本次未修改 GoodBuddy Agent
|
||||
或桌面到 Agent 的执行路径,未额外发起真实模型请求。
|
||||
|
||||
## 2026-09-16:已完成保留至查看
|
||||
|
||||
对应 [FR-14](./prd.md#fr-14-项目活动汇总)、[US-E3](./user-stories.md#us-e3-完成后保留至查看)。
|
||||
已核对当前工作区 `use-unviewed-completions.ts`、`conversation-activity.ts` 和 `App.tsx` 的
|
||||
完成通知接入:Renderer 会话内集合、后台 Task 转换、本地及持久化活动运行成功标记、
|
||||
三类计数,以及聊天、设置和文档可见性条件。行为定义见[活动逻辑](./logic-design.md#9-项目活动汇总)。
|
||||
|
||||
活动汇总、完成通知 Hook、项目活动组件、项目选择器及 App 活动集成定向回归共 77 项通过。
|
||||
覆盖前台完成、后台完成、持久化完成补齐、失败和取消、普通会话入口及级联菜单进入、
|
||||
设置覆盖聊天、窗口隐藏、历史完成不提醒、查看后刷新不重复提示和下一轮完成。
|
||||
实际聊天详情加载完成后才清除提示。最终 `npm run typecheck` 与 `npm run lint` 通过。
|
||||
|
||||
全量 `npm test` 用 900 秒预算运行,762.20 秒结束:4,365 项通过、3 项失败、67 项跳过。
|
||||
失败为 `agent-package.test.ts` 离线依赖清单安装内部 60 秒超时,以及
|
||||
`heartbeat-database.test.ts` 两项迁移的 `duplicate column name: pinned`。
|
||||
共享工作区存在并行修改,此项未改动这些文件,不将全量检查记为通过。
|
||||
|
||||
Windows Electron 43.2.0 / Chromium 150 的当前组件 fixture 验证浅深主题、1280×800 和
|
||||
375×667、混合状态及仅完成状态,共 8 个场景、112 项检查通过,8 张 PNG 已审阅。
|
||||
检查包含完成图标、三类计数、原生方向键与 Space、进入回调及移除后的空闲隐藏。
|
||||
证据为临时目录 `activity-completion-0916-results.json` 和 `activity-completion-0916-review.md`。
|
||||
此项使用受控活动数据,未验证完整 Main/Preload 真实模型端到端;模型及知识库请求为 0。
|
||||
本次只修改 Renderer 展示与状态跟踪,本地和远程均消费已有事件/Task 更新,不改变 Agent 执行路径。
|
||||
|
||||
## 2026-09-14:菜单独立高度修正
|
||||
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 状态 | 设计中 |
|
||||
| 版本 | 0.2 |
|
||||
| 日期 | 2026-09-08 |
|
||||
| 状态 | 应用中心与本机推理监控已接入源码,整体验收待完成 |
|
||||
| 版本 | 0.4 |
|
||||
| 日期 | 2026-09-17 |
|
||||
| 关联 PRD | [应用导航与系统工具入口 PRD](./prd.md) |
|
||||
| 功能逻辑 | [功能逻辑设计](./logic-design.md) |
|
||||
| UI 设计 | [UI 设计](./ui-design.md) |
|
||||
|
||||
## 1. 现状
|
||||
|
||||
主页面没有 URL Router,由 `App.tsx` 中的 `WorkspaceView` 和 keep-alive 状态切换:
|
||||
主页面没有 URL Router,由 `App.tsx` 中的 `WorkspaceView` 和 keep-alive 状态切换;该类型也包含由独立状态打开 Modal 的入口:
|
||||
|
||||
```ts
|
||||
type WorkspaceView =
|
||||
@@ -21,6 +21,7 @@ type WorkspaceView =
|
||||
| 'magic-notes'
|
||||
| 'knowledge'
|
||||
| 'heartbeat'
|
||||
| 'local-inference'
|
||||
| 'activity'
|
||||
| 'settings'
|
||||
```
|
||||
@@ -28,90 +29,72 @@ type WorkspaceView =
|
||||
文件、浏览器和终端已经由 `RightAssistantSidebar`、`WorkbarShell` 与 `workbar-contracts.ts`
|
||||
承载。工作区是不可关闭的单实例应用,浏览器和终端是多实例应用。浏览器多实例的跨进程
|
||||
实现以[浏览器多实例技术设计](../assistant-workbar/browser-tabs-technical-design.md)为准。代码中尚无资源指标采集和资源监控
|
||||
面板。现有侧栏底部 `user-card` 整行打开设置,并显示“本地工作区”和平台架构。
|
||||
面板。侧栏底部已替换为应用中心与独立设置按钮。
|
||||
|
||||
本次改动增加入口编排、从主侧栏到工作栏的命令通道,以及本机资源监控的 Main/Preload/
|
||||
Renderer 最小实现。文件、终端和浏览器后端保持不变。
|
||||
应用中心、四应用共享排序、可选应用常驻及启用联动、共同设置页和本机推理监控已接入生产源码;本机资源监控的
|
||||
Main/Preload/Renderer 实现仍待交付。系统工具只使用右侧工作栏已有 Tab 和“+”链路。
|
||||
行为由[功能逻辑](./logic-design.md)定义,验证范围见[实施进度](./progress.md)。
|
||||
|
||||
## 2. 模块边界
|
||||
|
||||
| 模块 | 责任 |
|
||||
| --- | --- |
|
||||
| `App.tsx` | 组合主导航、系统工具栏、设置入口和页面离开检查 |
|
||||
| `SidebarSystemTools`(新增) | 渲染四个系统工具按钮并发送打开意图 |
|
||||
| `RightAssistantSidebar.tsx` | 接收打开意图,调用工作栏实例创建或激活逻辑 |
|
||||
| `App.tsx` | 组合主导航、应用中心、固定设置,持有确认配置及页面离开检查 |
|
||||
| `ApplicationMenu.tsx` / `AnchoredMenu.tsx` | 底部向上锚定的已启用应用菜单;关闭后导航,或打开既有管理 Modal;共享几何定位与键盘行为 |
|
||||
| `ApplicationCenter.tsx` | 浏览、搜索内置清单,调用既有页面导航及共同设置入口,编辑常驻和顺序;复用设置 Modal 样式和焦点工具 |
|
||||
| `ApplicationSettingsView` | 位于 `ApplicationCenter.tsx`,由应用中心对应行进入的应用配置表单 |
|
||||
| `RightAssistantSidebar.tsx` | 保留右侧工具 Tab 和“+”目录调用链路 |
|
||||
| `WorkbarShell.tsx` | 继续负责实例策略、Tab、目录和焦点行为 |
|
||||
| `workbar-contracts.ts` | 继续作为工作栏应用 ID 和实例布局的权威契约 |
|
||||
| `resource-monitor-contracts.ts`(新增) | 定义本机资源快照、可用性和错误码 |
|
||||
| `resource-monitor-service.ts`(新增) | 在 Main 中采集并规范化本机与 GoodBuddy 指标 |
|
||||
| `ResourceMonitorPanel.tsx`(新增) | 按需轮询、保留有界历史并渲染资源面板 |
|
||||
| `application-settings-contracts.ts` | 增加应用导航偏好 schema 和默认值 |
|
||||
| `application-settings-store.ts` | 迁移并原子保存应用导航偏好 |
|
||||
| `PlatformFeaturesSettingsSection` 或独立设置 Section | 编辑“显示在侧边栏”偏好 |
|
||||
| `application-settings-contracts.ts` | 定义常驻、顺序及应用启用字段;保留笔记既有配置键 |
|
||||
| `application-settings-store.ts` | 迁移默认值,串行合并更新并原子保存,返回完整确认快照 |
|
||||
| `PlatformFeaturesSettingsSection` | 移除全局笔记设置页签、跳转入口及专用回调,保留通用设置和远程项目 |
|
||||
|
||||
不要在 `SidebarSystemTools` 中读取文件、创建 PTY、操作浏览器或采集系统指标;该组件只发送
|
||||
有界的工具 ID。
|
||||
应用中心只编排应用导航和配置,不读取文件、创建 PTY、操作浏览器或采集系统指标。
|
||||
不增加左侧工具组件、左侧工具状态镜像或主侧栏到工作栏的专用命令通道。
|
||||
|
||||
## 3. Renderer 契约
|
||||
|
||||
### 3.1 系统工具 ID
|
||||
### 3.1 内置应用清单
|
||||
|
||||
```ts
|
||||
export const sidebarSystemToolIds = [
|
||||
'workspace',
|
||||
'terminal',
|
||||
'browser',
|
||||
'resources'
|
||||
] as const
|
||||
type BuiltInApplicationId = 'magic-notes' | 'knowledge' | 'heartbeat' | 'local-inference'
|
||||
|
||||
export type SidebarSystemToolId = (typeof sidebarSystemToolIds)[number]
|
||||
```
|
||||
|
||||
该类型可以放在 Renderer 内共享模块。首期不暴露 IPC,因为动作只编排已有工作栏 Renderer
|
||||
状态;实际能力调用继续走现有 Preload API。
|
||||
|
||||
### 3.2 打开意图
|
||||
|
||||
`App` 向 `RightAssistantSidebar` 传入单调递增的请求或命令对象:
|
||||
|
||||
```ts
|
||||
type WorkbarOpenIntent = {
|
||||
requestId: number
|
||||
appId: 'workspace' | 'terminal' | 'browser' | 'resources'
|
||||
reason: 'sidebar-system-tool'
|
||||
context?: { conversationId?: string; projectId?: string }
|
||||
type BuiltInApplicationDefinition = {
|
||||
id: BuiltInApplicationId
|
||||
source: 'builtin'
|
||||
titleKey: string
|
||||
descriptionKey: string
|
||||
view: BuiltInApplicationId
|
||||
}
|
||||
```
|
||||
|
||||
`requestId` 使连续点击同一工具仍能被消费。不要仅设置 `assistantSidebarTab`,因为用户可能已
|
||||
关闭对应实例,且终端需要按执行空间寻找实例。
|
||||
全部应用 ID 在 `application-settings-contracts.ts` 的 `BuiltInApplicationId` 定义;`EditableApplicationId` 包含 `magic-notes`、`heartbeat`、`local-inference`。Renderer 的 `applicationDefinitions` 映射图标、国际化键和可选应用启用键,知识库始终可用。
|
||||
`App.tsx` 默认导入 `LocalInferencePage`,传入 `onClose`、`enabled` 和 `restoreFocus`。`local-inference` 保留为入口 ID,`setView` 在提交工作区切换前将其转为 `localInferenceOpen = true` 并返回,不更新 `view` 或 KeepAlive 缓存;组件按该独立状态挂载及卸载。
|
||||
静态清单复用既有应用页面和国际化键。管理 Modal 的搜索只过滤该清单,包含未常驻和关闭的应用,不请求
|
||||
远程目录。启用、常驻和排序从确认配置派生,不在清单中持久化第二份状态。
|
||||
|
||||
建议由 `RightAssistantSidebar` 在消费意图后通过现有 `WorkbarShell` 控制接口完成。控制器必须
|
||||
接收上下文和调用原因,不能只按 `appId` 查找第一个实例:
|
||||
### 3.2 应用与设置导航
|
||||
|
||||
```ts
|
||||
type WorkbarController = {
|
||||
openOrActivate(intent: WorkbarOpenIntent): Promise<WorkbarTabInstance>
|
||||
}
|
||||
```
|
||||
应用中心调用既有 `navigateFromSidebar` 或同一页面导航函数,沿用未保存离开检查和焦点
|
||||
策略。打开前检查确认的启用值;未常驻但已启用的应用不附带常驻写入。本机推理监控从常驻入口、菜单及管理 Modal 均经 `setView('local-inference')` 打开独立 Modal,保留底层工作区与设置草稿;其他应用继续页面导航。
|
||||
`ApplicationMenu` 按四个应用共用的持久化顺序派生清单,仅按启用过滤,不读取常驻或打开历史。应用行先关闭菜单再导航;底部“管理应用”关闭菜单、恢复锚点焦点并打开既有 `ApplicationCenter` Modal。
|
||||
应用中心对应行的设置操作仅接受 `EditableApplicationId`;应用页和全局设置不提供重复入口。知识库的管理行提供“打开”和排序,原工作区实际配置保留,不添加通用空设置页。
|
||||
共同页复用设置 Modal 容器、配置读取及更新方法,不建立独立配置存储或重复表单。
|
||||
|
||||
若当前 `WorkbarShell` 没有命令式入口,先增加窄的回调或受控请求 Props,不引入全局事件总线。
|
||||
### 3.3 工作栏选择与创建
|
||||
|
||||
工作区意图始终激活注册表中的不可关闭单实例;仅在迁移或布局损坏时补回。浏览器意图按
|
||||
`conversationId` 激活最近使用的匹配实例,没有匹配实例时创建一个。工作栏“+”不走该去重
|
||||
路径,选择浏览器时始终创建新实例。
|
||||
已有 Tab 仅按实例 ID 激活,保留目标和历史状态。不得按当前项目、执行空间或 Conversation
|
||||
查找最近匹配实例替代用户选择,也不在选中时自动重建退出或失败终端。
|
||||
|
||||
### 3.3 终端匹配
|
||||
|
||||
工作栏“+”目录选择终端时每次创建新实例;侧栏系统工具按钮执行“进入当前终端”,采用:
|
||||
|
||||
1. 解析当前项目对应的 `ExecutionSpaceSelection`。
|
||||
2. 在现有终端实例中按目标 identity 查找 `starting` 或 `running` 状态。
|
||||
3. 存在多个匹配实例时激活最近使用的实例。
|
||||
4. 没有匹配实例时调用现有终端创建流程。
|
||||
|
||||
`closing`、`exited`、`interrupted` 和 `failed` 实例保留为历史 Tab,不阻止快捷按钮创建新终端。
|
||||
工作栏“+”继续按原规则创建额外终端,不受快捷按钮去重影响。
|
||||
“+”选择终端每次调用既有创建流程,解析当前 `ExecutionSpaceSelection`,绑定本机或托管
|
||||
SSH 项目的权威目录;无项目时使用本机 Home。选择浏览器每次创建额外实例,绑定当前
|
||||
Conversation,无 Conversation 时创建待绑定空实例。同一 Conversation 的浏览器页面独立,
|
||||
登录上下文共享规则继续由工作栏负责。文件按单实例聚焦,缺失时仅由布局修复补回。
|
||||
选择现有文件 Tab 保留跟随或固定模式,固定目标失效时不切换目标。
|
||||
|
||||
### 3.4 资源监控工作栏应用
|
||||
|
||||
@@ -125,8 +108,8 @@ type WorkbarController = {
|
||||
}
|
||||
```
|
||||
|
||||
该应用不加入升级后的默认打开实例。侧栏资源按钮与工作栏“+”目录都通过既有单实例规则创建
|
||||
或聚焦它。
|
||||
该应用不加入升级后的默认打开实例,仅通过右侧已有 Tab 或“+”目录访问,使用既有单实例
|
||||
规则创建或聚焦。左侧应用清单不包含 `resources`。
|
||||
|
||||
## 4. 应用导航偏好
|
||||
|
||||
@@ -135,31 +118,35 @@ type WorkbarController = {
|
||||
在 `ApplicationSettings` 增加独立字段:
|
||||
|
||||
```ts
|
||||
type BuiltInApplicationId = 'magic-notes' | 'knowledge' | 'heartbeat'
|
||||
|
||||
type ApplicationNavigationSettings = {
|
||||
hiddenBuiltInApplicationIds: BuiltInApplicationId[]
|
||||
order: BuiltInApplicationId[]
|
||||
pinned: Record<EditableApplicationId, boolean>
|
||||
}
|
||||
```
|
||||
|
||||
默认值为空数组,保持升级前所有可用入口可见。ID 数组可以避免为每个应用增加一个顶层布尔
|
||||
字段;Zod schema 必须去重、限制数量并拒绝未知内置
|
||||
ID。
|
||||
|
||||
首期不保存排序,因为内置应用顺序固定。插件偏好在插件系统确定稳定 identity 和卸载保留
|
||||
规则后另行扩展,不提前混入当前 schema。
|
||||
`applicationNavigation` 保存四个应用的完整顺序和三个可选应用的常驻布尔值。默认顺序为
|
||||
`knowledge`、`heartbeat`、`magic-notes`、`local-inference`;心跳和魔法笔记常驻默认 `true`,本机推理监控默认 `false`。
|
||||
权威返回 schema 和更新 schema 均要求四个 ID 各出现一次,拒绝重复、未知和缺失 ID;`pinned` 包含三个可选应用,缺失 `heartbeat` 时补 `true`,显式布尔值保留。Renderer 直接读取该顺序,再按各入口的可见性过滤。知识库始终启用、常驻,无可写开关键。
|
||||
按[排序规则](./logic-design.md#3-主导航组合规则)提交完整顺序;不持久化可推导的可见 ID 列表。
|
||||
首期不预建插件字段,也不为未发布的旧隐藏 ID 草案增加迁移读取器。
|
||||
|
||||
### 4.2 与现有开关组合
|
||||
|
||||
入口可见条件为:
|
||||
可选应用侧栏入口可见条件如下,知识库按保存位置始终显示:
|
||||
|
||||
```ts
|
||||
const visible = featureEnabled && !hiddenBuiltInApplicationIds.includes(id)
|
||||
const visible = featureEnabled && applicationNavigation.pinned[id]
|
||||
```
|
||||
|
||||
`magicNotesEnabled` 继续控制功能启停。知识库和智能心跳若未来增加业务启停开关,也按同一组合
|
||||
规则处理。功能停用时设置页保留显隐偏好但禁用显隐 Switch,并提供原功能设置入口。导航设置
|
||||
不能写回现有开关。
|
||||
`magicNotesEnabled` 保留为笔记启用的唯一权威键,`localInferenceEnabled` 为本机推理监控应用启用键,默认 `true`,不在导航配置再复制 enabled map。`heartbeatEnabled` 控制心跳与监督者应用;Renderer 仅将 `settings?.heartbeatEnabled === true` 视为启用,设置未加载时也不开放入口。默认值及运行边界见[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行),Main 与侧栏接入见[监督者运行路径](../conversation-supervision/technical-design.md#2-运行路径)。关闭可选应用时保留常驻控件和恢复入口。
|
||||
|
||||
笔记配置使用 `magicNotesShowIncompleteTodoCount`、`magicNoteCommentMode`、
|
||||
`magicNoteCommentFormat` 和 `magicNoteCanvasPageCount`,应用中心与笔记页使用同一 schema 和更新链路。
|
||||
画布发送页数默认 1、范围 1~8,具体选择规则见[画布分析契约](../magic-notes/technical-design.md#canvas-analysis-and-comments)。未配置的笔记
|
||||
布尔值(含 `magicNotesEnabled`)默认 `true`;迁移必须用字段是否存在区分缺失与显式 `false`,
|
||||
不能用 `value || true`。已有单选值保留,缺失时分别使用 `immediate` 和 `combined`。
|
||||
若旧“显示入口”实际绑定 `magicNotesEnabled`,保留其启用含义和原值;独立常驻使用新字段,
|
||||
不得直接把旧业务启用键改名为 pinned。只为确认已发布的独立显隐偏好增加对应迁移。
|
||||
|
||||
### 4.3 持久化与 IPC
|
||||
|
||||
@@ -173,61 +160,73 @@ Renderer
|
||||
-> application-settings.json
|
||||
```
|
||||
|
||||
更新继续由 Main 校验可信 sender、Zod 输入和原子写入。设置版本迁移只补充默认空数组,不
|
||||
修改已有 `magicNotesEnabled` 或其他平台设置。
|
||||
Main 继续校验可信 sender、Zod 输入,复用 `ApplicationSettingsStore` 的更新队列,读取最新
|
||||
确认值后合并本次 patch、原子写入并返回完整 `ApplicationSettings`。写入失败不发布目标值。
|
||||
存储版本 12 补充可选应用导航和启用字段,为缺失字段补默认值;保留已有
|
||||
`true`、`false`、单选值和无关设置,不迁移或清理笔记、知识及会话业务数据。
|
||||
新安装和升级时缺失整个 `applicationNavigation` 均使用共享默认值,本机推理监控启用但不常驻;本次不增加存储版本。已保存的常驻值均保留,无法区分旧默认 `true` 与用户主动固定,不强制改为 `false`。
|
||||
|
||||
仅存储读取允许补齐旧数据:缺失的 `knowledge`、`heartbeat` 按此顺序前置,再保留已有 ID 的相对顺序,最后按默认顺序追加缺失的可选应用。旧 `[local-inference, magic-notes]` 因此变为 `[knowledge, heartbeat, local-inference, magic-notes]`;完整四项顺序原样保留。缺失或空顺序采用默认顺序,部分 `pinned` 映射仅补缺失键,显式 `false` 保留。重复或未知 ID 仍按现有损坏文件处理规则处理,不静默去重或丢弃。版本 11 迁移时写入归一化结果;版本 12 读取时归一化,下一次设置保存时写入完整结果。IPC 和配置工具写入不接受这些部分数据。
|
||||
|
||||
版本 12 属于未发布的分支格式,原草案 `knowledgeEnabled` 已移除,不增加兼容读取器。`heartbeatEnabled` 是当前应用启用字段,由 Store 归一化缺失值并保留显式保存值。
|
||||
|
||||
## 5. 侧栏底部重构
|
||||
|
||||
现有 `user-card` 是一个覆盖整行的设置按钮。实现时替换为容器和相邻独立按钮:
|
||||
原 `user-card` 整行设置按钮已替换为容器和相邻独立按钮:
|
||||
|
||||
```tsx
|
||||
<div className="sidebar-footer">
|
||||
<SidebarSystemTools onOpenTool={openSystemTool} />
|
||||
<button onClick={toggleApplicationMenu} aria-haspopup="menu" aria-expanded={applicationMenuOpen} ...>
|
||||
{t('navigation.applicationCenter')}
|
||||
</button>
|
||||
<button aria-label={t('navigation.settings')} ...>
|
||||
<Settings aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
不能在设置按钮内部嵌套工具按钮。设置按钮继续调用 `navigateFromSidebar('settings', trigger)`,
|
||||
保留设置预加载、焦点恢复和未保存离开确认。
|
||||
应用中心和设置为相邻独立按钮。设置按钮继续调用 `navigateFromSidebar('settings', trigger)`,
|
||||
保留设置预加载、焦点恢复和未保存离开确认。该调用只打开设置 Modal,不更新当前工作区或
|
||||
KeepAlive 缓存;`settingsOpen` 独立管理打开状态。关闭后卸载设置内容,下次按入口指定的分类
|
||||
和消息通道重新挂载,避免缓存上次分类覆盖本次直达目标。底层工作区继续挂载,普通关闭不跳回聊天。
|
||||
|
||||
系统工具按钮调用统一 `openSystemTool`:
|
||||
应用菜单使用 `AnchoredMenu` 的 body Portal,向上锚定,无模态遮罩或背景隔离;`menu` 语义纳入共享浏览器 viewport 相交遮挡。管理 Modal 及共同设置沿用原 Portal、焦点隔离、恢复及 Escape 机制。
|
||||
不在主侧栏复制工具入口。工作栏仍由窗口右上方固定开关展开和关闭,关闭后焦点返回该开关。
|
||||
|
||||
1. 设置工作栏打开意图。
|
||||
2. 展开工作栏。
|
||||
3. 由工作栏创建或激活实例。
|
||||
4. 按现有策略把焦点移入目标面板。
|
||||
## 6. 应用可用性与知识请求
|
||||
|
||||
工作栏仍由窗口右上方的固定开关关闭,关闭后的焦点返回该开关。
|
||||
Renderer 从同一确认快照派生可选应用主导航、中心状态和页面可用性,依赖只包含相应
|
||||
字段。keep-alive 页挂载不代表应用仍启用;禁用后在原位置渲染不可用状态,
|
||||
保留页面草稿及业务数据,阻止新的页面操作。重新启用不触发导航或业务重放。
|
||||
|
||||
## 6. 工作栏状态与快捷按钮
|
||||
`ApplicationAvailability` 隐藏并设置业务内容为 inert,保留挂载的页面实例;重新启用从
|
||||
应用中心对应行的设置进入。笔记页面直接消费确认的评论配置,
|
||||
不因修改评论方式而重建编辑器。
|
||||
|
||||
`RightAssistantSidebar` 应向 `App` 暴露当前活动实例的 `appId` 和必要的有界状态摘要,或由
|
||||
现有 `onTabChange` 扩展得到。主侧栏不读取终端输出、文件内容、URL 或活动详情。
|
||||
知识库不经过 `ApplicationAvailability`;会话知识按钮、范围、检索方式和标签继续遵守原知识库规则。心跳页面受应用可用性控制,单条计划 enabled 保持不变。
|
||||
请求构建没有应用级禁用知识库的裁剪或守卫;本机、托管 SSH 和排队请求沿用原知识上下文链路。
|
||||
单个知识库实例的配置、停用、删除及取消清理仍由知识库功能负责。
|
||||
|
||||
```ts
|
||||
type SidebarSystemToolState = {
|
||||
activeToolId?: SidebarSystemToolId
|
||||
statusByToolId?: Partial<Record<SidebarSystemToolId, 'attention' | 'error' | 'running'>>
|
||||
}
|
||||
```
|
||||
## 7. 串行保存与确认快照
|
||||
|
||||
首期可以只实现 `activeToolId`,没有可靠聚合来源时不显示状态徽标。不能根据局部事件猜测错误
|
||||
或运行状态。
|
||||
中心与应用页的启用、常驻、排序及笔记配置共用一个 Renderer 更新入口及 pending
|
||||
状态。任一提交未完成时,所有入口中修改这组设置的控件暂时禁用;下一次操作基于最新确认
|
||||
快照生成 patch,不提交整个过期 `ApplicationSettings` 覆盖无关字段。Main 队列仍负责串行合并。
|
||||
|
||||
## 7. 应用显隐更新
|
||||
|
||||
应用显隐 Switch 复用平台功能设置的即时更新模式。请求开始后禁用三个应用显隐 Switch,只
|
||||
允许一个完整目标集合处于提交中。Main 返回成功结果后更新 App 中的平台设置快照,主导航
|
||||
随之变化。失败时恢复控件原值,在对应设置行显示可重试错误;主导航继续读取最后一次成功
|
||||
保存的设置。
|
||||
`ApplicationSettingsStore.update` 在持久化成功后通过 `onChanged` 发布完整快照;IPC 转发
|
||||
`applicationSettingsChanged`,Preload 提供 `updates.onSettingsChanged`。工具配置服务调用同一 Store,
|
||||
因此配置工具写入也同步界面,保存失败不发事件。`App` 收到事件后推进读取修订号并替换快照,
|
||||
迟到的旧读取及旧保存响应不能覆盖新状态。每次重开中心都刷新配置,刷新期间锁定修改,失败后
|
||||
保留锁和重试入口;控件不先乐观生效。
|
||||
明确保存失败时保留最后确认值,就地显示可重试错误与编辑上下文。若 IPC 响应中断而写入
|
||||
结果未知,通过既有设置读取重新取得权威快照再解锁;读取失败继续显示错误并允许重试读取,
|
||||
不自动重发变更或写回旧配置。无须引入持久化回滚日志或独立导航快照文件。
|
||||
|
||||
## 8. 插件兼容边界
|
||||
|
||||
首期不增加插件注册表、插件 schema 或插件持久化字段,只保留系统 ID 不向插件开放的产品边界。
|
||||
插件市场设计需要单独确定 Host、权限、签名、生命周期和稳定 identity;不得直接复用 DeepSeek
|
||||
Harness 的 npm 插件市场。
|
||||
首期不增加市场 URL、YAML 导入导出、ShareServer API、插件注册表或安装服务。FR-15 的
|
||||
私有市场协议、YAML Schema、资源分发与扩展生命周期另行设计;系统 ID 不向插件开放。
|
||||
不得直接复用 DeepSeek Harness 的 npm 插件市场。后续依赖见第 11 节,首期不预建占位界面。
|
||||
|
||||
## 9. 本机资源监控
|
||||
|
||||
@@ -333,23 +332,28 @@ resourceMonitor.getLocalSnapshot(input: {
|
||||
|
||||
### 10.1 Renderer 测试
|
||||
|
||||
- 固定入口顺序不受三个应用显隐组合影响。
|
||||
- 运行记录和设置没有显隐控件。
|
||||
- 四个系统工具按钮具有名称、工具提示、键盘行为和选中状态。
|
||||
- 文件复用不可关闭单实例;浏览器和终端按当前上下文聚焦匹配实例,工作栏“+”允许显式多实例。
|
||||
- 工具按钮展开工作栏但不改变当前主页面。
|
||||
- 显隐更新成功后主导航立即变化,失败时 Switch 和主导航恢复原状态。
|
||||
- 窄窗口下工具按钮不重叠,焦点恢复正确。
|
||||
- 固定入口不受应用启用、常驻和顺序组合影响,应用中心及设置始终可达;左侧没有重复系统工具。
|
||||
- US-B6:底部菜单向上锚定且无遮罩,只列已启用应用,不受常驻或打开历史影响;点击行关闭菜单并导航,“管理应用”进入既有可搜索 Modal,包含关闭及未常驻应用。
|
||||
- US-F3/US-F5:三个入口均打开本机推理监控 Modal 并保留底层工作区;覆盖嵌套确认的焦点循环、逐层 Escape、提交期间禁止关闭与重复执行、关闭后的入口及后备焦点恢复,以及停用内容后的键盘焦点。
|
||||
- US-B7:常驻、取消常驻、键盘及拖动排序成功后同步,重新开启及重启保留原偏好和顺序。
|
||||
- US-B8:知识库始终启用且常驻,对应行提供打开与排序,不提供启用或常驻开关;心跳应用启停按监督者逻辑验收,单条计划 enabled 不变;可选应用关闭后的不可用页保留草稿,重新开启不自动导航或重放。
|
||||
- US-B9:应用中心对应行进入笔记表单,管理启用、常驻、待办数量、评论方式及形式;笔记页和全局设置无重复入口。
|
||||
- US-B5:所有入口共享 pending,保存失败保留相同确认值;重开刷新期间锁定修改,较新事件使旧读取失效;结果未知时重读,不自动重发。
|
||||
- 文件复用不可关闭单实例;选择任一已有终端或浏览器 Tab 保留确切目标,“+”每次创建额外实例。
|
||||
- 已退出、失败的终端 Tab 不因选中而重建;工具切换不改变主页面,后台状态不抢占 Tab。
|
||||
- 窄窗口、键盘排序、共享 Switch、Tab 语义及浮层焦点恢复正确;中心和设置不被原生浏览器遮挡。
|
||||
- 资源面板只在可见活动时轮询,跳过重叠请求并把历史限制为 150 项。
|
||||
- 窗口隐藏、最小化和恢复时停止轮询并重新建立区间基线。
|
||||
- 资源部分可用、全部失败和重新进入活动状态均显示正确结果。
|
||||
|
||||
### 10.2 Main 与契约测试
|
||||
|
||||
- 新设置字段默认值、合法 ID、重复 ID、数量上限和未知 ID 校验。
|
||||
- 旧版本设置迁移后默认显示所有既有应用。
|
||||
- 更新导航偏好不修改魔法笔记启停等无关字段。
|
||||
- 原子写入失败时保留最后一次有效设置。
|
||||
- 顺序包含四个应用 ID 各一次;公开写入拒绝重复、缺失、未知 ID 及无效常驻字段;旧存储顺序按迁移规则补齐,不为未发布的 `knowledgeEnabled` 添加迁移。
|
||||
- 未配置的心跳应用默认关闭,其余应用启用及笔记布尔项默认开启,本机推理监控默认不常驻;已保存的启用、常驻值和评论单选值经迁移及重启保留。
|
||||
- 更新常驻或顺序不修改启用、计划或业务数据;笔记既有配置键保持语义。
|
||||
- 串行 patch 在 Main 最新快照上合并,原子写入失败不发布目标状态;响应丢失后重读实际结果。
|
||||
- Store 持久化后才发布变更,工具配置写入使用同一通知链路;知识库不受可选应用偏好影响。
|
||||
- 保存启用失败不会改变业务判断;会话关联和历史知识引用不因入口隐藏被清空。
|
||||
- 资源快照拒绝 NaN、Infinity、负数字节数、越界百分比和未知错误码。
|
||||
- 采集超时、权限不足和磁盘 IO 不支持时返回有界部分结果。
|
||||
- 超时采集尚未实际结束时保持单飞,不累积新的后台采集任务。
|
||||
@@ -369,22 +373,33 @@ resourceMonitor.getLocalSnapshot(input: {
|
||||
- 浅色、深色、`220px` 与 `420px` 侧栏宽度。
|
||||
- Windows、macOS 和 Linux 的真实 CPU、内存与磁盘 IO 采集;不支持项显示准确原因。
|
||||
|
||||
FR-16 单独覆盖 US-F3 概览去重、未知指标、US-F4 单任务取消与完成竞态、US-F5 服务
|
||||
实际启动/停止/重启或加载/释放、使用方确认、外部管理限制及失败真实状态;用接入的真实引擎
|
||||
验证共享服务不被单任务取消终止,重启不重放。设计文档检查或模拟服务不算生产路径验证。
|
||||
|
||||
## 11. 实施顺序
|
||||
|
||||
实施范围以 [PRD 分期](./prd.md#8-分期)为准。本节替代旧实施顺序;前文关于四个左侧
|
||||
系统工具按钮、仅持久化隐藏 ID 的契约需在实现前同步,不能直接按旧规格开发。
|
||||
实施范围以 [PRD 分期](./prd.md#8-分期)及最新评审决定为准,逻辑映射见
|
||||
[需求对应](./logic-design.md#6-与需求的对应关系)。既有 FR/US 编号保持稳定;新增故事使用
|
||||
US-B6 应用中心、US-B7 顺序、US-B8 启用联动、US-B9 笔记迁移,以及 US-F3 概览、
|
||||
US-F4 取消、US-F5 服务生命周期。PRD、User Stories、配套设计与根 UI 规范已同步入口及持久化规则。
|
||||
|
||||
1. 同步 PRD 功能要求、User Stories、逻辑、UI 与技术契约,收敛首期应用中心样式。
|
||||
以下为实施阶段划分,应用中心及本机推理监控已接入源码,验证状态以进度记录为准。
|
||||
|
||||
1. 底部入口使用向上锚定的轻量菜单,通过“管理应用”进入既有整窗 Modal;核对已发布配置的迁移来源。
|
||||
2. 接入现有内置应用页面与最底部应用中心入口,保留独立设置和固定运行记录入口。
|
||||
3. 为常驻显隐和排序设计最小持久化契约,保留已发布的相关设置与用户数据,按保存结果更新导航。
|
||||
4. 接入应用设置的共同界面与既有设置更新路径,先验证魔法笔记多入口状态一致性。
|
||||
5. 验证右侧现有文件、终端、浏览器 Tab;移除计划中的左下角工具快捷条和专用于该快捷条的意图接口工作。
|
||||
3. 实现常驻、完整顺序及启用契约,迁移时保留明确旧值,通过串行保存与确认快照同步所有界面。
|
||||
4. 迁移魔法笔记共同设置,保留知识库始终可用及原工作区配置、心跳单条计划 enabled,验证可选应用关闭和重新开启。
|
||||
5. 验证右侧现有文件、终端、浏览器 Tab 的确切目标与“+”额外实例行为,不实现左侧重复工具条。
|
||||
6. 单独实现资源快照契约、Main 采集服务、Preload API 与右侧 `resources` 单实例面板。
|
||||
7. 验证键盘、焦点、浅深主题、窄窗口、设置失败、重启持久化和跨平台行为。
|
||||
|
||||
首期使用内置应用清单,不依赖远程商店、插件注册或安装服务。商店目录、推荐、安装与
|
||||
更新机制在后续独立设计中确定;Demo 中的模拟商店不接入首期产品界面。
|
||||
|
||||
本轮 `local-inference` 已接入管理 Modal、类型化 IPC 与真实引擎管理,支持范围见 11.1;
|
||||
实机和自动化验证范围见[实施进度](./progress.md)。
|
||||
|
||||
后续设计输入见 [FR-15](./prd.md#fr-15-私有化市场与-yaml-交换后续) 与
|
||||
[FR-16](./prd.md#fr-16-本机模型管理入口新增计划项):
|
||||
|
||||
@@ -396,11 +411,50 @@ resourceMonitor.getLocalSnapshot(input: {
|
||||
市场来源配置不覆盖现有模型、应用更新或 Runtime 下载源设置。
|
||||
- ShareServer 的市场发布与分发实现由其独立仓库承接,本仓库先记录依赖,不将桌面需求
|
||||
记录标为服务端已实现。
|
||||
- 本机模型管理实施前盘点现有 OCR、向量、语音及推理服务的生命周期接口,优先复用其
|
||||
Main 服务。只为实际支持的操作提供 typed IPC;区分受管子进程、外部服务和进程内模型,
|
||||
不把直连模型 HTTP 请求等同于独立模型进程。
|
||||
- 模型管理的详细逻辑需确定共享服务的使用方识别、忙碌时停止与重启、退出应用时的释放
|
||||
行为,再接入管理页面并验证实际引擎。该工作单独排期,不依赖市场上线。
|
||||
|
||||
### 11.1 本机推理任务与执行服务
|
||||
|
||||
ASR、OCR 与内置向量的跨功能执行边界、任务控制缺口及有条件的优化方向,见[本机专用推理架构与任务控制评估](../../architecture/local-inference-task-control.md)。该评估区分源码现状、建议与待办,未将常驻化或后台宿主迁移列为已采用实现。
|
||||
|
||||
`LocalInferencePage` 默认导出,接收必需的 `onClose` 及可选的 `enabled`、`restoreFocus`,通过 `createPortal(..., document.body)` 渲染管理 Modal,复用 `PageHeader` 和共享
|
||||
`activateModalFocus`/`trapTabFocus`。`ImpactConfirmation` 使用独立 Portal 和紧凑确认样式;共享焦点工具隔离下层 Modal,并在关闭时恢复可用触发元素或指定后备焦点。`operation` ref 防止重复执行,`busy` 和 `control` 控制关闭与 Escape,交互细节见 [UI 设计](./ui-design.md#91-本机推理管理)。`local-inference-contracts.ts` 定义快照、操作与取消输入;Main 注册四个
|
||||
`local-inference:*` 通道,复用可信 sender 校验和 Zod 输入校验。Preload 只暴露读取、服务操作、
|
||||
任务取消及打开既有设置的方法,不接收 PID、命令或模型路径。
|
||||
|
||||
| 引擎 | 生产接入与可用操作 | 明确限制 |
|
||||
| --- | --- | --- |
|
||||
| ASR / sherpa-onnx | `SpeechTranscriptionService` 登记真实请求;执行路径保留独立 Worker 请求取消 | 每次请求加载、结束释放;未提供 Worker 就绪观测,模型可用时状态为 unknown,不从活动请求推断 running |
|
||||
| OCR / PaddleOCR | `DocumentOcrBroker` 登记文档请求;`document-ocr-bridge` 提供实际 Worker 状态、加载及空闲释放 | 有活动或排队请求时拒绝释放;不把终止共享 Worker 暴露为单任务取消 |
|
||||
| 内置向量 / Granite | 按模型目录复用引用计数 Broker;启动、停止、重启;取消消息等待 Worker 结果或取消确认 | 启动确认进程 spawn,模型在首个请求时加载;不把进程启动称为模型已加载 |
|
||||
| TTS | 显示当前没有执行服务 | 不提供虚假任务或控制 |
|
||||
| 外部向量连接 | 快照契约保留配置,Modal 不显示;通过既有模型设置管理 | 本页不主动探测,不推断本机归属,不控制外部进程 |
|
||||
|
||||
`LocalInferenceService` 在 Main 内存中登记真实 ASR、OCR、向量请求,保留活动请求和最近
|
||||
100 条终态记录。只保存能力来源、时间、模型(执行路径有提供时)、状态和错误,不保存音频、
|
||||
图片或向量输入文本。运行阶段包括排队、模型加载及执行,没有统一阶段进度;来源未提供项目或
|
||||
会话对象时不补造对象关联。Modal 仅使用活动请求生成影响确认,不展示任务历史或单任务取消。
|
||||
|
||||
向量执行端通过 `utilityProcess.fork` 创建独立进程。每个 transport 持有自己的资源采样器,按当前 `child.pid` 从 `app.getAppMetrics()` 匹配 Utility 条目。服务快照的 `resources.scope` 为 `service-process`,携带 PID、采样时间及可用指标;无进程或无法采集时为 `unavailable` 并给出原因。Broker 清除退出的 transport,重启使用新采样器,不复用旧进程指标。
|
||||
|
||||
Modal 可见且启用时每 5 秒读取一次快照,跳过重叠请求。CPU 使用累计 CPU 秒数之差除以单调时钟间隔,100% 表示一个逻辑核心,可超过 100%;首次读取、PID 或创建时间变化、采样间隔超过 15 秒、计数回退及采样失败后重新建立基线。15 秒阈值为定时器延迟和跳过重叠请求留出余量。工作集由 Electron 的 KiB 转为契约中的字节,界面显示 MiB。缺少 CPU 时间或内存读数时只标记该指标不可用,不补零。数值为整个向量服务进程的占用,包含运行时和并发使用方,不是单任务或模型权重大小。
|
||||
|
||||
ASR 使用主进程中的 `node:worker_threads`;OCR 使用渲染进程中的 Web Worker,两者均没有独立进程工作集,逐行标明无法独立统计 CPU/内存。没有将主进程或整个 Renderer 的读数归属给服务。显存未采集,本机资源监控工作栏应用仍未实现。
|
||||
|
||||
服务操作按 serviceId 串行,操作期间拒绝新增该服务请求;停止前校验所有当前活动 taskId
|
||||
都在用户确认清单内,新增使用方要求刷新后重新确认。向量停止等待实际 child exit;失败保留
|
||||
可观测状态和错误。Renderer 操作失败时关闭旧确认并重新读取快照,刷新期间或失败时禁用服务操作;再次操作须重新确认当前活动请求。重启不重放中断请求。任务取消与服务停止分开,完成竞态保留执行方结果。
|
||||
|
||||
向量 Broker 将调用方 Promise 与 Worker 执行记录分开。调用方取消或超时可先返回错误,
|
||||
执行记录在 Worker 确认、返回结果或进程退出前仍为活动项,保留在途名额并计入服务停止影响;
|
||||
停止等待超时也不能提前移除记录。请求取消与自然完成竞争时采用执行方结果。
|
||||
|
||||
OCR 模型可用性由 Main 校验,Worker 运行状态由实际持有它的 Renderer 模块提供。手动加载
|
||||
同样复用既有 Worker,加载超时恢复为可重试错误;空闲 60 秒自动释放。Modal 启用、文档可见且内容未被 hidden/inert 隔离时每 5 秒刷新,跳过未结束的请求;嵌套确认期间暂停新快照读取。
|
||||
关闭 Modal 时卸载组件并清理轮询,窗口隐藏、关闭管理应用或取消常驻不会触发服务停止。退出整个进程继续
|
||||
沿用原有应用清理路径。模型文件卸载仍在模型设置中独立进行。
|
||||
|
||||
向量 Main 引擎使用 `onnxruntime-web` 的 Node 条件入口,避免浏览器 `/wasm` 入口通过 fetch
|
||||
加载本地文件失败。执行方的具体错误随任务返回;Renderer 不把失败展示为零指标或恢复成功。
|
||||
|
||||
## 12. 项目活动汇总
|
||||
|
||||
@@ -408,8 +462,17 @@ resourceMonitor.getLocalSnapshot(input: {
|
||||
派生状态,界面行为见[活动界面](./ui-design.md#10-项目活动汇总)。
|
||||
|
||||
- `conversation-activity.ts` 从 `App.tsx` 的会话、`activeConversationIds`、全部可见
|
||||
`assistantTasks` 和项目元数据派生会话行、全局及项目计数,不新增持久化活动副本。
|
||||
`assistantTasks`、`completedConversationIds` 和项目元数据派生会话行、全局及项目计数,不新增持久化活动副本。
|
||||
`ProjectActivity` 在有活动时渲染汇总与项目、会话级联菜单,`ProjectSwitcher` 复用项目计数。
|
||||
- `use-unviewed-completions.ts` 在 Renderer 内存维护完成集合和上一轮 Task 状态;已观察到的
|
||||
顶层 Task 转为 `completed` 时加入集合,初始历史不生成通知。`App` 的本地 `done` 事件和
|
||||
活动运行持久化快照的成功终态调用 `markConversationCompleted`,失败、取消不调用。
|
||||
新运行及 Task 恢复运行或等待处理时清除旧通知。具体优先级和通知生命周期以活动逻辑为准。
|
||||
- `App` 仅在 `view === 'chat' && !settingsOpen && !applicationCenterOpen` 时向 Hook 传入当前会话 ID;Hook 监听
|
||||
`visibilitychange`,文档不为 `hidden` 时才按当前会话清除通知。这使普通侧栏、活动菜单
|
||||
导航和可见性恢复共用查看规则,KeepAlive 挂载本身不代表查看。集合不写入 SQLite 或设置。
|
||||
- 聚合状态新增 `completed`,全局和项目计数包含 `attention`、`running`、`completed`,
|
||||
同一 Conversation ID 只按最高优先级计数;活动行为空才隐藏摘要。
|
||||
- `App` 向 `ProjectActivity` 传入项目元数据、活动行、侧栏可见状态与既有精确跳转回调。
|
||||
菜单按项目 ID 分组并记忆化派生集合;排序仅影响展示,不改聚合状态或持久化数据。
|
||||
- 两层菜单在同一 body Portal 内相接,桌面会话层使用独立 fixed 定位,不参与项目层高度计算。
|
||||
@@ -425,13 +488,15 @@ resourceMonitor.getLocalSnapshot(input: {
|
||||
- `AssistantDatabase.listTasks()` 保留最近历史窗口(默认 100 条),额外包含窗口外当前
|
||||
运行或等待处理的可见 Task;`activeVisibleTaskSelect` 与 `toTask` 的有效状态保持一致,
|
||||
待调度运行规范化为 `queued`,不因旧 Task 状态被误纳入实时集合。
|
||||
- `listConversations()` 保留最近 100 条活动会话,同时包含上述 Task 关联的较早活动会话,
|
||||
以及含 `streaming` 消息的较早活动会话。两类列表去重后仍按原时间顺序返回,结果允许超过
|
||||
历史窗口大小,避免后台工作被最近记录挤出。
|
||||
- `listConversations()` 的范围与排序遵守
|
||||
[会话列表读取与前端保留](../assistant-workbar/execution-history-storage.md#会话列表读取与前端保留),
|
||||
包含上述 Task 关联及含 `streaming` 消息的较早活动会话,避免后台工作被最近记录挤出。
|
||||
`listTasks()` 仍按原时间顺序返回;列表结果允许超过历史窗口大小。
|
||||
- `openActivityConversation` 按确切 Conversation ID 导航;内存中缺失时刷新持久化会话
|
||||
列表并合并,再查找同一 ID。刷新失败或会话已不存在时显示通知,不回退到项目最新会话。
|
||||
设置离开检查通过后才提交项目、会话及界面清理状态。
|
||||
- 远程恢复成功提示沿用独立的自动消失计时器;活动汇总更新不重置或替代该计时器。
|
||||
|
||||
验证记录见[项目活动级联实施进度](./progress.md)。本次只改 Renderer 展示与交互,远程
|
||||
Runtime、Agent 协议和桌面到 Agent 的执行路径未改变。
|
||||
完成通知的定向验证应覆盖本地成功、持久化活动运行成功、后台 Task 状态转换、初始历史、
|
||||
失败和取消、实时状态优先、三类计数、普通侧栏查看、设置覆盖、隐藏文档与恢复可见、无活动行隐藏。
|
||||
验证记录见[项目活动级联实施进度](./progress.md);源码核对不能替代真实 Electron 路径验证。
|
||||
|
||||
@@ -4,15 +4,16 @@
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 状态 | 设计中 |
|
||||
| 版本 | 0.2 |
|
||||
| 日期 | 2026-09-08 |
|
||||
| 状态 | 应用中心与本机推理监控已接入源码,整体验收待完成 |
|
||||
| 版本 | 0.4 |
|
||||
| 日期 | 2026-09-17 |
|
||||
| 关联 PRD | [应用导航与系统工具入口 PRD](./prd.md) |
|
||||
| 功能逻辑 | [功能逻辑设计](./logic-design.md) |
|
||||
| 全局规范 | [GoodBuddy 统一界面设计系统](../../../UI-DESIGN.md) |
|
||||
|
||||
本文定义入口、设置和轻量资源监控界面。文件、终端和浏览器的内容布局继续遵守助手工作栏
|
||||
文档。
|
||||
本文定义已接入的应用中心、共同设置和本机推理监控界面,以及待实现的右侧资源监控界面。
|
||||
行为以[功能逻辑](./logic-design.md)为准,需求保留 FR-1 至 FR-16 与既有 US 编号。
|
||||
根 UI 规范已同步应用中心与工具入口规则;共享控件、主题、设置 Modal 与工作栏布局遵守根规范。
|
||||
|
||||
## 1. 侧栏信息架构
|
||||
|
||||
@@ -24,110 +25,131 @@
|
||||
搜索
|
||||
|
||||
当前对话
|
||||
魔法笔记 3
|
||||
知识库
|
||||
智能心跳 1
|
||||
应用(按保存顺序;知识库始终显示,可选应用仅显示启用且常驻项)
|
||||
运行记录
|
||||
|
||||
最近对话
|
||||
...
|
||||
|
||||
────────────────────────
|
||||
[文件] [终端] [浏览器] [资源] [设置]
|
||||
[应用中心] [设置]
|
||||
```
|
||||
|
||||
“当前对话”和“运行记录”属于固定系统入口。三个内置应用位于二者之间。侧栏底部不再显示
|
||||
“本地工作区”和 `platform · arch`;排障信息移至关于或诊断页面。
|
||||
“当前对话”和“运行记录”属于固定系统入口,常驻应用位于二者之间。首期应用清单为魔法笔记、
|
||||
知识库、智能心跳及本轮追加的本机推理监控,应用中心与设置始终可见。侧栏底部不再显示“本地工作区”和 `platform · arch`;
|
||||
排障信息移至关于或诊断页面。左侧不再提供文件、终端、浏览器、资源监控的重复入口。
|
||||
|
||||
## 2. 底部系统工具栏
|
||||
## 2. 底部应用中心入口
|
||||
|
||||
底部使用连续平整表面和顶部分隔线,不做成账户卡片或嵌套卡片。按钮顺序固定:
|
||||
底部使用连续平整表面和顶部分隔线,应用中心在前,固定设置靠右,二者为独立按钮:
|
||||
|
||||
| 位置 | 图标 | 可见工具提示 | 点击结果 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | `FolderOpen` | 文件 | 聚焦不可关闭的工作区面板 |
|
||||
| 2 | `SquareTerminal` | 终端 | 打开或聚焦当前执行空间终端 |
|
||||
| 3 | `Globe` | 浏览器 | 聚焦当前 Conversation 的匹配浏览器实例 |
|
||||
| 4 | `Activity` | 资源监控 | 打开或聚焦本机资源监控面板 |
|
||||
| 末位 | `Settings` | 设置 | 打开设置页面 |
|
||||
| 1 | `Grid2X2` | 应用中心 | 向上展开已启用应用菜单,底部“管理应用”进入管理 Modal |
|
||||
| 末位 | `Settings` | 设置 | 在当前工作区上打开设置 Modal |
|
||||
|
||||
图标使用项目现有 Lucide 库。每个按钮采用统一的 `34px × 34px` 点击区域,内部图标建议
|
||||
`16px`。工具栏使用 `8px` 内边距和 `4px` 间隔;设置按钮通过自动空白靠右,不添加第二条
|
||||
视觉分隔线。
|
||||
图标使用项目现有 Lucide 库。应用中心保留文字,设置图标采用统一的 `34px × 34px` 点击区域。
|
||||
底部使用共享间距和焦点样式,设置通过弹性空白靠右;不得把整个容器做成设置按钮。
|
||||
|
||||
### 2.1 状态
|
||||
|
||||
- 默认:透明背景,图标使用次级文字色。
|
||||
- 悬停:使用弱表面色,不能强于当前选中状态。
|
||||
- 选中:使用强调选中背景,并提高图标对比度。
|
||||
- 展开:应用中心按钮显示展开反馈,不能当作当前应用的选中状态。
|
||||
- 键盘焦点:显示统一焦点环,不能被侧栏边缘裁切。
|
||||
- 处理中:创建或激活请求未完成时暂时禁用对应按钮,保留尺寸并提供进行中名称。
|
||||
- 徽标:只用于等待处理、错误或活动状态,不显示无意义的常驻绿色圆点。
|
||||
- 加载:读取应用配置时保留入口,中心内显示加载状态;失败提供重试,不用默认值冒充已保存配置。
|
||||
|
||||
选中状态表达“对应工作栏面板正在显示”,不表达功能已安装或权限已授予。
|
||||
应用中心入口不承载工具运行徽标。全部可选应用关闭时菜单仍显示知识库及“管理应用”,可进入 Modal 重新开启可选应用。
|
||||
|
||||
## 3. 主导航应用区
|
||||
|
||||
应用入口沿用现有 `nav-item` 行,不使用应用卡片。图标、名称和徽标保持单行:
|
||||
|
||||
```text
|
||||
[图标] 魔法笔记 3
|
||||
[图标] 知识库
|
||||
[图标] 智能心跳 1
|
||||
[图标] 魔法笔记 3
|
||||
[图标] 本机推理监控
|
||||
```
|
||||
|
||||
隐藏某个应用后,其余应用向上补位。运行记录始终位于最后一个可见应用之后,不因应用显隐
|
||||
改变样式或归类为工具。
|
||||
取消常驻或关闭可选应用后不留下空白。知识库始终启用并显示;心跳按启用和常驻显示,位置遵守共享应用顺序。运行记录始终位于应用区之后。
|
||||
四个应用均在管理行上通过拖动及“上移”“下移”按钮排序;首尾无目标时禁用对应按钮。两种方式提交完整应用顺序,保存成功后更新,重启保留。当前对话、运行记录和底部系统入口不参与排序。
|
||||
|
||||
设置页打开期间,主侧栏显示最后一次成功保存的导航状态。Switch 更新成功后导航立即变化,
|
||||
失败时保持原导航。
|
||||
取消常驻不关闭当前页面或本机推理监控 Modal。重新常驻按已保存顺序恢复,关闭应用保留常驻与顺序偏好。
|
||||
主侧栏始终读取最后确认配置,不展示尚未保存成功的排序或开关目标值。
|
||||
|
||||
## 4. 应用与工具设置
|
||||
## 4. 应用中心与共同设置
|
||||
|
||||
设置中心新增“应用与工具”分类,采用 `standard` PageShell:
|
||||
底部点击打开轻量菜单,锚定按钮向上展开,无模态遮罩、背景隔离或焦点循环。菜单按共享保存顺序列出已启用应用,不按打开历史或常驻状态过滤。点击应用行关闭菜单,本机推理监控打开独立 Modal,知识库、智能心跳和魔法笔记在主内容区打开页面。分隔线下的“管理应用”关闭菜单并打开既有管理 Modal。
|
||||
|
||||
菜单通过共享 `AnchoredMenu` 挂载到 body,宽度最多 `280px`,距窗口边缘至少 `16px`,与按钮上缘间隔 `8px`;短窗口限制高度并内部滚动,滚动、窗口和内容尺寸变化时重算位置。原生浏览器相交遮挡复用共享机制。
|
||||
|
||||
管理 Modal 沿用设置 Modal 的表面、遮罩和焦点规则,提供全部内置应用的名称及用途搜索、打开,以及可选应用的常驻和设置:
|
||||
|
||||
应用管理始终采用紧凑单列横向列表,从上到下对应保存顺序;行间使用 `--border-subtle` 弱分隔线,不加卡片外框。概览与设置详情共用 `640px` 最大宽度,窄窗口保留两侧各 `16px` 外边距,高度随内容增长并受窗口限制。
|
||||
每行左侧显示图标、名称、状态和简短用途,右侧对齐打开、可用的设置及上下移动操作;空间不足时操作组换到下一行并保持右对齐,组内也可换行,上下箭头保持成组。名称旁区分
|
||||
“始终显示”(英文 `Always shown`)和“可选 · 已启用/已关闭”。名称与用途可自然换行,不截断用途说明;正文与控件均不可横向溢出。
|
||||
|
||||
```text
|
||||
应用与工具
|
||||
管理主导航中的应用。系统工具由 GoodBuddy 提供并固定在侧栏底部。
|
||||
|
||||
应用
|
||||
魔法笔记 笔记、待办和评论 [显示在侧边栏 开]
|
||||
知识库 管理和检索知识 [显示在侧边栏 开]
|
||||
智能心跳 查看计划、建议和运行历史 [显示在侧边栏 开]
|
||||
|
||||
系统工具
|
||||
文件 打开当前工作区 内置
|
||||
终端 进入当前执行空间 内置
|
||||
浏览器 打开 GoodBuddy 受控浏览器 内置
|
||||
资源监控 查看本机 CPU、内存和磁盘 IO 内置
|
||||
|
||||
系统入口
|
||||
当前对话、运行记录和设置位置固定,不参与上述配置。
|
||||
应用中心
|
||||
[搜索内置应用]
|
||||
知识库 始终显示 [打开] [上移] [下移]
|
||||
智能心跳 可选 · 已关闭 [打开(禁用)] [设置] [上移] [下移]
|
||||
魔法笔记 可选 · 已启用 [打开] [设置] [上移] [下移]
|
||||
本机推理监控 可选 · 已启用 [打开] [设置] [上移] [下移]
|
||||
```
|
||||
|
||||
应用显隐使用共享 Switch,标签必须是“显示在侧边栏”,不能缩写为“启用”。切换时立即保存,
|
||||
请求期间禁用三个应用显隐 Switch;失败后恢复原值,并在对应设置行显示可重试错误。系统工具
|
||||
使用紧凑只读列表,不渲染禁用的卸载、停用或固定按钮。
|
||||
已启用但未常驻的应用可以直接打开,打开不自动常驻。关闭的应用仍能被搜索和配置,
|
||||
保留禁用的“打开”按钮,可通过“设置”重新开启。启用、常驻放在设置详情中;每行右侧提供成组的上下箭头,与打开、设置操作按空间换行。箭头使用共享图标按钮,名称和提示包含方向及应用名;目标取完整保存顺序中的相邻应用,仅首尾无目标或修改锁定时禁用对应箭头。无搜索词且未锁定修改时,所有行均支持拖动排序,包括关闭或未常驻的应用;搜索时禁用拖动,箭头仍按完整保存顺序操作,未命中的应用保留在顺序中。无搜索结果显示清除搜索入口。
|
||||
首期不展示商店页签、推荐、安装按钮、市场 URL 或 YAML 操作,系统工具也不在此列出。
|
||||
|
||||
功能已停用时,对应应用行显示“功能已停用”和设置入口,保留显隐偏好但暂时禁用 Switch。
|
||||
### 4.1 共同应用设置页
|
||||
|
||||
若某应用自身支持业务启停,应在该应用原有设置中保留“启用功能”开关,并用说明区分:
|
||||
智能心跳、魔法笔记、本机推理监控的通用应用设置统一从应用中心对应行的“设置”进入;应用页面不再提供重复快捷入口。本机推理监控保留“模型与连接设置”。知识库在管理 Modal 中提供“打开”和排序,不提供启用、常驻开关或通用空设置;原工作区实际配置入口保留。
|
||||
共同页遵守[设置中心 UI 规范](../../../UI-DESIGN.md#138-设置中心),不为每个入口建立一套设置。
|
||||
全局设置移除魔法笔记页签及跳转入口。旧“显示魔法笔记入口”绑定业务启用值,迁为“启用应用”并保留原值;
|
||||
“常驻左侧菜单”使用独立配置,只有已发布的独立显隐偏好才映射到常驻。
|
||||
|
||||
- “显示在侧边栏”只控制入口。
|
||||
- “启用功能”控制该功能是否运行。
|
||||
| 控件 | 表达含义 |
|
||||
| --- | --- |
|
||||
| 启用应用 | 控制业务可用性及主导航、会话等业务入口 |
|
||||
| 常驻左侧菜单 | 仅控制主导航捷径,关闭应用时保留该偏好,并说明开启后才显示 |
|
||||
| 显示未完成待办数量 | 仅控制魔法笔记导航徽标 |
|
||||
| AI 评论方式 | 即时/保存后自动/保存后手动,单选 |
|
||||
| AI 评论形式 | 长评 + 要点/长评/要点,单选 |
|
||||
|
||||
二元配置使用共享 Switch 和 `role="switch"`;评论选项使用共享单选控件。本机推理监控
|
||||
默认启用但不常驻;心跳应用默认关闭,常驻偏好默认开启,其余未配置布尔项默认开启。评论默认值沿用既有值,已有保存的选择保留。启用与常驻不可合并。
|
||||
设置详情最大宽度 `640px`,紧凑标题栏包含返回图标、应用名称、用途及关闭按钮。进入详情和
|
||||
返回列表时正文滚动位置重置到顶部;进入详情聚焦关闭按钮,返回后聚焦搜索框。
|
||||
共同设置表单使用共享 Switch,相关字段分组,组间使用弱分隔线;说明沿用正文字号。
|
||||
评论分段控件按内容占宽,窄窗口中标签与选项上下排列;详情不提供排序控件。
|
||||
智能心跳单条计划 CRUD 及 enabled 仍由其业务页面负责,应用级开关及执行条件见[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)。
|
||||
|
||||
所有入口共享保存中状态。串行保存期间禁用相关启用、常驻、排序及笔记配置控件,保留
|
||||
最后确认值;持久化成功后的设置事件同步中心、侧栏、会话、应用页及其他设置入口,包括工具配置写入。重开中心刷新期间锁定修改,迟到的旧读取不能覆盖更新快照。失败在发起操作
|
||||
附近显示可重试错误并保留焦点;结果未知时显示“正在确认保存结果”,重读成功后再开放修改。
|
||||
|
||||
### 4.2 关闭后的页面与会话
|
||||
|
||||
关闭当前可选应用成功后,原页面或本机推理监控 Modal 显示“应用已关闭”,重新启用通过应用中心对应行的设置完成;本机推理监控需先关闭 Modal。保留草稿和内容状态,
|
||||
暂停该页面的新业务操作。重新开启后恢复可用性,不自动跳到已离开的应用页或重放任务。
|
||||
|
||||
知识库不显示应用关闭状态;会话知识控件、历史引用和工作区配置遵守原功能规则。心跳关闭时的侧栏行为见[监督者 UI 设计](../conversation-supervision/ui-design.md#会话侧栏)。可选应用的已有请求和后台任务继续按功能生命周期展示,详细边界见[关闭应用与恢复](./logic-design.md#32-关闭应用与恢复)。
|
||||
|
||||
## 5. 助手工作栏反馈
|
||||
|
||||
点击系统工具后,右侧工作栏按现有布局回流打开。主内容页面保持不变,侧栏不渲染工具内容。
|
||||
系统工具仅从右侧工作栏已有 Tab 或“+”目录进入,工作栏按现有布局回流,主内容页面保持不变。
|
||||
|
||||
- 文件:激活默认存在且不可关闭的“工作区”Tab,焦点进入目录区域或错误修复入口。
|
||||
- 终端:激活匹配终端,焦点进入终端输入区;新建期间保留按钮宽度和进度状态。
|
||||
- 浏览器:激活绑定当前 Conversation 的最近使用“浏览器”Tab;没有匹配实例时创建一个,
|
||||
没有 Conversation 时按产品规则复用最近实例或进入待绑定空状态。
|
||||
- 终端:选择已有 Tab 激活该确切终端并保留目标与历史状态;“+”每次创建额外终端,按当前
|
||||
项目选择本机或托管 SSH,无项目时使用本机 Home。新建期间显示进度,失败保留诊断。
|
||||
- 浏览器:选择已有 Tab 保留其 Conversation、页面及导航状态;“+”每次创建额外浏览器,
|
||||
无活动 Conversation 时新建待绑定空实例。不会按当前会话改选其他 Tab 或重绑已有实例。
|
||||
- 资源监控:激活“资源监控”Tab,焦点进入指标摘要;首个样本加载后开始绘制历史。
|
||||
|
||||
后台事件可以更新徽标,不能自动展开工作栏或切换当前 Tab。由用户点击快捷按钮触发的打开
|
||||
属于显式操作,可以展开工作栏并切换 Tab。
|
||||
文件和资源监控在“+”中按单实例规则聚焦或创建,终端和浏览器不按目标去重。
|
||||
后台事件可以更新 Tab 徽标,不能自动展开工作栏或切换当前 Tab。
|
||||
|
||||
### 5.1 资源监控面板
|
||||
|
||||
@@ -167,7 +189,9 @@ CPU 4% · 内存 620 MB · 桌面进程 7
|
||||
|
||||
| 对象 | 文案 |
|
||||
| --- | --- |
|
||||
| 设置分类 | 应用与工具 |
|
||||
| 底部入口 | 应用中心 |
|
||||
| 菜单底部管理入口 | 管理应用 |
|
||||
| 应用设置开关 | 启用应用 |
|
||||
| 文件系统工具 | 文件 |
|
||||
| 终端系统工具 | 终端 |
|
||||
| 浏览器系统工具 | 浏览器 |
|
||||
@@ -175,7 +199,8 @@ CPU 4% · 内存 620 MB · 桌面进程 7
|
||||
| 资源系统工具 | 资源监控 |
|
||||
| 资源范围 | 本机 |
|
||||
| 指标缺失 | 不可用 |
|
||||
| 应用显隐开关 | 显示在侧边栏 |
|
||||
| 应用常驻开关 | 常驻左侧菜单 |
|
||||
| 关闭的应用页 | 应用已关闭 |
|
||||
| 内置来源 | 内置 |
|
||||
|
||||
不再使用“本地工作区”作为侧栏底部身份名称。项目和默认工作区的路径继续由项目选择器、页面
|
||||
@@ -183,33 +208,81 @@ CPU 4% · 内存 620 MB · 桌面进程 7
|
||||
|
||||
## 7. 键盘和无障碍
|
||||
|
||||
- 底部按钮均为独立 `button`,不能嵌套在整行设置按钮中。
|
||||
- Tab 顺序为文件、终端、浏览器、资源监控、设置。
|
||||
- 底部应用中心和设置均为独立 `button`,Tab 顺序与视觉顺序一致,不嵌套按钮。
|
||||
- 图标按钮通过 `aria-label` 提供名称,工具提示不能作为唯一可访问名称。
|
||||
- 工作栏打开后按现有焦点策略聚焦目标面板;用户通过固定工作栏开关关闭时,焦点返回该开关。
|
||||
- 选中按钮使用 `aria-pressed` 或等价的可访问状态,不能只依靠背景色。
|
||||
- 徽标的数量或状态合并进按钮可访问名称,例如“终端,1 个等待处理”。
|
||||
- 应用显隐 Switch 支持 Space 切换,保存失败后恢复原值并保留焦点。
|
||||
- 应用中心按钮提供 `aria-haspopup="menu"`、`aria-expanded` 和控制关系;菜单使用 `menu`/`menuitem`,打开后聚焦首个可用项,上下方向键循环选择,Home、End 到首尾,Enter、Space 激活。
|
||||
- Escape 关闭菜单并恢复入口焦点;Tab 关闭后继续正常焦点顺序,点击外部、焦点离开或再次点击入口也关闭。菜单不隔离背景,不增加遮罩。
|
||||
- “管理应用”打开的 Modal 使用共享 Portal、整窗遮罩、原生浏览器遮挡、背景隔离和焦点循环,初始焦点进入搜索。行操作与详情控件按正常 Tab 顺序访问,保留共享可见焦点环,不伪装成单个菜单项;详情切换焦点见共同应用设置页。
|
||||
- 工作栏使用 `tablist`、`tab`、`tabpanel`、`aria-selected`、游标焦点和方向键、Home、End。
|
||||
“+”使用普通按钮语义,同类型 Tab 仍按实例独立标记。
|
||||
- 徽标数量或状态合并进可访问名称;Switch 支持 Space,失败保留焦点。排序按钮名称包含
|
||||
应用名,操作结果通过 polite 状态区播报;拖动不是唯一排序方式。
|
||||
|
||||
## 8. 响应式
|
||||
|
||||
- 侧栏宽度 `220px` 至 `420px` 时,底部按钮尺寸不随宽度缩放。
|
||||
- 窄窗口继续使用现有侧栏抽屉;工具栏不转为多行。
|
||||
- 空间不足时缩小设置按钮前的弹性空白,不隐藏四个系统工具和设置。
|
||||
- 侧栏宽度 `220px` 至 `420px` 时,应用中心与设置均可操作,不随宽度缩小点击区域。
|
||||
- 窄窗口继续使用现有侧栏抽屉,底部入口不重叠;应用菜单向上展开并限制窗口边界、内部滚动。管理 Modal 的长名称可换行,搜索、设置与重新开启入口始终可达。
|
||||
- 右侧 Tab 单行横向滚动,“+”持续可见;工作栏保持停靠回流,不遮罩主页面。
|
||||
- 工具提示不得超出窗口;靠近底边时显示在按钮上方。
|
||||
- 粗指针设备不依赖悬停,按钮的可访问名称和选中反馈保持有效。
|
||||
|
||||
### 会话置顶与操作菜单
|
||||
|
||||
会话行的“更多会话操作”提供“置顶会话/取消置顶”,本地与消息通道会话均可使用。
|
||||
置顶会话显示带“已置顶”可访问名称的图标;当前项目和搜索过滤仍生效,过滤后的列表先显示
|
||||
置顶会话,两组内各按更新时间倒序。取消置顶恢复时间排序,不改变会话更新时间。
|
||||
置顶状态重启后保留;保存成功后更新图标和顺序,失败保留原状态并显示应用错误通知。
|
||||
存储尚未就绪或置顶请求进行中时禁用置顶操作。
|
||||
|
||||
操作菜单通过 body Portal 浮在会话列表上方,不撑开会话行。菜单锚定更多按钮,默认位于
|
||||
按钮下方,随滚动、窗口及菜单尺寸变化重新定位,距窗口边缘至少 `8px`;空间不足时限制
|
||||
宽高并允许菜单内部滚动。使用共享表面色、边框、圆角与浮层阴影。
|
||||
|
||||
入口提供 `aria-haspopup="menu"`、展开状态和控制关系;菜单使用 `menu`/`menuitem`。
|
||||
打开后聚焦首个可用按钮,上下方向键循环移动,Home/End 到首尾;Escape 关闭并返回
|
||||
触发按钮,点击外部或焦点离开时关闭。本地会话继续提供分支、重命名和删除确认,复制与
|
||||
导出也适用于消息通道会话。持久化与刷新规则见
|
||||
[会话置顶存储](../assistant-workbar/execution-history-storage.md#会话置顶)。
|
||||
|
||||
## 9. 未来插件入口
|
||||
|
||||
插件市场交付后另行定义插件区域、固定数量、溢出和权限界面。系统入口与四个系统工具的位置
|
||||
保持保留,不向插件开放。
|
||||
FR-15、US-F1/US-F2 的市场 URL、YAML 导入导出及 ShareServer 分发在后续设计中定义。
|
||||
首期中心只浏览本地内置清单,不显示未来市场的空壳界面。
|
||||
|
||||
### 9.1 本机推理管理
|
||||
|
||||
FR-16 的 `local-inference` 入口已纳入本轮应用清单,可常驻主导航。从常驻入口、应用菜单和管理 Modal 的“打开”均进入同一本机推理监控 Modal,关闭来源菜单或管理 Modal,保留底层工作区、项目、会话和草稿。其他内置应用仍打开主内容区页面。
|
||||
Modal 与真实执行模块已接入;支持清单及验证范围分别见技术设计和实施进度。
|
||||
复用 `custom-task-dialog` 的整窗 body Portal、遮罩、背景隔离及浏览器遮挡机制。主 Modal 最大宽度 `960px`,高度随内容增长,最大高度为 `min(720px, 100dvh - 2 × --space-6)`;紧凑标题栏和关闭按钮固定在顶部,正文独立滚动。直接显示服务列表,没有页签或任务历史。工具栏显示“每 5 秒刷新 · CPU 100% = 一个逻辑核心”、模型与连接设置、刷新状态,窄屏可换行。服务行显示“服务进程 PID … · CPU …% · 工作集 … MiB”,预热时显示“正在建立采样基线”;共享进程的 worker 在该行显示无法独立统计的原因。
|
||||
初始焦点进入“关闭本机推理监控”,Tab/Shift+Tab 在当前 Modal 内循环。没有影响确认且没有操作提交时,关闭按钮或 Escape 关闭 Modal;点击遮罩不关闭。关闭后优先恢复仍可用的原触发元素,入口已移除时回到应用中心,窄侧栏已隐藏时回到侧栏开关。
|
||||
服务采用弱分隔列表;名称使用区块标题字号,状态及引擎元数据使用
|
||||
辅助字号,状态与名称同组并与操作区分开,原因与操作说明使用正文字号。桌面操作独立位于右侧;
|
||||
窗口宽度不超过 `720px` 时移到正文下方并左对齐,保留完整状态和原因。
|
||||
|
||||
- US-F3 每行显示服务名、简短状态、引擎、模型及错误。ASR 可用时显示“按需运行(状态未知)”;OCR 和向量使用执行模块的状态。TTS 显示不可用,外部连接不列入本机服务。
|
||||
- US-F4 不再提供单任务取消入口;活动任务仅在服务操作的影响确认中出现。
|
||||
- US-F5 服务行右侧保留实际支持的启动、停止、重启或模型加载、释放。
|
||||
- 停止、重启、释放前打开最大宽度 `480px` 的紧凑嵌套确认,列出正在使用的任务与影响;无已登记任务时说明执行前再次核对。初始焦点进入“保留运行状态”,最终危险按钮写明“确认停止服务/重启服务/释放模型”;取消保留运行状态。
|
||||
确认使用独立 body Portal,隔离底层管理 Modal,Tab/Shift+Tab 只在确认中循环;Escape 只关闭确认,关闭后恢复原服务操作按钮,按钮不可用时回到管理 Modal。确认存在时禁用主 Modal 关闭按钮;提交期间禁用确认和取消按钮,两层 Escape 均不能关闭,也不能重复执行。失败关闭旧确认,在服务列表显示错误并刷新;再次操作须重新确认当前影响。成功只关闭确认。
|
||||
操作失败呈现实际状态和可重试错误,无法确认则显示未知。服务恢复不自动重放任务。
|
||||
关闭 Modal、关闭管理应用或取消常驻不停止服务,模型文件卸载另行操作。
|
||||
|
||||
服务列表提供首次加载、无服务、加载失败重试及保留旧快照的刷新失败状态;刷新期间或失败时禁用服务操作。
|
||||
OCR 忙碌或模型不可用时禁用加载与释放,并在执行模块再次检查。
|
||||
文档解析与 HTTP OCR 的配置归属见[文档处理入口设计](../document-processing/ui-design.md#11-入口与职责),
|
||||
不因接入 HTTP 而扩展本机服务列表。“模型与连接设置”现直达 `model`,OCR 服务行的
|
||||
“文档解析设置”直达 `document-parsing`,关闭设置返回原工作区;实施与验证见
|
||||
[文档处理进度](../document-processing/progress.md#2026-09-19-实现与验证)。
|
||||
页面不显示无来源的进度条或资源数值。US-B6 至 US-B9 的应用中心、顺序、启用联动、笔记共同设置映射见
|
||||
[需求对应](./logic-design.md#6-与需求的对应关系)。
|
||||
|
||||
## 10. 项目活动汇总
|
||||
|
||||
对应 [FR-14](./prd.md#fr-14-项目活动汇总),状态与计数遵守
|
||||
[活动逻辑](./logic-design.md#9-项目活动汇总)。
|
||||
|
||||
- 全局汇总按钮仅在有活动时显示在项目选择器下方,紧凑显示待处理与运行中数量,空闲时整行隐藏,
|
||||
- 全局汇总按钮仅在有活动行时显示在项目选择器下方,紧凑显示待处理、运行中与已完成数量,无活动行时整行隐藏,
|
||||
右侧显示展开箭头,与下方新建会话保留 `8px` 间距。可访问名称明确数量属于“全项目活动”。
|
||||
- 项目菜单在项目行显示同口径数量,保留“本地项目 / 远程项目 / 远程通道”分组和 SSH Host
|
||||
层级,不因活动状态重排项目。数量为零时不显示标记,连接状态仍独立显示。
|
||||
@@ -219,10 +292,11 @@ CPU 4% · 内存 620 MB · 桌面进程 7
|
||||
相接,各自拥有边框、圆角与阴影;会话层对齐所选项目行,底部空间不足时仅上移会话层,
|
||||
不改变项目层的位置或高度。指针离开项目行进入会话层时不收起。展开项目不切换当前会话。
|
||||
- 会话层顶部显示项目名,每行显示完整可换行的会话标题与明确状态文字,待处理会话排在
|
||||
运行中会话之前。项目行保留子菜单箭头,会话行不显示箭头。不展开 Task、Job 或 Run 列表。
|
||||
运行中会话之前,已完成未查看会话排在最后并显示“已完成”。项目行保留子菜单箭头,会话行不显示箭头。不展开 Task、Job 或 Run 列表。
|
||||
- 点击会话先关闭菜单并恢复入口焦点,再请求进入该条会话;允许离开当前页面后,
|
||||
选择所属项目、清除搜索和该会话未读标记、清除任务选择与会话操作菜单,窄窗口同时关闭侧栏。
|
||||
设置页沿用未保存修改的离开检查,取消离开时保留原页面。
|
||||
设置页沿用未保存修改的离开检查,取消离开时保留原页面和完成通知。完成通知在目标聊天
|
||||
实际可见后清除,从普通侧栏进入也相同;设置覆盖下的聊天或隐藏文档不触发清除。
|
||||
- 菜单挂载到 body,使用 `menu`、`menuitem`、`aria-haspopup`、`aria-expanded` 与控制关系,
|
||||
不隔离背景、不增加遮罩。原生浏览器相交遮挡沿用共享机制。
|
||||
- 打开后焦点进入首个项目;上下方向键循环选择当前层项目或会话,Home、End 到首尾,
|
||||
@@ -231,4 +305,4 @@ CPU 4% · 内存 620 MB · 桌面进程 7
|
||||
顺序;点击外部、焦点离开、再次点击入口或隐藏侧栏也关闭菜单。
|
||||
- 单面板宽度为 `264px`,窗口小于 `620px` 或项目菜单两侧均无法容纳子菜单时,就地进入会话层并显示“返回项目列表”,
|
||||
触控无需悬停。浮层距窗口边缘至少 `16px`,每层最大高度为 `480px`,短窗口进一步限制
|
||||
高度并独立滚动。活动全部结束时,关闭已打开菜单并隐藏摘要。
|
||||
高度并独立滚动。成功完成但尚未查看的会话继续保留;所有活动行移除后,关闭已打开菜单并隐藏摘要。
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 状态 | 设计中 |
|
||||
| 版本 | 0.2 |
|
||||
| 日期 | 2026-09-08 |
|
||||
| 状态 | 应用中心与本机推理监控已接入源码,整体验收待完成 |
|
||||
| 版本 | 0.4 |
|
||||
| 日期 | 2026-09-17 |
|
||||
| 关联 PRD | [应用导航与系统工具入口 PRD](./prd.md) |
|
||||
| 关联逻辑 | [功能逻辑设计](./logic-design.md) |
|
||||
|
||||
@@ -29,47 +29,50 @@
|
||||
|
||||
验收:
|
||||
|
||||
- Given 用户隐藏任意内置应用
|
||||
- Given 用户隐藏任意可选内置应用
|
||||
- When 用户查看主导航
|
||||
- Then “运行记录”仍显示在应用区之后
|
||||
- And 设置中没有隐藏、卸载或固定运行记录的选项
|
||||
|
||||
### US-A2 从固定入口进入设置
|
||||
|
||||
作为用户,我希望设置入口始终位于侧栏底部,以便快捷工具状态变化时仍能进入配置。
|
||||
作为用户,我希望设置入口始终位于侧栏底部,以便应用状态变化时仍能进入配置。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 任意主页面和任意侧栏宽度
|
||||
- When 用户查看侧栏底部
|
||||
- Then 设置按钮保持可见且点击区域不与工具按钮重叠
|
||||
- Then 设置与应用中心为始终可见的独立按钮,点击区域不重叠
|
||||
- And 点击后继续遵守未保存内容的离开确认
|
||||
|
||||
## 3. Epic B:管理应用入口
|
||||
|
||||
本节的启用、常驻和共同设置适用于智能心跳、魔法笔记、本机推理监控;四个应用均可排序,知识库始终启用并常驻。心跳的默认关闭及执行边界按[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)验收,保留既有故事 ID。
|
||||
|
||||
### US-B1 识别完整应用
|
||||
|
||||
作为用户,我希望魔法笔记、知识库和智能心跳显示在主导航,以便知道它们会打开完整页面。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 对应功能已启用且应用设置为显示
|
||||
- Given 始终显示的应用,或已启用且常驻的可选应用
|
||||
- When 用户打开主侧栏
|
||||
- Then 应用位于“当前对话”和“运行记录”之间
|
||||
- And 点击应用在主内容区打开对应一级页面
|
||||
- And 原有待办或建议徽标继续显示
|
||||
- And 点击知识库、智能心跳或魔法笔记在主内容区打开对应一级页面;本机推理监控打开保留底层工作区的 Modal,验收见 US-F3
|
||||
- And 原有待办或建议徽标按已保存的显示设置继续显示
|
||||
- And 当前对话之后按保存顺序显示应用;知识库始终显示,可调整位置,无启用或常驻开关;心跳按启用及常驻过滤
|
||||
|
||||
### US-B2 隐藏低频应用
|
||||
### US-B2 取消低频应用常驻
|
||||
|
||||
作为用户,我希望隐藏不常用的内置应用,以便缩短主导航,但保留已有数据。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 应用已有内容
|
||||
- When 用户关闭该应用的“显示在侧边栏”Switch
|
||||
- Then 主导航不再显示该应用
|
||||
- Given 应用已启用且已有内容
|
||||
- When 用户关闭该应用的“常驻左侧菜单”Switch 并保存成功
|
||||
- Then 主导航不再显示该应用,当前已打开页面保持不变
|
||||
- And 应用数据、计划和业务启停状态不变
|
||||
- And 用户可从同一设置位置恢复显示
|
||||
- And 用户可从应用中心打开该应用,或从同一设置位置恢复常驻
|
||||
|
||||
### US-B4 保留功能启停语义
|
||||
|
||||
@@ -78,35 +81,133 @@
|
||||
验收:
|
||||
|
||||
- Given 魔法笔记功能已启用
|
||||
- When 用户只隐藏其侧栏入口
|
||||
- When 用户只取消其侧栏常驻并保存成功
|
||||
- Then 笔记及待办处理能力继续按原设置工作
|
||||
- And 再次显示时恢复原页面和数据
|
||||
- And 重新常驻只恢复入口,不自动导航;再次打开时保留原页面状态和数据
|
||||
|
||||
### US-B5 恢复显隐保存失败
|
||||
### US-B5 保持设置保存一致
|
||||
|
||||
作为用户,我希望入口设置保存失败时继续使用原导航,以免界面状态与实际设置不一致。
|
||||
作为用户,我希望应用设置保存期间和失败后各入口保持一致,以免界面与实际配置不符。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 应用显隐更新尚未完成
|
||||
- When 用户切换“显示在侧边栏”Switch
|
||||
- Then 三个应用显隐 Switch 在请求期间暂时不可操作
|
||||
- And 更新成功后主导航立即变化
|
||||
- And 更新失败时 Switch 和主导航恢复原状态并显示可重试错误
|
||||
- Given 配置已加载且没有待确认的更新
|
||||
- When 用户修改启用、常驻、顺序或魔法笔记配置
|
||||
- Then 更新串行保存,在请求确认前,应用中心、应用页及全局设置中修改这组配置的所有控件均暂不可操作
|
||||
- And 各处保留最后确认值,不提前改变导航、业务可用性、徽标或会话入口
|
||||
- When 保存成功
|
||||
- Then 各入口同时采用确认配置,后续修改基于该配置提交
|
||||
- And 工具配置写入持久化成功后也同步各入口;较新的确认快照不被迟到的读取或保存响应覆盖
|
||||
- When 关闭后重新打开应用中心
|
||||
- Then 刷新最新配置期间锁定修改,读取失败保留重试入口,不允许用旧快照提交
|
||||
- When 保存明确失败
|
||||
- Then 各入口仍使用相同的最后确认配置,操作处显示可重试错误,保留焦点和编辑上下文,不重复通知
|
||||
- Given 响应中断,无法确认保存结果
|
||||
- When 用户查看设置
|
||||
- Then 显示正在确认保存结果,重读权威配置成功后才恢复修改;读取失败可重试读取,不自动重发变更或写回旧值
|
||||
|
||||
### US-B6 从应用中心访问内置应用
|
||||
|
||||
作为用户,我希望从固定应用中心找到应用,以便未常驻或已关闭的应用仍有访问和恢复入口。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 用户位于任意一级页面,包括全部可选应用均已关闭的情况
|
||||
- When 点击侧栏底部应用中心
|
||||
- Then 向上打开轻量锚定菜单,无模态遮罩;按保存顺序列出知识库和已启用的可选应用,不按打开历史或常驻筛选
|
||||
- When 点击菜单中的应用行
|
||||
- Then 菜单关闭;知识库、智能心跳和魔法笔记经既有离开检查在主内容区打开,本机推理监控打开保留底层工作区的 Modal,不自动常驻
|
||||
- When 点击菜单底部“管理应用”
|
||||
- Then 菜单关闭并打开既有管理 Modal,可按名称或用途搜索全部内置应用,包含未常驻及已关闭的可选应用
|
||||
- And 知识库行提供“打开”和排序,没有通用设置、启用或常驻开关;心跳行可管理启用和常驻,单条计划 enabled 不变
|
||||
- And 首期没有商店、推荐、安装、市场 URL、YAML 操作或系统工具入口
|
||||
- Given 应用已启用但未常驻
|
||||
- When 用户选择打开
|
||||
- Then 本机推理监控打开 Modal,底层内容无需离开;其余应用经既有未保存内容离开检查进入页面,不自动常驻
|
||||
- Given 应用已关闭
|
||||
- When 用户在管理 Modal 中找到该应用
|
||||
- Then 显示关闭状态,保留禁用的“打开”按钮,可通过“设置”进入共同设置重新开启
|
||||
- Given 搜索无匹配结果
|
||||
- Then 显示无结果及清除搜索入口,搜索不修改启用和常驻
|
||||
- Given 配置正在加载或读取失败
|
||||
- Then 中心保留加载状态或重试入口,不用默认值冒充已保存配置
|
||||
|
||||
### US-B7 保存常驻及应用顺序
|
||||
|
||||
作为用户,我希望常驻和顺序按设备保存,以便重启后仍使用熟悉的导航。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 首次使用且未配置导航偏好
|
||||
- Then 默认顺序为知识库、智能心跳、魔法笔记、本机推理监控;心跳默认关闭且保留默认常驻偏好,魔法笔记默认常驻,本机推理监控默认启用但不常驻,仍可从应用菜单打开;侧栏只按启用与常驻过滤可选项
|
||||
- When 用户修改常驻,或通过拖动、键盘可操作的上移和下移按钮排序并保存成功
|
||||
- Then 管理列表从上到下、菜单和侧栏按同一保存顺序显示,知识库和智能心跳可移动;当前对话、运行记录及底部系统入口位置不变,重启后恢复保存结果
|
||||
- And 每行均提供上下移动,以完整保存顺序中的相邻应用为目标;未常驻及关闭应用仍可排序,设置详情不提供排序控件
|
||||
- And 搜索时箭头仍按完整顺序移动,保留未命中应用,拖动禁用;清除搜索后显示完整保存结果
|
||||
- Given 旧设置仅保存本机推理监控、魔法笔记的顺序
|
||||
- When 升级后读取设置
|
||||
- Then 知识库、智能心跳补在前面,两个可选应用保留原有相对顺序和常驻值
|
||||
- Given 应用已关闭或取消常驻
|
||||
- When 用户重新开启或常驻
|
||||
- Then 按保存顺序恢复入口;关闭或取消常驻不删除顺序,已关闭应用仍可管理常驻偏好
|
||||
- Given 保存失败
|
||||
- Then 按 US-B5 保留原常驻和顺序,在操作处提供重试并保留键盘焦点
|
||||
- Given 没有可见的可选应用或仅有一个
|
||||
- Then 固定入口和应用中心仍可用,没有移动目标的排序按钮禁用,不产生空白占位项
|
||||
|
||||
### US-B8 独立控制启用与常驻
|
||||
|
||||
作为用户,我希望关闭应用时保留数据和导航偏好,并能明确知道哪些业务操作已不可用。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 应用已启用
|
||||
- When 仅取消常驻并保存成功
|
||||
- Then 业务能力和后台操作保持原状,仍可从应用中心打开
|
||||
- Given 知识库已有会话关联,智能心跳已有启用或暂停的计划
|
||||
- When 用户关闭全部可选应用或修改其常驻与顺序
|
||||
- Then 知识库始终启用,侧栏位置稳定,页面可打开;知识控件及关联保持可用,请求遵守知识库自身规则,心跳单条计划 enabled 原值不变
|
||||
- And 心跳应用关闭后显示不可用状态,可从应用中心设置重新开启;新运行、在途运行及侧栏查询按[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)验收
|
||||
- Given 关闭的应用页面或本机推理监控 Modal 仍打开
|
||||
- Then 原位置显示“应用已关闭”,保留草稿及页面状态,阻止新业务操作而不强制跳转;用户从应用中心对应行的设置重新启用,监控 Modal 需先关闭
|
||||
- When 用户重新开启并保存成功
|
||||
- Then 仍停留的页面恢复可用;已离开时不自动导航,不重放或补发旧请求和任务
|
||||
- And 保留原常驻及顺序,未常驻应用不自动常驻
|
||||
- Given 启用变更保存失败
|
||||
- Then 按 US-B5 保留原业务状态和入口,不执行关闭或恢复副作用
|
||||
|
||||
### US-B9 迁移魔法笔记共同设置
|
||||
|
||||
作为魔法笔记用户,我希望在应用中心统一管理笔记设置,并在升级后保留已有选择。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 用户从应用中心的魔法笔记行进入设置
|
||||
- Then 显示笔记设置表单;笔记页面不提供重复入口,也不保留“显示魔法笔记入口”开关
|
||||
- And 全局设置不再显示魔法笔记页签或跳转入口,已有配置值和业务数据保持不变
|
||||
- And 启用、常驻、显示未完成待办数量分别控制业务、导航和徽标;隐藏数量不修改待办
|
||||
- Given 升级前已有明确保存的布尔值和评论选择
|
||||
- When 迁移配置并重启
|
||||
- Then 保留已有 `true`、`false`、评论单选值及业务数据;缺失导航配置时,本机推理监控默认不常驻,魔法笔记默认常驻;未配置的应用启用和笔记相关布尔项默认 `true`
|
||||
- And 旧入口开关若实际控制笔记启用,保留其启用含义和原值,不将其改作常驻
|
||||
- Given AI 评论方式或评论形式未配置
|
||||
- Then 分别默认“即时”和“长评 + 要点”,均为单选,不同时启用所有模式
|
||||
- When 任一入口修改配置并保存成功
|
||||
- Then 应用中心、主导航、笔记页、会话及其他设置入口同步采用确认值;保存中和失败均遵守 US-B5
|
||||
|
||||
## 4. Epic C:使用系统工具
|
||||
|
||||
### US-C1 一键查看文件
|
||||
|
||||
作为本地工作用户,我希望点击左下角文件按钮后直接查看当前工作区文件。
|
||||
作为本地工作用户,我希望从右侧工作栏查看文件,并保留工作区面板的目标模式。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 当前项目有有效根目录
|
||||
- When 用户点击文件按钮
|
||||
- Then 助手工作栏展开
|
||||
- And 聚焦默认存在且不可关闭的工作区单实例
|
||||
- And 文件按钮通过选中状态表明该面板正在显示
|
||||
- When 用户展开右侧工作栏并选择已有工作区 Tab,或从“+”选择文件
|
||||
- Then 聚焦默认存在且不可关闭的工作区单实例,不创建重复实例;仅迁移或布局修复时补回缺失实例
|
||||
- And 保留跟随或固定模式,跟随模式继续按工作栏规则更新上下文
|
||||
- And 仅该实例 Tab 显示选中状态,焦点进入目录区域
|
||||
|
||||
### US-C2 处理无效工作区
|
||||
|
||||
@@ -116,67 +217,62 @@
|
||||
|
||||
- Given 工作区面板处于跟随模式
|
||||
- And 当前项目和默认工作区均不可用
|
||||
- When 用户点击文件按钮
|
||||
- When 用户在右侧工作栏选择工作区 Tab 或从“+”选择文件
|
||||
- Then 工作区面板仍打开
|
||||
- And 面板说明目录不可用并提供选择或修复目录的入口
|
||||
- And 应用不静默改用其他目录
|
||||
- Given 工作区面板固定的目标已失效,即使当前项目或默认目录可用
|
||||
- When 用户选择工作区 Tab
|
||||
- Then 保留固定目标并显示该目标的修复状态,不回退到其他目录或切换为跟随模式
|
||||
|
||||
### US-C3 快速进入终端
|
||||
|
||||
作为本地工作用户,我希望点击终端按钮后进入当前执行空间的终端。
|
||||
作为本地工作用户,我希望选择确切终端继续查看,并能通过“+”创建额外终端。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 已有终端 Tab,包括绑定其他执行空间的终端
|
||||
- When 用户选择其中一个 Tab
|
||||
- Then 仅激活该确切实例,保留执行空间、目录及历史,不按当前项目查找最近匹配实例或重新绑定
|
||||
- Given 已选 Tab 正在关闭、已退出、已中断或失败
|
||||
- Then 保留其真实状态,不因选中而重建
|
||||
- Given 当前执行空间已有正在启动或运行中的终端
|
||||
- When 用户点击终端按钮
|
||||
- Then 工作栏聚焦最近使用的匹配终端,不创建重复实例
|
||||
- Given 当前执行空间没有终端
|
||||
- When 用户点击终端按钮
|
||||
- Then 创建一个绑定该执行空间的新终端
|
||||
- When 用户从右侧工作栏“+”选择终端
|
||||
- Then 每次创建绑定当前执行空间的额外实例,不复用或按目标去重
|
||||
- Given 当前没有项目
|
||||
- When 用户点击终端按钮
|
||||
- Then 创建或聚焦使用本机 Home 的终端
|
||||
- Given 当前执行空间只有已退出、已中断或失败的终端
|
||||
- When 用户点击终端按钮
|
||||
- Then 创建新终端并保留原终端的历史状态
|
||||
- When 用户从“+”选择终端
|
||||
- Then 创建使用本机 Home 的新终端,已有终端保持原目标和历史
|
||||
|
||||
### US-C4 使用远程项目终端
|
||||
|
||||
作为远程项目用户,我希望同一个终端按钮进入远程项目目录,以免误在本机执行命令。
|
||||
作为远程项目用户,我希望新建终端绑定远程项目目录,以免误在本机执行命令。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 当前项目绑定托管 SSH 执行空间
|
||||
- When 用户点击终端按钮
|
||||
- Then 创建或聚焦绑定该远程项目及其权威目录的终端
|
||||
- When 用户从右侧工作栏“+”选择终端
|
||||
- Then 创建绑定该远程项目及其权威目录的额外终端
|
||||
- And 面板持续显示远程主机和工作目录
|
||||
- And 连接失败时保留重连与诊断信息
|
||||
- And 连接失败时保留远端目标、重连与诊断信息,不回退为本机终端
|
||||
- When 切换当前项目后重新选择该终端 Tab
|
||||
- Then 仍激活原远程实例,不重绑到新项目
|
||||
|
||||
### US-C5 打开受控浏览器
|
||||
|
||||
作为用户,我希望浏览器按钮打开 GoodBuddy 受控浏览器,以便查看与 Agent 相同的会话。
|
||||
作为用户,我希望从右侧工作栏使用 GoodBuddy 受控浏览器,并按需创建独立页面。
|
||||
|
||||
验收:
|
||||
|
||||
- Given 浏览器面板跟随当前上下文且当前会话已有浏览器会话
|
||||
- When 用户点击浏览器按钮
|
||||
- Then 工作栏聚焦绑定当前 Conversation 的最近使用浏览器实例
|
||||
- And 不打开系统默认浏览器或无意创建重复实例
|
||||
- Given 现有浏览器实例都绑定到其他 Conversation
|
||||
- When 用户点击浏览器按钮
|
||||
- Then 工作栏创建并聚焦绑定当前 Conversation 的新浏览器实例
|
||||
- Given 当前没有活动 Conversation
|
||||
- And 已存在浏览器实例
|
||||
- When 用户点击浏览器按钮
|
||||
- Then 工作栏聚焦最近使用的浏览器实例并保持其目标
|
||||
- Given 当前没有活动 Conversation
|
||||
- And 不存在浏览器实例
|
||||
- When 用户点击浏览器按钮
|
||||
- Then 创建待选择目标的空实例
|
||||
- Given 用户需要同一 Conversation 的另一个独立页面
|
||||
- Given 已有浏览器 Tab,当前 Conversation 与其目标不同或没有活动 Conversation
|
||||
- When 用户选择该 Tab
|
||||
- Then 激活该确切实例,保留绑定目标、页面及导航状态,不重绑、重建或改选最近匹配实例
|
||||
- Given 当前有活动 Conversation,无论是否已有匹配实例
|
||||
- When 用户从工作栏“+”选择浏览器
|
||||
- Then 每次创建拥有独立页面和导航状态的新实例
|
||||
- And 同一 Conversation 内共享隔离登录上下文
|
||||
- Then 每次创建绑定当前 Conversation 的额外实例,页面和导航状态独立,同一 Conversation 内共享隔离登录上下文
|
||||
- Given 当前没有活动 Conversation,无论是否已有浏览器实例
|
||||
- When 用户从“+”选择浏览器
|
||||
- Then 创建待选择目标的空实例,不复用已有实例
|
||||
- And 以上操作均使用 GoodBuddy 受控浏览器,不打开系统默认浏览器
|
||||
|
||||
### US-C6 查看本机资源
|
||||
|
||||
@@ -186,17 +282,26 @@
|
||||
验收:
|
||||
|
||||
- Given 资源监控面板尚未打开
|
||||
- When 用户点击左下角资源监控按钮
|
||||
- When 用户从右侧工作栏“+”选择资源监控
|
||||
- Then 助手工作栏创建并激活单实例资源监控面板
|
||||
- And 页面显示整机 CPU、内存、磁盘读写及 GoodBuddy 桌面进程摘要
|
||||
- And 页面每 2 秒更新一次,并用图表保留最近 5 分钟
|
||||
- And 可见活动期间每 2 秒采样一次,前次请求未结束时跳过新请求,图表最多保留 150 个真实时间样本,连续采样约 5 分钟
|
||||
- Given 资源监控实例已存在
|
||||
- When 用户选择其 Tab 或再次从“+”选择资源监控
|
||||
- Then 激活同一本机单实例,不新建重复面板
|
||||
- When 用户切换到其他工作栏 Tab、收起工作栏、隐藏窗口或最小化窗口
|
||||
- Then 资源监控停止请求新样本
|
||||
- When 用户再次显示窗口并返回资源监控
|
||||
- Then 首次采样建立区间指标基线,下一次采样开始显示 CPU 和磁盘 IO 速率
|
||||
- And 内存等瞬时指标可立即显示,基线未建立时不显示虚构零值
|
||||
- Given 某项指标在当前平台不可用
|
||||
- When 页面收到部分结果
|
||||
- Then 该指标显示“不可用”及原因,其他指标继续更新
|
||||
- Given 本次采样全部失败或超时
|
||||
- Then 显示失败原因和重试入口,保留已有历史并标明最后更新时间,不把旧值当作实时数据
|
||||
- And 失败或暂停采样期间保留真实时间缺口,不补零或伪造中间样本
|
||||
- When 可见活动期间自动重试或用户手动重试成功
|
||||
- Then 按真实采样结果恢复更新,较晚返回的失活前旧请求不覆盖当前结果
|
||||
|
||||
### US-C7 从任意应用打开工具
|
||||
|
||||
@@ -205,35 +310,51 @@
|
||||
验收:
|
||||
|
||||
- Given 用户位于任意一级页面
|
||||
- When 用户点击文件、终端、浏览器或资源监控
|
||||
- When 用户展开右侧工作栏,选择已有工具 Tab 或通过“+”选择文件、终端、浏览器或资源监控
|
||||
- Then 主页面保持不变
|
||||
- And 右侧工作栏打开对应面板
|
||||
- And 工具不会覆盖主内容或伪装成一级页面
|
||||
- And 创建失败时工作栏保持展开并显示对应错误,主页面和其他实例不被清空
|
||||
- And 后台事件不自动展开工作栏或切换当前 Tab,左侧和应用中心不复制工具入口或选中状态
|
||||
|
||||
## 5. Epic D:可访问性和布局
|
||||
|
||||
### US-D1 使用键盘打开系统工具
|
||||
### US-D1 使用键盘管理应用与访问工具
|
||||
|
||||
作为键盘用户,我希望按顺序聚焦并打开左下角工具,以便不依赖鼠标。
|
||||
作为键盘用户,我希望浏览和管理应用,并通过右侧工作栏访问工具,以便不依赖鼠标。
|
||||
|
||||
验收:
|
||||
|
||||
- 每个系统工具和设置都是独立按钮
|
||||
- Tab 顺序与视觉顺序一致
|
||||
- Enter 和 Space 均可激活按钮
|
||||
- 每个纯图标按钮都有可访问名称和工具提示
|
||||
- 焦点样式不被侧栏边界裁切
|
||||
- Given 用户使用键盘访问侧栏底部
|
||||
- Then 应用中心和设置是独立按钮,Tab 顺序与视觉顺序一致,Enter 和 Space 可激活,图标按钮有可访问名称和工具提示
|
||||
- When 打开应用中心
|
||||
- Then 入口提供菜单展开状态和控制关系,焦点进入首个可用菜单项;上下方向键循环移动,Home、End 到首尾,Enter、Space 激活
|
||||
- When 按 Escape 关闭菜单
|
||||
- Then 焦点返回应用中心入口;Tab 关闭菜单后继续正常焦点顺序,点击外部或焦点离开也关闭
|
||||
- When 选择“管理应用”
|
||||
- Then 关闭菜单并进入既有 Modal,焦点进入搜索;每行的打开、设置、排序及详情中的启用、常驻控件按正常 Tab 顺序可达,不伪装成单个菜单项;进入详情及返回时滚动归顶,返回后聚焦搜索
|
||||
- When 通过键盘修改常驻或顺序
|
||||
- Then Switch 使用共享开关语义并支持 Space,上移和下移按钮名称包含应用名,结果通过 polite 状态区播报,失败保留焦点
|
||||
- Given 用户进入右侧工作栏
|
||||
- Then Tab 选择、游标焦点、方向键及 Home、End 由工作栏负责,使用 `tablist`、`tab`、`tabpanel` 和 `aria-selected`,按确切实例表达选中状态
|
||||
- And “+”为普通按钮,工具状态纳入 Tab 可访问名称;激活后焦点进入面板,关闭工作栏后返回固定工作栏开关
|
||||
- And 焦点环不被边界裁切,应用中心及设置沿用共享浮层和原生浏览器遮挡处理
|
||||
|
||||
### US-D2 在窄窗口使用快捷栏
|
||||
### US-D2 在窄窗口访问应用中心与工作栏
|
||||
|
||||
作为小屏用户,我希望侧栏抽屉中的工具按钮保持可操作。
|
||||
作为小屏用户,我希望应用管理和工具实例在窄窗口中仍可操作。
|
||||
|
||||
验收:
|
||||
|
||||
- 工具按钮保持单行且不重叠
|
||||
- 不因宽度不足隐藏文件、终端、浏览器、资源监控或设置
|
||||
- 触控点击区域符合统一界面规范
|
||||
- 关闭侧栏抽屉后焦点返回原触发按钮
|
||||
- Given 侧栏宽度为 `220px` 至 `420px`,或窄窗口使用侧栏抽屉
|
||||
- Then 底部应用中心和设置不重叠,不因宽度不足隐藏入口或缩小点击区域
|
||||
- When 打开应用菜单或通过“管理应用”进入 Modal
|
||||
- Then 菜单向上锚定且限制在窗口内,可内部滚动;Modal 中长名称可换行,搜索、常驻、排序、设置及重新开启入口均可达
|
||||
- And 触控不依赖悬停,工具提示保持在窗口内,点击区域符合统一规范
|
||||
- When 关闭侧栏抽屉
|
||||
- Then 焦点返回原触发按钮
|
||||
- Given 右侧工作栏有多个工具实例
|
||||
- Then Tab 单行横向滚动且“+”持续可见,工作栏停靠回流,不遮罩主页面;布局和 Tab 交互仍由工作栏负责
|
||||
|
||||
## 6. Epic E:项目活动级联
|
||||
|
||||
@@ -249,6 +370,9 @@
|
||||
- Then 进入该确切 Conversation ID,清除搜索、任务选择和会话菜单,窄窗口关闭侧栏
|
||||
- Given 设置页有未保存修改
|
||||
- Then 会话跳转仍需通过既有离开检查,取消离开保留设置草稿和原项目
|
||||
- Given 用户选择的确切 Conversation ID 未在内存中或已被删除
|
||||
- When 请求进入该会话
|
||||
- Then 刷新持久化会话列表后仍只查找同一 ID;刷新失败或会话不存在时显示通知并保留原页面和选择,不回退到项目最新会话或其他会话
|
||||
|
||||
### US-E2 键盘、窄窗口与实时结束
|
||||
|
||||
@@ -259,14 +383,34 @@
|
||||
- Then 点击项目后仅显示会话层,可通过返回按钮回到项目列表;长列表可滚动到最后一项
|
||||
- Given 鼠标从项目行移向相接的会话面板
|
||||
- Then 子菜单保持打开,点击会话正常导航
|
||||
- Given 当前项目的活动全部结束
|
||||
- Given 当前项目已无待处理、运行中或已完成未查看会话
|
||||
- Then 返回项目层并恢复可用焦点;全局无活动时整行隐藏摘要并关闭菜单,新活动到来时只恢复摘要
|
||||
- When 点击外部、焦点离开、按 Tab 或隐藏侧栏
|
||||
- Then 菜单关闭,后台新活动不自动重新展开
|
||||
|
||||
### US-E3 完成后保留至查看
|
||||
|
||||
对应 [FR-14](./prd.md#fr-14-项目活动汇总),具体状态规则见[活动逻辑](./logic-design.md#9-项目活动汇总)。
|
||||
|
||||
- Given 用户正在其他会话或应用中工作
|
||||
- When 本次应用会话内的运行成功完成,或已观察到的后台任务转为完成
|
||||
- Then 活动会话保留为“已完成”,全局与项目的已完成计数同步更新
|
||||
- When 从活动菜单或普通侧栏实际进入该会话的可见聊天
|
||||
- Then 清除其完成通知;没有其他活动行时隐藏摘要并关闭菜单
|
||||
- Given 聊天被设置覆盖或文档隐藏
|
||||
- When 对应运行完成
|
||||
- Then 完成通知保留,直到设置关闭且目标聊天在可见文档中显示;取消设置离开不清除通知
|
||||
- Given 目标聊天已可见且未被设置覆盖
|
||||
- When 运行成功完成
|
||||
- Then 不保留该会话的完成通知
|
||||
- Given 初始加载已完成任务历史,或任务失败、取消
|
||||
- Then 不产生新的完成通知;重载或重启也不恢复上一应用会话的通知
|
||||
- Given 同一会话仍有待处理或运行中工作
|
||||
- Then 按实时状态优先展示,只计入一个类别
|
||||
|
||||
## 7. 后续需求场景
|
||||
|
||||
本节对应新增计划,不计入应用中心首期完成条件。
|
||||
US-F1、US-F2 为后续市场计划;US-F3 至 US-F5 的本机推理监控已接入源码,当前支持边界见技术设计,验证范围见实施进度。
|
||||
|
||||
### US-F1 连接组织应用市场
|
||||
|
||||
@@ -275,33 +419,85 @@
|
||||
- Given 组织已部署提供应用市场的 ShareServer
|
||||
- When 用户配置市场 URL,并在来源要求时完成登录
|
||||
- Then 可以浏览该来源的应用目录、详情及可获取版本
|
||||
- Given 市场暂时不可达
|
||||
- Then 显示失败原因与重试入口,已安装和内置应用仍可打开,不切换公共市场
|
||||
- And 市场 URL 按设备保存,重启后恢复;认证信息使用受保护凭据存储,不作为可导出配置
|
||||
- Given 市场不可达、URL 无效、登录过期或认证失败
|
||||
- When 用户访问该来源
|
||||
- Then 显示具体原因及修改地址、重新登录或重试入口,已安装和内置应用仍可打开,不静默切换公共市场或其他来源
|
||||
- Given 用户修改市场来源或开始获取应用资源
|
||||
- Then 市场配置不改变模型、应用更新及 Runtime 等独立下载源;已开始的下载保持原来源,失败不静默切源
|
||||
|
||||
### US-F2 导入导出应用定义
|
||||
|
||||
对应 FR-15。
|
||||
|
||||
- Given 用户需要在私有部署间迁移应用
|
||||
- When 导出应用为 `.yml` 并在另一台设备导入
|
||||
- When 导出应用为 YAML,并在另一台设备选择 `.yml` 或 `.yaml` 文件导入
|
||||
- Then 预览应用身份、版本、可迁移配置和依赖,凭据与业务数据不包含在导出文件中
|
||||
- When 用户确认有效定义且所需依赖已满足,导入成功
|
||||
- Then 应用目录显示导入的身份、版本和配置,可按其启用状态打开或配置,不自动启动模型或任务
|
||||
- Given 同标识应用已存在
|
||||
- Then 用户可选择更新或取消,取消后原应用与配置保持不变
|
||||
- When 用户查看导入预览
|
||||
- Then 可选择更新或取消,取消后原应用与配置保持不变
|
||||
- When 更新成功
|
||||
- Then 显示已确认的新定义和配置
|
||||
- When 更新失败
|
||||
- Then 显示具体原因及重试入口,保留原应用定义、版本和配置,不留下部分更新
|
||||
- Given 文件格式无效、版本不支持或离线环境缺失程序包或模型资源
|
||||
- Then 显示具体问题与补充要求,不将应用误报为可运行,也不自动启动模型或任务
|
||||
|
||||
### US-F3 管理本机模型服务
|
||||
### US-F3 查看本机推理服务与资源
|
||||
|
||||
对应 [FR-16](./prd.md#fr-16-本机模型管理入口新增计划项)。
|
||||
|
||||
- Given 用户使用依赖本机模型的应用
|
||||
- When 从应用中心或常驻菜单打开“本机模型”
|
||||
- Then 可查看已接入模型的状态、管理方式及使用方,两处入口访问同一页面
|
||||
- Given 多个应用共用一个 GoodBuddy 受管模型服务
|
||||
- Then 展示同一服务及使用方,启动操作不重复创建进程
|
||||
- When 用户停止或重启仍有任务使用的受管服务
|
||||
- Then 先展示任务影响供用户决定,取消后服务保持运行;操作失败显示原因与实际状态
|
||||
- Given 模型服务由外部程序启动,且未接入其管理接口
|
||||
- Then 展示“外部管理”与连接配置,不提供停止或重启
|
||||
- When 用户关闭管理页或取消常驻
|
||||
- Then 模型运行状态不变
|
||||
- When 从常驻入口、应用菜单或管理 Modal 的“打开”进入“本机推理监控”
|
||||
- Then 均打开同一管理 Modal,来源菜单或管理 Modal 关闭,底层工作区、当前项目、会话和未提交草稿保持不变
|
||||
- And 直接显示服务列表,不出现任务页签、任务历史或外部连接;知识库、智能心跳和魔法笔记仍打开主内容区页面
|
||||
- And 初始焦点进入关闭按钮,背景不可交互,Tab/Shift+Tab 在 Modal 内循环;标题与关闭按钮保持可见,正文独立滚动
|
||||
- Given 没有影响确认且没有操作提交
|
||||
- When 点击关闭按钮或按 Escape
|
||||
- Then 关闭并卸载管理 Modal,底层工作区继续显示,模型服务不停止;点击遮罩不关闭
|
||||
- And 焦点恢复到仍可用的原触发元素;原入口已移除时回到应用中心,窄侧栏已隐藏时回到侧栏开关
|
||||
- When 查看服务列表
|
||||
- Then 显示服务名、模型、引擎、简短状态及错误;共享服务只展示一次
|
||||
- Given 向量服务独立进程正在运行
|
||||
- Then 显示带 PID 和进程范围的 CPU、工作集内存;CPU 首次采样显示预热状态
|
||||
- Given 服务 worker 与宿主共享进程,或进程已退出、采样失败
|
||||
- Then 在服务行显示不能独立统计或当前不可用的原因,不展示虚构零值或借用整个应用进程指标
|
||||
- Given ASR 有活动请求但没有 Worker 就绪观测
|
||||
- Then 显示“按需运行(状态未知)”,不推断服务正在运行
|
||||
|
||||
### US-F4 单任务取消入口(已移除)
|
||||
|
||||
保留故事 ID。FR-16 已收窄为服务监视,Modal 不再提供任务取消按钮。执行路径的原有取消行为和活动请求登记继续用于服务影响校验,见[逻辑设计](./logic-design.md#81-本机推理管理)。
|
||||
|
||||
### US-F5 管理实际执行服务生命周期
|
||||
|
||||
对应 FR-16;当前引擎支持清单见[技术设计](./technical-design.md#111-本机推理任务与执行服务)。
|
||||
ASR 取消独立 Worker,向量停止核对所有活动请求,OCR 有活动或排队请求时拒绝释放。
|
||||
运行阶段包括排队、加载和执行;当前未接入统一进度及来源项目对象关联。资源指标按服务进程统计,见 US-F3。
|
||||
|
||||
- Given 多个应用共用一个由 GoodBuddy 受管子进程承载的模型服务
|
||||
- When 用户启动、停止或重启服务
|
||||
- Then 仅提供引擎实际支持的操作,按同一服务串行处理;重复请求不重复创建进程或并发执行同一服务的重启
|
||||
- And 以真实进程退出和服务就绪确认结果,不将命令已发送当作操作成功
|
||||
- Given 用户准备停止、重启或释放服务或模型
|
||||
- When 用户发起操作
|
||||
- Then 在管理 Modal 上方打开紧凑影响确认,列出使用方、任务及影响;无已登记活动任务时说明执行前再次核对,取消后不发送操作指令且运行状态不变
|
||||
- And 初始焦点进入“保留运行状态”,Tab/Shift+Tab 在确认内循环,底层管理 Modal 不可交互;Escape 只关闭确认并恢复服务操作按钮焦点,按钮不可用时回到管理 Modal
|
||||
- When 确认提交尚未完成
|
||||
- Then 禁止重复执行,确认和取消按钮均禁用,两层 Modal 都不能通过关闭按钮或 Escape 关闭
|
||||
- When 操作成功
|
||||
- Then 只关闭影响确认,管理 Modal 保持打开
|
||||
- Given 执行前出现确认清单未包含的新任务
|
||||
- Then 拒绝执行,关闭旧确认并显示错误,自动刷新状态;刷新期间或失败时禁用服务操作,再次发起操作须确认新的影响清单
|
||||
- Given 停止失败,或重启时停止成功但启动失败
|
||||
- Then 显示实际状态、原因和可重试错误,不假报已停止或已恢复;无法确认时显示未知
|
||||
- When 服务再次启动或重启成功
|
||||
- Then 已中断任务不自动重放,由来源应用显式重新发起
|
||||
- Given 模型连接由外部程序管理
|
||||
- Then 在既有模型与连接设置中查看,不将其列入本机服务或提供停止、重启
|
||||
- Given 模型在进程内运行
|
||||
- Then 仅提供引擎实际支持的加载、释放,不显示虚构 PID、进程数或进程启动停止状态
|
||||
- When 用户关闭管理 Modal、关闭管理应用或取消常驻
|
||||
- Then 模型服务运行状态不变,取消常驻不关闭当前 Modal,卸载模型文件为独立操作
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
| [多终端页签 PRD](./terminal-tabs-prd.md) | 工作栏应用的固定、可关闭、单实例和多实例策略,以及多终端生命周期 |
|
||||
| [浏览器多实例技术设计](./browser-tabs-technical-design.md) | 浏览器 Tab 身份、Conversation 绑定、IPC、BrowserService 和 MCP 路由 |
|
||||
| [实现与验证进度](./progress.md) | 工作区与 Runtime 相关改动的实现事实、验证证据与剩余阻塞 |
|
||||
| [执行记录存储与升级回收](./execution-history-storage.md) | 子任务差量存储、远程去重、旧库转换、会话摘要与完整历史按需读取、内存引用释放 |
|
||||
| [执行记录存储与升级回收](./execution-history-storage.md) | 子任务差量存储、远程去重、旧库转换、会话置顶、摘要与完整历史按需读取、内存引用释放 |
|
||||
| [Runtime 交互边界](./runtime-interactions.md) | 原生权限、结构化问答、后台拒绝、终端交互、并行与输出、消息请求状态及资源生命周期规则 |
|
||||
| [Runtime 进程复用技术设计](./runtime-process-reuse-technical-design.md) | 跨 Runtime 资源调研、锁定协议证据、进程与会话拆分、远端模型桥、实施顺序和性能验收 |
|
||||
| [Runtime 执行清单技术设计](./runtime-checklist-technical-design.md) | OC/CN 原生清单提取、远程 Agent 传递复用、消息恢复与真实部署验收;尚未实现 |
|
||||
|
||||
@@ -153,9 +153,38 @@ Conversation 归属,不新建租约或把页面重新归属到子级对话。
|
||||
单次工具取消信号、Tab 使用租约信号和请求信号,取消一次工具调用不终止后续调用。
|
||||
|
||||
该阶段 MCP transport、endpoint 和 token 生命周期不变,只扩展服务端 capability 内容和内部
|
||||
路由。工具调用使用当前请求绑定的 Tab,只有上述显式导航恢复会更换绑定。
|
||||
路由。工具调用使用当前请求绑定的 Tab;显式导航恢复和下述点击开窗会更换绑定。
|
||||
|
||||
### 6.2 Agent 多 Tab 阶段
|
||||
### 6.2 Page-created tabs
|
||||
|
||||
`target="_blank"` and `window.open` create managed `WebContentsView` tabs through Electron's
|
||||
`setWindowOpenHandler.createWindow`. Chromium retains the original request and opener relationship,
|
||||
including a blank window followed by a script-assigned location. These tabs share the Conversation
|
||||
partition, proxy and permission restrictions. HTTP(S) URL checks remain in the session and filtering
|
||||
proxy; `about:blank` is permitted only as the initial empty popup. Unsupported URL schemes remain denied.
|
||||
|
||||
BrowserService reserves capacity before creating the native view, assigns a new Tab/workbar identity,
|
||||
and owns initialization, the Driver and cleanup. Initialization failure or Conversation disposal releases
|
||||
the popup. Closing the opener leaves an already created popup alive. A popup's `window.close()` releases
|
||||
its service slot and usage leases; Renderer removes its workbar instance and returns to the opener when
|
||||
it is still available. No separate unmanaged BrowserWindow is created.
|
||||
|
||||
Live state includes `openerTabId`. A popup from the visible browser is selected even when the active chat
|
||||
has switched to a different Conversation. Other workbar panels and viewport occlusion rules retain their
|
||||
existing behavior. The existing workbar capacity notification still applies when all 32 slots are occupied.
|
||||
|
||||
During `browser_click`, the service collects popup registrations from that Tab and waits for their
|
||||
initialization. The result identifies the last created popup. BrowserModelTools binds subsequent tools to
|
||||
that Tab and asks the model to obtain new references with `browser_snapshot`; the MCP gateway also replaces
|
||||
its capability binding and usage lease. Switching visible tabs does not alter this binding. Closing the
|
||||
popup retains the existing explicit `browser_navigate` recovery behavior. A page may still be loading
|
||||
when the click returns. Popups requested asynchronously after the click has completed are displayed as
|
||||
managed tabs, but are not attributed to that completed tool call.
|
||||
|
||||
The remote ACP Runtime exposes its separately scoped image MCP server, not this desktop browser gateway;
|
||||
this change does not add browser access to the deployed GoodBuddy Agent.
|
||||
|
||||
### 6.3 Agent 多 Tab 阶段
|
||||
|
||||
只有明确需要 Agent 管理多个页面时,才增加:
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"date": "2026-09-17",
|
||||
"status": "passed-with-counting-limitation",
|
||||
"sourceBase": "3639566e1b52296c6a9bc409bcfcecb86c905e30",
|
||||
"source": "Working tree including consecutive-request cancellation and terminal snapshot fixes",
|
||||
"environment": {
|
||||
"platform": "Windows x64",
|
||||
"runtime": "OpenCode",
|
||||
"mode": "ask",
|
||||
"model": "Existing configured default text model",
|
||||
"upstreamProtocol": "http",
|
||||
"fullProductionApp": true,
|
||||
"isolatedProfile": true,
|
||||
"nativeInput": true
|
||||
},
|
||||
"checks": {
|
||||
"twoQueuedRequestsCompleteAndPersist": true,
|
||||
"cancelAfterExternalDispatch": true,
|
||||
"cancelToResendMs": 25,
|
||||
"resendCompletesAndPersists": true,
|
||||
"cancellationPersists": true,
|
||||
"stopAbsentAfterBothScenarios": true,
|
||||
"sendEnabledWithUnsentProbe": true,
|
||||
"terminalScreenshotsReviewed": true
|
||||
},
|
||||
"requests": {
|
||||
"instrumentedExternalDispatches": 4,
|
||||
"http200": 3,
|
||||
"cancelledBeforeResponseHeaders": 1,
|
||||
"observedRetries": 0,
|
||||
"toolExecutions": 0,
|
||||
"initialUninstrumentedCompletedGenerations": 2,
|
||||
"totalUiSubmissions": 6,
|
||||
"totalExternalDispatches": null
|
||||
},
|
||||
"limitations": [
|
||||
"Initial Main counting hook missed child-process HTTP traffic; total dispatches across attempts are unknown.",
|
||||
"Successful rerun used a transparent Main relay to the unchanged configured HTTP upstream for dispatch counting.",
|
||||
"Seven tool definitions remained exposed by production Ask, with zero observed tool executions.",
|
||||
"Upstream proxy retries and cancelled request outcome at the upstream are not observable.",
|
||||
"No deterministic stale-event ordering, remote Host, or restart verification in this live run."
|
||||
],
|
||||
"validation": {
|
||||
"focusedTestsPassed": 362,
|
||||
"typecheck": "passed",
|
||||
"productionBundle": "passed"
|
||||
},
|
||||
"cleanup": {
|
||||
"ownedProcessesStopped": true,
|
||||
"isolatedProfileRemoved": true,
|
||||
"temporaryBuildRemoved": true,
|
||||
"sensitiveCopiesRemoved": true,
|
||||
"originalSettingsUnchanged": true,
|
||||
"targetSourceHashesUnchanged": true
|
||||
},
|
||||
"localArtifacts": "consecutive-ui-0917/{final-evidence.json,scenarios.json,report.json,normal-terminal.png,cancel-resend-terminal.png} under the approved temporary directory"
|
||||
}
|
||||
@@ -33,22 +33,33 @@
|
||||
的缓存引用,不影响其他会话。若事务随后回滚或仍收到该任务的后续事件,按已提交记录重建。
|
||||
释放引用不等于进程 RSS 立即下降,垃圾回收和 Runtime 自身会话缓存另按生命周期回收。
|
||||
|
||||
## 活动历史连续保存
|
||||
|
||||
活动历史在桌面 `AssistantDatabase` 中按变化记录写入,保留记录顺序和重复 ID 的独立位置。
|
||||
连续保存使用最近一次成功提交的记录作为比较基准,不再逐次读取整份历史。关闭数据库、
|
||||
清空数据或检测到其他 SQLite 连接提交后重新读取;事务失败不更新比较缓存,重试仍基于已提交内容。
|
||||
本次优化不改变数据库 schema 或完整快照 IPC,也不改变 Agent、Runtime 和远程模型桥。
|
||||
本机与远程任务的桌面活动记录共用此保存路径,Host 不需要更新。
|
||||
|
||||
WAL 增长与 SQL 全历史扫描次数的定向验证见 [BUILD.md](../../../BUILD.md);
|
||||
这些指标不等于安装后应用的物理磁盘吞吐测量。
|
||||
|
||||
## 会话列表读取与前端保留
|
||||
|
||||
列表读取与数据库存储是两个不同层次。`messages.metadata_json` 继续保留完整过程,
|
||||
本次读取优化不修改 schema、迁移或用户数据:
|
||||
|
||||
- Renderer 启动及焦点、会话、队列刷新使用 `conversations.listSummaries(detailIds)`。
|
||||
普通未打开会话只返回标题、项目、上下文设置和消息数量/首条角色,不解析或跨 IPC
|
||||
普通未打开会话只返回标题、项目、置顶状态、上下文设置和消息数量/首条角色,不解析或跨 IPC
|
||||
传输正文、工具、子任务及图片操作元数据。
|
||||
- 当前会话、现有 KeepAlive 视图,以及 Main 仍持有请求、数据库中仍有活动 Task 或
|
||||
streaming 消息的会话返回完整消息。保留既有最近 100 条加较早活动会话的列表规则,
|
||||
请求中的已缓存会话即使更早也仍能刷新。
|
||||
streaming 消息的会话返回完整消息。列表包含全部 active 会话,不限制数量,
|
||||
按置顶优先、组内更新时间倒序返回;仅置顶不会加载完整历史。
|
||||
- 打开历史通过 `conversations.get(id)` 读取全部消息,不使用会截断历史的分页上限。
|
||||
加载时明确显示进度;失败保留会话与输入,提供原地重试。发送、队列派发和压缩上下文
|
||||
必须先确认完整历史;复制与导出临时读取完整内容,不把未打开会话留入视图缓存。
|
||||
必须先确认完整历史,发送时不按消息条数截断;复制与导出临时读取完整内容,不把未打开会话留入视图缓存。
|
||||
- 搜索由 Main 在 SQLite 中读取标题与正文,不读取过程元数据,保持原来的 Unicode
|
||||
大小写匹配和字面子串规则。范围包含 Renderer 已保留的较早会话;前端同时匹配尚未
|
||||
大小写匹配和字面子串规则。范围包含全部 active 会话;前端同时匹配尚未
|
||||
落库的消息。搜索状态只保留命中的 ID,不额外持有整份历史对象。
|
||||
- KeepAlive 淘汰后,在现有保存周期中释放已经确认落库且空闲的详情及持久化比较引用。
|
||||
未保存消息、正在执行的会话、待答/待审批内容不被淘汰。读取中的请求按会话合并;
|
||||
@@ -65,8 +76,27 @@
|
||||
此优化不等于工具折叠内容的延迟渲染,也不保证操作系统立即归还全部 Private Commit。
|
||||
实际测量与适用边界见[实现与验证进度](./progress.md)。
|
||||
|
||||
## 会话置顶
|
||||
|
||||
界面行为见[会话置顶与操作菜单](../application-tool-navigation/ui-design.md#会话置顶与操作菜单)。
|
||||
`conversations.pinned` 是桌面数据库维护的会话属性,本地和消息通道会话共用,默认未置顶。
|
||||
完整快照与摘要均返回 `pinned`,旧快照缺省按未置顶处理。写入只通过
|
||||
`conversations.setPinned({ conversationId, pinned })`,经 Preload、可信发送方校验和严格
|
||||
Schema 校验后更新活动会话,再发布会话变更通知;不存在的会话报错。该写入不修改时间戳、
|
||||
消息或其他会话字段,普通自动保存 header 排除 `pinned`,不会覆盖置顶状态。
|
||||
|
||||
尚未落库的本地草稿先等待保存队列并保存,再设置置顶;消息通道会话直接调用专用接口。
|
||||
Renderer 合并时独立接收 Main 的置顶属性,即使本地正文更新时间更晚也不忽略它。
|
||||
请求修订号和进行中标记防止较早发出的列表/详情响应覆盖刚更新的置顶状态。
|
||||
此属性不修改 Runtime 或远端 Agent 协议。
|
||||
|
||||
## 旧库升级
|
||||
|
||||
启动检查只根据旧 schema 或仍未转换的笔记正文决定是否进入升级流程;正常写入、更新
|
||||
与删除产生的 SQLite 空闲页不是旧格式证据。迁移后新增数据使用当前格式,不因正常使用
|
||||
再次迁移。同一次启动已进入的迁移流程可重试压缩;若正文已转换、仅压缩中断,退出后
|
||||
重开不单凭空闲页重新进入迁移,剩余页由 SQLite 后续写入复用。不新增持久化迁移标记。
|
||||
|
||||
桌面在打开业务数据库之前启动独立 Node 工作线程,使用同一套编码和还原函数转换旧库。
|
||||
按任务有序读取,每批最多 32 条,提交后报告进度;SQLite 保留事务原子性及正常 WAL
|
||||
自动 checkpoint,每个任务结束时截断 WAL。读取缓存为 64 MiB,避免 1 MiB 级历史
|
||||
@@ -77,8 +107,10 @@
|
||||
不建立额外的恢复状态表。最后运行 SQLite `VACUUM` 回收已释放的空间,而非
|
||||
在转换前复制整份膨胀内容。回收仍需要额外可用磁盘空间,失败保留已提交转换并允许重试。
|
||||
|
||||
当前源码的业务初始化随后将数据库升级为 schema 35;schema 34 用户也会执行本次整理,
|
||||
成功后后续启动直接跳过。继续复用 schema 34 的子任务事件部分索引和同一差量表示。
|
||||
子任务整理对应 schema 35;schema 34 用户也会执行该整理,成功后后续启动直接跳过。
|
||||
继续复用 schema 34 的子任务事件部分索引和同一差量表示。当前业务初始化随后在事务中
|
||||
升级为 schema 36,新增 `pinned INTEGER NOT NULL DEFAULT 0 CHECK(pinned IN (0, 1))`,
|
||||
已有会话默认未置顶,保留原消息与时间戳;失败回滚本次字段迁移。
|
||||
低于该 schema 的旧版客户端不能重新打开已经升级的数据库。需要回退客户端时,必须使用
|
||||
升级前备份,不要修改 `user_version` 强行绕过版本检查。
|
||||
|
||||
|
||||
@@ -101,12 +101,14 @@ Runtime
|
||||
|
||||
### 3.2 当前上下文只提供默认值
|
||||
|
||||
任务中心只有一个工作栏实例,默认跟随当前项目,不跟随当前 Conversation。用户可以在同一
|
||||
实例内显式切换到全局任务或全部项目,但不能为不同项目打开多个任务中心 Tab。其他可绑定
|
||||
任务中心只有一个工作栏实例,任务列表固定跟随当前项目,不随当前 Conversation 筛选;顶部
|
||||
统计跟随当前 Conversation。不提供全局或所有项目入口。其他可绑定
|
||||
目标的能力使用当前会话、项目、Runtime 和主机帮助新面板实例初始定位,但这些上下文不是
|
||||
使用门槛:
|
||||
|
||||
- 监督默认选择当前会话,用户可以改选其他 Conversation、Task 或实验对象。
|
||||
|
||||
当前监督卡片已支持 Conversation/Task 跟随与固定,绑定保存在 tasks 实例的 `targetRef`;绑定只影响监督卡片,任务列表继续跟随当前项目。实现范围与验证见[监督者进度](../conversation-supervision/progress.md)。实验对象仍属后续工作。
|
||||
- Runtime 默认跟随当前会话或 Task;执行事件可以查看,但 Job/Run 不作为独立选择对象。
|
||||
- 终端创建时固定到当前项目执行空间,由项目配置自动决定本机或托管 SSH;没有当前项目时
|
||||
使用本机 Home。
|
||||
@@ -217,8 +219,8 @@ Runtime
|
||||
- 应用注册表分别声明实例策略、默认打开、必须存在、是否可关闭和是否允许排序。`single` 或
|
||||
`multiple` 只控制实例数量;`required` 控制布局修复,不能兼作关闭或排序策略。
|
||||
- 在工作栏“+”目录中重复选择单实例应用时聚焦已有实例;选择浏览器、终端等多实例应用时
|
||||
每次创建新实例。主侧栏系统工具快捷入口按当前上下文聚焦匹配实例,不能因连续点击无意
|
||||
创建重复实例。
|
||||
每次创建新实例。选择已有 Tab 只激活该实例,保留其目标与历史。主侧栏应用中心不重复
|
||||
提供文件、终端、浏览器或资源监控入口。
|
||||
- 支持方向键、Home、End、Enter、Space、可关闭面板的关闭命令和正确焦点恢复。不可关闭
|
||||
实例不渲染关闭按钮,也不响应关闭快捷命令。
|
||||
- 徽标显示未解决数量、等待审批或失败状态,并同时提供文字或可访问名称。
|
||||
@@ -230,7 +232,7 @@ Runtime
|
||||
从当前聊天右栏迁移时,Task Center 和工作区作为默认打开、单实例且不可关闭的基础应用保留;
|
||||
浏览器保留一个默认打开实例,但注册为可关闭的多实例应用;成果保持默认打开、单实例且可
|
||||
关闭;终端不默认打开,由用户通过“+”创建,并从首期开始支持多实例。Tab 必须单行横向滚动,
|
||||
不能自动隐藏或缩写到不可辨认,“+”不能随滚动消失。Task Center 继续作为 Task 的现有
|
||||
不能自动隐藏或缩写到不可辨认,“+”紧随最后一个 Tab 并随标签滚动,溢出时两端提供固定滚动箭头。Task Center 继续作为 Task 的现有
|
||||
入口;审批定位到所属 Task 或 Runtime。智能心跳不作为工作栏页签,其报告、建议、历史和
|
||||
完整配置统一归属“智能心跳”菜单入口。当前阶段不新增独立自动化中心。
|
||||
|
||||
@@ -260,25 +262,26 @@ Runtime
|
||||
Task Center 保留为工作栏中的稳定入口,并在现有基础上适度完善:
|
||||
|
||||
- 只索引 Task;每个 Task 只关联一条 Conversation,一条 Conversation 可以关联多个 Task。
|
||||
- 显示名称、关联 Conversation、Global 或 Project 范围、Ask/Execute、状态、最近进展、
|
||||
真实活动时间和需要关注信息。
|
||||
- 顶部显示当前会话消息总数和 Agent 累计回复时间;列表显示本项目任务的名称、关联 Conversation、
|
||||
所属项目、Ask/Execute、状态、最近进展、真实活动时间、累计执行时间和需要关注信息。
|
||||
- 点击 Task 打开其关联 Conversation 并定位 Task。
|
||||
- 完整消息留在 Conversation;工具、Subagent、审批、错误和成果按 Task 关联到 Runtime、
|
||||
活动记录和成果查看器中,不在窄栏复制,也不呈现 Job/Run 树。
|
||||
- 智能心跳的报告、建议、历史和配置不进入任务中心。
|
||||
|
||||
任务中心是应用级单例,默认存在且不可关闭。它的数据范围默认跟随当前项目,并在标题或范围
|
||||
控件中明确显示项目;用户可以切换到“全局任务”或“全部项目”,该选择只改变同一实例的列表
|
||||
范围,不创建第二个任务中心 Tab。切换回“跟随当前项目”后,项目变化立即刷新列表。后台状态
|
||||
任务中心是应用级单例,默认存在且不可关闭。当前项目是唯一范围,标题明确显示项目;切换
|
||||
项目刷新任务和审批,同项目切换会话只更新顶部统计。无项目时提示选择项目。后台状态
|
||||
可以更新徽标和排序,但不能抢占用户当前工作。
|
||||
|
||||
当前实现已按活动项目过滤 Scheduled Task 和审批,并提供“当前项目”“全局任务”“全部项目”
|
||||
范围控件。
|
||||
回复时间按每轮请求区间累加,包含轮内工具和审批等待、不计轮间空闲;任务用时汇总历次执行
|
||||
请求,不按父任务生命周期计算。缺失历史有提示,远程缺少可靠证据时不虚构时间。
|
||||
项目任务标题右侧先显示“+”新建按钮,再显示筛选图标;状态筛选默认收起、默认全部。
|
||||
|
||||
主侧栏最近会话为关联 Task 提供轻量入口:父会话行只显示行首展开按钮,Task 子项使用
|
||||
任务图标和本地化摘要,展开层级只到 Task;点击 Task 子项打开同一 Conversation 并定位。
|
||||
新建定制任务使用 Modal 选择当前或新
|
||||
Conversation,默认 Execute,并持续显示 Runtime、Project、工作目录、工具和审批摘要。
|
||||
新建定制任务使用共享 Modal,默认立即执行和当前 Conversation,可选本项目其他可用会话或
|
||||
新会话,以及单次定时、每日或每周。内容必填,名称可留空从内容生成;工作模式使用关联
|
||||
会话的执行时配置,未设置时使用项目默认值,不在创建表单另设模式。
|
||||
|
||||
详细产品边界以 [Task Center PRD](../task-and-job/task-center-prd.md) 和
|
||||
[Task 与 Job 统一领域模型](../task-and-job/task-and-job-model.md) 为准。
|
||||
@@ -416,8 +419,9 @@ Renderer 不接收任意系统 PID 控制能力。控制动作引用 Main 签发
|
||||
不自动 force、reset、discard 或 stash,不因浏览分支而自动 Fetch。
|
||||
分支触发器复用共享模型选择按钮,带分支图标和展开箭头;Fetch 使用共享次按钮,二者在
|
||||
紧凑工作栏内保持同一行,长分支名省略并提供完整名称提示。
|
||||
展开的分支选择面板不超过 360px 或工作栏可用宽度;搜索使用共享表单标签、输入框与主题样式,
|
||||
分支列表独立限高滚动,创建按钮以紧凑次操作放在列表下方。Escape 收起面板并将焦点返回分支触发器。
|
||||
分支选择面板浮在内容上方,展开时不挤占变更列表和提交历史;宽度不超过 360px,并限制在窗口内。
|
||||
搜索使用共享表单标签、输入框与主题样式,分支列表独立限高滚动,创建按钮以紧凑次操作放在列表下方。
|
||||
点击面板外或焦点移出时收起;Escape 收起面板并将焦点返回分支触发器。
|
||||
- FR-W5:未提交更改支持列表和目录树切换;两种视图均可打开单文件 Diff。
|
||||
- FR-W6:Git 工作区底部提供默认收起的提交历史区,独立滚动,可调整高度。
|
||||
历史标题默认停在面板底部,首次展开向上占用当前 Git 内容区的一半高度;收起后标题回到底部。
|
||||
@@ -457,6 +461,12 @@ Renderer 不接收任意系统 PID 控制能力。控制动作引用 Main 签发
|
||||
|
||||
### 7.7 浏览器
|
||||
|
||||
Page requests made with `target="_blank"` or `window.open` open managed browser tabs in the same
|
||||
Conversation. A popup from the visible page becomes visible in the workbar; closing it from page script
|
||||
removes that popup tab. When an Agent click opens a new tab, subsequent browser tools follow that page
|
||||
and obtain fresh element references. Detailed lifecycle and routing rules are defined in
|
||||
[Page-created tabs](./browser-tabs-technical-design.md#62-page-created-tabs).
|
||||
|
||||
浏览器是可关闭的多实例应用。升级后的默认布局保留一个浏览器实例;用户从“+”目录选择
|
||||
浏览器时每次创建新的 Browser Tab。每个实例必须显示可区分的标题和绑定 Conversation,
|
||||
不能让多个工作栏 Tab 只是同一页面的重复控制入口。
|
||||
@@ -475,7 +485,7 @@ Renderer 不接收任意系统 PID 控制能力。控制动作引用 Main 签发
|
||||
|
||||
新建或重新创建的空白标签页尚无已提交 URL 时禁用刷新;只输入地址草稿不会启用刷新。
|
||||
首次导航期间仍可停止加载。操作错误仅在当前面板本次打开期间显示,切换面板、关闭当前
|
||||
页签或收起工作栏后清除,已离开面板的异步失败不得重新显示旧提示。
|
||||
页签或收起工作栏后清除;关闭失败后重试成功也清除原错误,已离开面板的异步失败不得重新显示旧提示。
|
||||
|
||||
同一 Conversation 下的 Browser Tab 共享隔离存储和代理上下文,但每个 Tab 拥有独立页面、
|
||||
导航状态、操作队列和元素引用空间。模型请求开始时绑定一个确定的 Browser Tab;用户切换
|
||||
@@ -644,7 +654,7 @@ type WorkbarPanelInstance = {
|
||||
|
||||
### 9.1 设置入口
|
||||
|
||||
设置中心“平台功能”提供独立“远程项目(技术预览)”页签及开关,默认关闭。关闭时不显示
|
||||
设置中心“平台功能”提供独立“远程项目”页签及开关,默认关闭。关闭时不显示
|
||||
“主机与远程执行”分类、托管 SSH 项目创建选项和已保存远程项目,Main IPC 同时拒绝新的
|
||||
SSH/远程项目操作;已有 Host、项目和凭据保持不变。若当前项目已经是远程项目,则切回
|
||||
项目列表中的第一个普通
|
||||
@@ -1057,7 +1067,8 @@ todo/plan,本次不为其增加工具。也不增加 plan 工作模式、计
|
||||
- 建立实例级跟随、固定和失效目标语义。
|
||||
- 保留现有任务中心、工作区、浏览器和成果行为;附件与知识库继续由 Composer 管理。
|
||||
- 将 Task Center 明确为 Task 的单例索引,并补齐范围、状态、最近进展、需要关注和直接打开 Conversation。
|
||||
- 审批在所属任务或 Runtime 中持续可见,不新增独立审批面板。
|
||||
- 审批在所属任务或 Runtime 中持续可见;任务中心只为未关联任务的审批保留按需显示的处理
|
||||
区域,具体规则见 [Task Center 交互](../task-and-job/task-center-prd.md#5-交互)。
|
||||
- 智能心跳菜单入口承接完整配置和范围后,再从任务中心移除重复表单;不得移除任务中心本身。
|
||||
|
||||
### 阶段 1:成果与 Runtime 可观测性
|
||||
|
||||
@@ -1,5 +1,228 @@
|
||||
# 工作栏实现与验证进度
|
||||
|
||||
## 2026-09-21 重复历史迁移补修(Desktop 0.13.9 候选)
|
||||
|
||||
- 0.13.8 的检查仍在已有笔记时把 `freelist_count > 0` 当作迁移依据;正常聊天更新
|
||||
也会释放页。此前无笔记的回归提前返回,漏掉这一场景,不能证明该版本已修复。
|
||||
- 启动检查现在只依据旧 schema 或旧笔记 payload;空闲页只用于已进入迁移流程的
|
||||
压缩步骤。同一启动流程的 Worker 重试显式复用已确认的迁移决定,不持久化额外标记。
|
||||
若正文已转换但仅压缩中断,退出后重新启动不因剩余空闲页再次迁移,SQLite 可复用这些页。
|
||||
- 新回归先复现旧实现错误,再覆盖真实 SQLite 的旧笔记迁移、新建与编辑笔记、
|
||||
聊天释放页、连续三次重开、内容一致性和完整性;断言没有进度回调或 VACUUM。
|
||||
- 此次仅修改 Desktop 启动流程;新写入格式、schema、Agent 和远程协议不变。
|
||||
- 迁移、Magic Notes、数据库专项复跑 **135 项通过**(30 秒单测上限);此前一个
|
||||
listTasks 测试超时,独立复跑通过。加强后的迁移与笔记专项 **32 项通过**。
|
||||
全量测试此前被取消,候选全量验证和 CI 尚待完成。
|
||||
|
||||
## 2026-09-19 问答位置持久化
|
||||
|
||||
- Main 在收到问题时保存消息位置块,而不是等回答成功才追加;前台及重启恢复的实时待答
|
||||
快照、带来源的语义事件投影共用同一去重函数。答案写入不移动位置块,旧消息不补造历史
|
||||
顺序;规则见[Runtime 交互边界](./runtime-interactions.md#运行与失败)。未改变 schema。
|
||||
- `npm exec -- vitest run src/main/assistant/assistant-database.test.ts src/main/ipc.test.ts`
|
||||
通过 227 项,覆盖真实 SQLite 的问题/正文/工具交错、重复和重放、回答/跳过、完成及
|
||||
重新打开,以及三个设置重载入口的并行远程问题恢复。
|
||||
`npm exec -- tsc --noEmit -p tsconfig.node.json` 通过。
|
||||
- 共享 Linux x64 Host 使用已保存凭据与固定 Host Key,经 LAN 路由运行当前源码 Agent、
|
||||
Desktop Runtime 适配器和 Main SQLite 投影,使用真实 OpenCode `1.18.29`。首轮暴露实时
|
||||
问题没有语义来源的实际分支,使用 1 次真实文本模型请求后停止;补齐 Main 接收入口后,
|
||||
最终一轮 2 次请求,验证回答前位置块已经落库,回答后为 `tool → question → text`,
|
||||
完成重开后消息完全一致。合计真实文本模型调用 **3 次**,每次最多 1024 输出 token,
|
||||
每轮有限调用预算与 120 秒请求超时;图片模型调用 **0 次**。
|
||||
- 临时复验脚本为既有批准临时目录中的 `question-order-host-build.cjs`、
|
||||
`question-order-host-run.cjs`,最终日志为 `question-order-host-live.log`。
|
||||
两轮均停止测试 daemon,所属进程剩余 0,移除本轮专属 Host 目录及上传文件。
|
||||
该记录只覆盖 Main/Agent 路径,不代替 Renderer 视觉验收或最终全量验证。
|
||||
|
||||
## 2026-09-19 远程 CN 完整桌面验证
|
||||
|
||||
默认联合开发包已包含 OC/CN,并通过桌面导入、验签、传输至共享 Linux x64、安装及
|
||||
CN 项目创建。真实输入框发送产生清单更新,顶部和消息保存一致;退出重启后恢复完成
|
||||
与待办状态。修复实测发现的 CN 冷会话续发及重复序号问题,复验重启续发、点击停止和
|
||||
取消后再次发送通过。证据、调用口径及平台边界见
|
||||
[远程记录](../remote-host/runtime-checklist-validation.md#2026-09-19-联合包与完整桌面验收)。
|
||||
|
||||
任务与清单分别展开,容器高度有界,键盘可进入清单滚动区;状态图标不再影响文字
|
||||
基线。此前真实 Electron 视觉矩阵覆盖同时展开、长列表、窄窗口、浅深主题及 200% 缩放。
|
||||
本轮实际桌面截图进一步核对真实远程清单和取消状态。开发包未发布,非 x64 平台未实测。
|
||||
|
||||
远程 CN 真实长清单为 55 项、5,601 字符,展示与落库一致;清空后退出重启仍保持空数组。
|
||||
本轮 Agent gateway 账本记录 16 次模型请求,其中 15 次完成、1 次取消结果未确认。
|
||||
测试 Host 目录、所属进程及本地隔离 profile 已清理。全量 4707 passed、67 skipped、
|
||||
1 failed;本地 OC 压缩失败项与远程 ACP、项目选择、清单组件专项复验共 136 passed。
|
||||
最终 typecheck、lint 和差异检查通过。
|
||||
|
||||
## 2026-09-18 Remote Continue Checklist
|
||||
|
||||
CL-P1、CL-P3 的远程 CN 源码已接入受管包、HTTP 宿主到 ACP 的适配、模型桥、项目
|
||||
校验和聊天选择。CN 原生成功 Checklist 经既有 semantic transcript 传递统一清单,
|
||||
不增加数据库或重连协议。修复聊天页把远程 CN 选择恢复为 OC、隐藏 Continue 菜单,
|
||||
以及复合包验包默认读取 OC payload 的遗漏。
|
||||
|
||||
Linux x64 当前源码与开发包通过验包、prepare/commit、Agent bootstrap/health、CN
|
||||
激活、真实模型清单新增/清空、未 ACK 断线重放、取消和同对话 Ask 续发。本轮新增
|
||||
17 个外部模型 HTTP 请求;精确分项、临时探针、已清理的 Host 目录及剩余验收见
|
||||
[远程执行清单验证记录](../remote-host/runtime-checklist-validation.md#cn-部署与生命周期)。
|
||||
本阶段使用单 Runtime 开发包;后续联合包与完整桌面验证见上方 2026-09-19 记录。
|
||||
|
||||
远程相关测试 27 文件、374 项通过;最终项目选择、顶部组件和安装管理 41 项、App
|
||||
远程选择与继承 3 项、CN 复合包安装专项通过。typecheck 和 lint 通过。
|
||||
全量测试完成一轮:4665 passed、67 skipped、6 failed。其中 CN 新增包测试的文件名
|
||||
错误已修正并复验;其余 5 项为 ActivityPanel/activity-store 的引用复用断言,本轮未
|
||||
修改这些文件。最终 typecheck、lint 和专项结果见上述远程记录。
|
||||
并行工作区更新后,ActivityPanel/activity-store 的 39 项专项复验全部通过。
|
||||
|
||||
## 2026-09-18 Local Runtime Checklist Backend
|
||||
|
||||
CL-P1 and the backend portion of CL-P2 are implemented. Shared contracts use
|
||||
`runtime-checklist.ts`, `AgentEvent.type = checklist`, and message field
|
||||
`runtimeChecklist`. Local OC validates parent Session, submitted message identity,
|
||||
assistant parent identity and native tool arguments. CN extracts before truncation,
|
||||
commits successful calls once, excludes startup history and retains explicit clears.
|
||||
Message metadata serialization, reconstruction, remote replay deduplication and
|
||||
terminalization preserve the field without a database migration. Existing IPC
|
||||
transport already forwards these events. Renderer and `acp-remote-runtime` were
|
||||
outside this backend change; no shared Linux Host acceptance is claimed here.
|
||||
|
||||
The focused backend suite passed 275 tests across shared checklist, OC/CN Runtime,
|
||||
host adapter, permissions, real-binary lifecycle and assistant database tests.
|
||||
Coverage includes OC reused Sessions and stale/child updates, native event ordering,
|
||||
CN long lists and omitted success arguments, failure preservation, history dedupe,
|
||||
empty lists, database reopen and remote event replay/terminalization.
|
||||
`npm run typecheck` and `npm run lint` passed on the final validation run.
|
||||
The full `npm test` run completed with 4604 passed, 67 skipped and 9 failed:
|
||||
one Agent offline-package inventory test exceeded 60 seconds; eight failures were
|
||||
in concurrently changing Magic Notes renderer tests (including PDF `DOMMatrix`
|
||||
availability and canvas UI expectations). These files were not modified here.
|
||||
|
||||
Real model evidence on Windows, using production local adapters and isolated
|
||||
temporary profiles:
|
||||
|
||||
| Runtime | Version | Submitted tasks | Actual model HTTP POSTs | Observed result |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| CN | 1.5.47 | 1 | 3 | Two successful Checklist calls, two structured snapshots; A pending then completed, B pending |
|
||||
| OC investigation | 1.18.29 | 2 | 6 | Native tools succeeded; revealed todo.updated precedes running input, initially no projected snapshot |
|
||||
| OC corrected ordering | 1.18.29 | 1 | 3 | Two native updates projected; done retained |
|
||||
| OC final sortable message ID | 1.18.29 | 1 | 3 | Two native updates projected; assistant parent IDs matched; done retained |
|
||||
|
||||
Total: **15 actual external model HTTP POSTs**, all HTTP 200, **5 submitted tasks**.
|
||||
The unavailable old OC server probe and two isolated startup attempts dispatched
|
||||
zero model requests. Startup attempts were blocked by the probe's fetch wrapper
|
||||
not accepting Request objects; correcting that wrapper required no product change.
|
||||
The proxy allowed only the exact native checklist tool, with a per-run request cap.
|
||||
No business-file tools ran. Saved production settings were read through isolated
|
||||
credential handling; the existing explicit test connection was used in memory
|
||||
because its endpoint/model tuple differed from the saved connection. Credentials
|
||||
were not printed, original settings were unchanged, and isolated run data was removed.
|
||||
|
||||
The real probes verified adapter extraction and OC Runtime events, not desktop
|
||||
rendering. CN Runtime event forwarding and persistence were verified by tests.
|
||||
Long lists, clears, cancellation boundaries and history replay were tested with
|
||||
fixtures/database tests, not exhaustively exercised through real models. Temporary
|
||||
probe sources: `cn-todo-probe.ts` / `cn-todo-run.cjs` under the approved OpenCode
|
||||
temporary directory; each run printed its dispatch count and structured results.
|
||||
|
||||
## 2026-09-17 连续请求取消与停止状态
|
||||
|
||||
- 复现并修复 OpenCode 迟到的会话取消中断下一条请求:同一次运行复用一个有界 abort,
|
||||
等待取消结束后释放会话锁;取消失败时移除对应绑定。规则见
|
||||
[本机 OpenCode](./runtime-process-reuse-technical-design.md#4-本机-opencode)。
|
||||
- 复现并修复旧 streaming 快照覆盖本地终态后停止按钮残留。完成、取消两种回归在
|
||||
修复前失败,修复后通过;连续两条消息结束后按钮恢复。规则见
|
||||
[消息底部请求状态](./runtime-interactions.md#消息底部请求状态)。
|
||||
- App 257 项测试通过;最终 OpenCode Runtime、权限与真实 lifecycle 105 项通过。
|
||||
lifecycle 使用真实 OpenCode 二进制与 SDK、本地模型 fixture,验证延迟取消后同会话
|
||||
下一条正常完成及 SSE 回收。该轮外部真实模型调用 0 次;后续完整 App 实测见下文。
|
||||
- `npm test`:4454 项通过、67 项跳过、2 项失败;失败均为 heartbeat 迁移的
|
||||
`duplicate column name: pinned`。`npm run typecheck`、修改文件定向 ESLint、
|
||||
`git diff --check` 通过;全库 lint 因范围外 `docs/features/story-graph/demo.js`
|
||||
的 22 项 `document` 未定义错误失败。
|
||||
- 托管 SSH 经 `ManagedRemoteAcpRuntime` / `AgentOwnedAcpPrompt` 使用独立 ACP
|
||||
取消流程,不复用此 HTTP abort 实现;本次未改 Agent 或远程协议,未做共享 Host 实测。
|
||||
|
||||
### Real-model follow-up
|
||||
|
||||
Current-source production Main, Preload, and Renderer were built into dedicated
|
||||
temporary output and launched in an isolated Electron profile on Windows. Native
|
||||
input submitted two queued OpenCode Ask requests, then cancelled another request
|
||||
after external HTTP dispatch and clicked resend within 25 ms. Both queued answers
|
||||
and the resend completed and were persisted. The cancellation was also persisted.
|
||||
After each scenario, the real DOM had no Stop button; Send was enabled after
|
||||
entering an unsent readiness probe. Both terminal screenshots were reviewed.
|
||||
|
||||
The instrumented run dispatched exactly four external text-model HTTP requests:
|
||||
three returned HTTP 200 and one was cancelled before response headers. There were
|
||||
no observed retries or tool executions. A transparent Main relay forwarded to the
|
||||
unchanged configured HTTP upstream to count child-process requests; model answers
|
||||
and events were not simulated. An initial driver attempt completed two additional
|
||||
real generations before its counting hook was found not to cover child traffic.
|
||||
The total external dispatch count across both attempts is therefore unknown.
|
||||
|
||||
This verifies the observed full-App flows, not deterministic stale-event ordering,
|
||||
remote Hosts, or restart behavior. Isolated credentials, profile, build, and owned
|
||||
processes were cleaned up; original settings and target source hashes were unchanged.
|
||||
The follow-up focused suite passed all 362 tests, and `npm run typecheck` passed.
|
||||
Redacted evidence: [real-model report](./consecutive-requests-live-2026-09-17.json).
|
||||
|
||||
## 2026-09-15 Managed browser popups
|
||||
|
||||
Implemented [page-created tabs](./browser-tabs-technical-design.md#62-page-created-tabs): native
|
||||
Electron popup creation now registers a managed Tab, and click results update direct-tool and MCP
|
||||
bindings. Renderer selects a visible opener's popup across chat switches and removes script-closed
|
||||
popup instances. Existing worktree changes were retained.
|
||||
|
||||
Validation against the current source:
|
||||
|
||||
- `npx vitest run src/main/browser src/main/agent/knowledge-mcp-gateway.test.ts src/main/agent/model-tool-provider.test.ts src/renderer/src/RightAssistantSidebar.resize.test.tsx src/shared/contracts.test.ts`: 10 files, 232 tests passed.
|
||||
- `node build/run-browser-tabs-electron-e2e.cjs`: passed twice after making the test wait for the shown
|
||||
native view's animation frames before sending mouse input. Real Chromium verified `target=_blank`,
|
||||
`window.open(url)`, blank-then-location, script close, opener close, shared partition, unsupported URL
|
||||
denial, and subsequent direct/MCP snapshots on the popup. No fixed click delay was added to production.
|
||||
- `npm run typecheck` and `npm run lint`: passed. `git diff --check`: no whitespace errors.
|
||||
- `npm test`: 4319 passed, 67 skipped, 6 failed across 5 files. Failures were Agent offline dependency
|
||||
installation timeout, local Runtime reuse timeout, diagnostic-log writer expectations, two heartbeat
|
||||
migration fixtures with `duplicate column name: pinned`, and an App external knowledge binding test
|
||||
missing the `Remote handbook` heading. The full suite was not green; none of these failures exercise
|
||||
the popup registration or binding path.
|
||||
|
||||
The Electron test uses production BrowserService, CDP and MCP with a local HTTP fixture; the workbar
|
||||
selection/removal path is covered by the actual Renderer component test. A complete installed-App
|
||||
visual test and macOS/Linux native popup checks were not run. No external model calls were made for
|
||||
the popup validation. Remote ACP exposes only its scoped image MCP server (`imageMcpServers`), so the
|
||||
deployed Agent has no separate affected browser implementation and no remote Host change was needed.
|
||||
|
||||
## 2026-09-15 会话置顶与浮动操作菜单
|
||||
|
||||
已核对当前源码差异:置顶经专用 IPC 写入 SQLite,schema 36 迁移保留已有会话;会话操作
|
||||
改为 body Portal 菜单。规则分别见[会话置顶](./execution-history-storage.md#会话置顶)和
|
||||
[菜单界面](../application-tool-navigation/ui-design.md#会话置顶与操作菜单)。
|
||||
|
||||
本次任务提供的已验证聚焦测试结果为 backend **135 项通过**、renderer **283 项通过**。
|
||||
本轮仅更新文档,未重新运行测试;任务未提供这两组的完整命令或原始日志路径。
|
||||
已核对 `assistant-database.test.ts` 与 `App.test.tsx` 中相关回归,覆盖持久化与重开、
|
||||
schema 35 升级、最近 100 条之外的置顶、自动保存隔离、草稿先保存、通道会话、失败保留
|
||||
状态、旧刷新竞态,以及 Portal、窗口边界、键盘与外部关闭。
|
||||
|
||||
原生 Electron 视觉与完整应用交互尚未验证,不能据组件测试声明实际菜单排版、原生浏览器
|
||||
遮挡或跨主题/窗口尺寸视觉验收通过;本轮也未运行全量测试、类型检查、lint 或构建。
|
||||
|
||||
## 2026-09-15 工作栏标签标题国际化修复
|
||||
|
||||
实现与兼容规则见[多终端页签 PRD 第 10 节](./terminal-tabs-prd.md#10-实例session-与持久化)。
|
||||
任务中心、工作区、成果和带默认编号的终端标题由 Sidebar 按当前语言派生;重命名清除默认
|
||||
编号。浏览器标题保持原行为,未修改工作栏壳层。本次只涉及桌面标题显示与布局描述,不改变
|
||||
本机或远程 PTY、Runtime、Agent 协议或桌面到 Agent 的执行路径。
|
||||
|
||||
聚焦验证:
|
||||
|
||||
- `npx vitest run src/renderer/src/RightAssistantSidebar.resize.test.tsx src/shared/workbar-contracts.test.ts src/renderer/src/WorkbarShell.test.tsx`:3 个文件、74 项通过。
|
||||
- Sidebar 新增 6 项行为回归,覆盖中英切换且不改写布局、默认终端编号持久化与跨语言恢复、
|
||||
重命名后恢复,以及中文默认名、英文默认名和自定义名三种无标记旧终端。测试使用真实
|
||||
Sidebar、WorkbarShell 和布局 Schema,终端面板使用测试替身,未启动 Shell。
|
||||
- `npx eslint src/renderer/src/RightAssistantSidebar.tsx src/renderer/src/RightAssistantSidebar.resize.test.tsx src/shared/workbar-contracts.ts`:通过。
|
||||
- 首轮测试有一处新增浏览器文案断言错误,修正为现有“未绑定会话”后通过。按用户要求未运行
|
||||
全量测试、全量 typecheck、全量 lint 或构建;本轮未进行 Electron 实机验证。
|
||||
|
||||
## 2026-09-15 Desktop 0.13.6 发布准备
|
||||
|
||||
用户批准两项性能修复的中英文更新说明,指定 Desktop 0.13.6,不发布 Agent 或
|
||||
@@ -1056,13 +1279,38 @@ src/agent-daemon/runtime-composition.test.ts src/main/ipc.test.ts` 为 179 项
|
||||
测试应用与具名浏览器会话已退出,所属 Electron/Node 进程及 fixture 监听端口均为 0;
|
||||
临时截图和隔离 profile 保留在本机测试目录,不进入仓库。
|
||||
|
||||
## 2026-09-16 任务卡内审批
|
||||
|
||||
- 任务中心按 [Task Center 交互规则](../task-and-job/task-center-prd.md#5-交互) 展示卡内审批,
|
||||
未关联审批保留处理入口;没有审批时不渲染空审批区域。
|
||||
- 当前定时运行从队列 `taskId` 记录到 `ActiveRun`,审批事件再写入消息的临时审批数据;
|
||||
历史消息使用明确的 `message.task.id`。Sidebar 按任务 ID 匹配当前范围内顶层任务,
|
||||
并核对会话 ID 和项目 ID,不按会话、标题、时间或计划猜测运行归属。
|
||||
- `RightAssistantSidebar.resize.test.tsx` 的 54 项测试通过,覆盖项目范围、状态筛选、
|
||||
卡内处理、无审批、普通会话审批及关联不一致。App 使用 `-t "approval|scheduled instructions"`
|
||||
定向运行的 14 项测试通过,验证历史与当前定时审批的映射、允许/拒绝接口调用及成功移除。
|
||||
- `npm run typecheck`、`npm run lint` 通过。本次仅修改 Renderer 展示与临时关联数据,
|
||||
本机和远程审批继续使用原有接口;未改 Main、Agent 或存储协议。未进行真实 Electron
|
||||
窗口和模型调用验收;按本次要求未运行全量测试及无关数据库迁移测试。
|
||||
|
||||
## 2026-09-16 任务中心范围简化
|
||||
|
||||
- 任务中心仅保留“当前项目”和“所有项目”;无项目任务在后者中标注“未绑定项目”。具体规则见
|
||||
[Task Center 范围](../task-and-job/task-center-prd.md#4-范围)。
|
||||
- Renderer 将旧布局中的 `global` 恢复为 `all-projects` 并保存新值;共享解析契约、后端与任务
|
||||
数据不变。App 仍只向任务中心传入 schedule 来源的顶层任务。
|
||||
- `npx vitest run src/renderer/src/RightAssistantSidebar.resize.test.tsx src/shared/workbar-contracts.test.ts`
|
||||
通过 60 项;`npm run typecheck` 和 `npm run lint` 通过。未进行真实 Electron 窗口验收。
|
||||
- `npm test` 在 200 秒后超时,未取得整库结果;单独复跑 `heartbeat-database.test.ts` 为 6 项通过、
|
||||
2 项失败,均在未修改的数据库迁移代码中报 `duplicate column name: pinned`。
|
||||
|
||||
## 2026-09-10 固定基础页签与浏览器多实例
|
||||
|
||||
- 工作栏注册表已分别声明实例策略、默认上下文、默认打开、必须存在、可关闭和排序属性。
|
||||
任务中心与工作区保持默认存在且不可关闭;浏览器改为当前 Conversation 上下文的多实例。
|
||||
布局加载按 `required` 补回缺失的基础页签,并修复失效活动实例。
|
||||
- 任务中心增加“当前项目”“全局任务”“全部项目”范围,默认跟随活动项目;任务和审批按
|
||||
Conversation 所属项目过滤,范围模式随工作栏布局持久化。
|
||||
Conversation 所属项目过滤,范围模式随工作栏布局持久化。范围选项已于 2026-09-16 简化,见上方记录。
|
||||
- 浏览器服务已拆分 Conversation Context 与 Browser Tab。每个 Tab 拥有独立页面、Driver、
|
||||
操作队列、状态和元素引用空间,同一 Conversation 的 Tab 共享 partition、代理和 Cookie。
|
||||
- Renderer 使用工作栏实例 UUID 原子创建或恢复 Browser Tab,并以 UUID viewport token 防止
|
||||
|
||||
@@ -1,16 +1,38 @@
|
||||
# Runtime 执行清单技术设计
|
||||
|
||||
状态:设计阶段,尚未实现。需求、场景与顶部布局由 [PRD](./prd.md#131-runtime-执行清单)
|
||||
状态(2026-09-19):本地与远程 OC/CN 提取、统一事件、消息保存和顶部展示已实现。
|
||||
默认开发组包同时携带 OC/CN,远程 CN 已通过 Windows 桌面到 Linux x64 的真实模型
|
||||
更新、取消、重启恢复及续发验证。验证范围与调用数见
|
||||
[远程记录](../remote-host/runtime-checklist-validation.md#2026-09-19-联合包与完整桌面验收)。
|
||||
需求、场景与顶部布局由 [PRD](./prd.md#131-runtime-执行清单)
|
||||
维护,替换、隔离和终态规则由 [Runtime 交互边界](./runtime-interactions.md#runtime-执行清单)
|
||||
维护。研究证据与交付状态见[进度](./progress.md#2026-09-15-runtime-执行清单设计未实现)。
|
||||
|
||||
## 1. 原生数据与提取位置
|
||||
|
||||
Implemented local behavior:
|
||||
|
||||
- OC sends a native-sortable `messageID` with both prompt and command submissions.
|
||||
Only assistant messages whose `parentID` matches that request can own checklist
|
||||
calls. Parent Session identity is checked independently of child-session routing.
|
||||
OC 1.18.29 emits `pending`, `todo.updated`, then `running` with complete arguments.
|
||||
The adapter retains native snapshots only while an owned call is active and
|
||||
submits a snapshot once its validated items match that call's original input.
|
||||
Calls and pending snapshots are request-local; no `session.todo` polling is used.
|
||||
- CN extracts the exact `Checklist.checklist` argument in the patched native start
|
||||
callback and in native history before display truncation. Candidates survive
|
||||
success frames without input. A completed call commits once per call ID; failed
|
||||
calls and startup history do not commit. Ordinary tool events retain their limits.
|
||||
- CN accepts blank lines and `- [ ]`, `- [x]`, `- [X]` entries with nonempty content.
|
||||
Empty or whitespace-only strings clear the list. Other nonempty lines invalidate
|
||||
the entire update. Native 1.5.47 accepts a string without Markdown validation;
|
||||
these are GoodBuddy's explicit projection rules, not additional native guarantees.
|
||||
|
||||
| Runtime / 路径 | 已确认格式与设计接入 |
|
||||
| --- | --- |
|
||||
| 本地 OC 1.18.29 | `todo.updated` 与 `session.todo` 返回完整条目列表,字段为 `content/status/priority`,无 item ID;以原生更新事件驱动本轮整份替换 |
|
||||
| 本地 CN 1.5.47 | 精确原生工具 `Checklist` 的参数 `checklist` 是 Markdown 勾选列表,`- [ ]` / `- [x]` 表示未完成/完成;按一次成功调用提交完整替换 |
|
||||
| 远程 OC/CN | 先核对实际部署版本、原生输出与 Agent 传递结果;只有格式已确认且来源归属明确的原生 plan entries 或精确清单工具参数可以映射,当前尚无本功能远程实测 |
|
||||
| 远程 OC/CN | OC 1.18.29 通过原生 todo.updated,CN 1.5.47 通过成功 Checklist 参数进入 Agent transcript;Linux x64 清单与重放已实测,CN 同时验证取消后续发,见[远程记录](../remote-host/runtime-checklist-validation.md) |
|
||||
|
||||
`opencode-runtime.ts` 当前将普通工具 input 经 `boundedToolDetail(..., 4_000)` 转成
|
||||
展示字符串。接入应在 SDK 原始事件层消费 `todo.updated`;如需核对 `todowrite` 参数,
|
||||
@@ -24,23 +46,29 @@ CN 的提取位置在 `continue-host-adapter.ts`:宿主产生工具事件、HT
|
||||
流转。超出 4,000 字符的清单需要独立验证完整性,但不扩大所有普通工具输入的展示上限。
|
||||
|
||||
Markdown 解析只适用于已确认的 CN `Checklist.checklist`,不扫描助手正文或其他工具
|
||||
输出。空数组在统一事件中明确表示清除;CN 原生如何表达空清单,以及空白、大小写和
|
||||
非条目行的实际接受规则,实施时核对后固定解析规则。未确认或无效输入保留旧清单,
|
||||
不能误当清空。
|
||||
输出。空数组在统一事件中明确表示清除;CN 空字符串或仅空白的字符串表示清空。
|
||||
解析规则见上文;无效输入保留旧清单,不能误当清空。
|
||||
|
||||
## 2. 统一事件与消息归属
|
||||
|
||||
拟在现有 Runtime 事件联合中增加完整清单事件,承载 `requestId`、条目数组和原生来源;
|
||||
条目包含 `content`、`status` 及可选 `priority`。最终命名随实现确定,不设计条目级增删
|
||||
协议或稳定 item ID。空数组必须经过 schema、IPC、消息投影和保存链路,不能被“无内容”
|
||||
过滤丢弃。
|
||||
The shared contract is `src/shared/runtime-checklist.ts`: `runtimeChecklistSchema`
|
||||
and `RuntimeChecklist { source: 'opencode' | 'continue', items }`. Each item has
|
||||
`content`, `status: pending | in_progress | completed | cancelled`, and optional
|
||||
`priority: low | medium | high`. `AgentEvent` carries
|
||||
`{ type: 'checklist', requestId, checklist }`; conversation messages carry optional
|
||||
`runtimeChecklist`. Empty arrays are valid replacements. Existing IPC event
|
||||
transport forwards them without a separate channel or tool-input parsing.
|
||||
|
||||
Main 使用现有请求到助手消息的映射投影清单,Renderer 顶部读取该消息的最新有效状态。
|
||||
本地 Renderer 按请求映射更新助手消息并沿既有保存入口落库;远程 Main 使用既有事务
|
||||
投影更新消息。顶部读取所属请求消息的最新有效状态。
|
||||
同一请求若同时观察到原生清单事件与其工具记录,只以前者更新清单,后者保留活动;不能
|
||||
提交两次替换。原生父 Session、请求开始/结束边界需共同校验,子 Session 或旧请求事件
|
||||
不得通过当前活动请求映射到新轮。
|
||||
|
||||
优先扩展现有消息 metadata、Runtime 事件和数据库投影,历史清单与所属助手消息一起保存。
|
||||
The existing message metadata serializer and every database message reconstruction
|
||||
retain `runtimeChecklist`, including remote event projection and terminalization.
|
||||
No SQLite schema migration or separate checklist store is needed.
|
||||
历史清单与所属助手消息一起保存。
|
||||
远端沿用事件提交、消息投影和 ACK 的既有事务及去重规则;相关存储边界见
|
||||
[执行记录存储](./execution-history-storage.md)。实施前核对本地会话与远程任务实际落库
|
||||
字段,选择能复用的最小表示。不新建清单数据库、独立快照系统、恢复日志或持久化服务。
|
||||
@@ -48,16 +76,16 @@ Main 使用现有请求到助手消息的映射投影清单,Renderer 顶部读
|
||||
|
||||
## 3. 远程 Agent 与 ACP 复用
|
||||
|
||||
本次源码检查确认以下传递位置,属于设计依据,不是本功能远程验证结果:
|
||||
远程传递复用以下实现:
|
||||
|
||||
1. `AgentAcpConnection` 按原生 `sessionId` 分派 `sessionUpdate`;
|
||||
`AgentOwnedAcpPrompt` 校验 Session 后,以 `bindingId/operationId` 将通知写入
|
||||
既有 semantic transcript。
|
||||
2. `ProtocolRemoteRuntimeChannel` 已有 transcript 读取/ACK、身份核对,以及 ACP
|
||||
channel 恢复与重放;`AcpRemoteRuntime` 消费 `session-update` 并转换桌面事件。
|
||||
3. `AcpRemoteRuntime` 当前将 `plan.entries` 及 `plan_update` 的 items 转成 `status`
|
||||
字符串;markdown/URI 也只形成状态消息。工具事件按 `toolCallId` 合并 `rawInput`、
|
||||
`status` 和结果,再有界保留。清单提取应发生在结构丢失或截断前。
|
||||
3. OC 插件转发原生 `todo.updated`,CN 宿主将成功清单写入 `_meta.goodbuddyChecklist`;
|
||||
`AcpRemoteRuntime` 校验归属和结构后产生统一清单事件。标准 OC `plan.entries` 也有
|
||||
映射,但 OC 1.18.29 实测走插件事件。Markdown、URI 和普通状态文字不生成清单。
|
||||
|
||||
优先复用上述通知和 transcript,不另建远端清单通道。对实际 OC/CN 部署确认过的
|
||||
`plan.entries`,或已确认等价的 `plan_update` items,校验条目字段和状态后转成统一
|
||||
@@ -69,17 +97,25 @@ Main 使用现有请求到助手消息的映射投影清单,Renderer 顶部读
|
||||
真实格式证据,不能根据 ACP 类型定义推断 OC/CN 一定会发送。ACP 是协议,Runtime 身份
|
||||
仍为 OC 或 CN。
|
||||
|
||||
当前托管 SSH 产品及受管安装、校验、创建链路只支持 OC,详见
|
||||
[远程范围](../remote-host/technical-design.md#执行清单接入范围)。远程 CN 交付需补齐
|
||||
受管工件、安装与启动、项目选择/校验及原生事件接入,确认 Main 模型桥、请求归属、取消
|
||||
和已有恢复流程能用于该部署。不能只放开选择菜单或使用本地 CN 调远程模型来宣称完成。
|
||||
受管安装、校验、项目创建与聊天选择已接入 CN;Agent 在 Host 运行固定 CN HTTP 宿主,
|
||||
复用本地适配器和已有模型桥、请求归属、取消及恢复。开发包与 Linux 验证、联合分发和
|
||||
完整桌面验收的剩余范围见[远程记录](../remote-host/runtime-checklist-validation.md)。
|
||||
|
||||
重连按[既有 ACP 与断线规则](../remote-host/technical-design.md#acp-与断线)执行。
|
||||
已接受 Prompt 使用原 binding/operation 恢复,清单从已提交消息和其后的既有事件恢复;
|
||||
重复事件不重复展示。连接丢失不表示条目完成,不为恢复清单重新发送 Prompt、模型请求或
|
||||
工具调用,也不新增独立重连机制。
|
||||
|
||||
## 4. 实施与验收
|
||||
## 4. 顶部布局
|
||||
|
||||
`ConversationTaskStrip` 与 `RuntimeChecklistStrip` 共用顶部容器,任务在上、清单在下,
|
||||
各自展开和折叠。没有 Task 时清单仍可显示;远程会话也保留清单入口。总高度不超过
|
||||
45vh,各自展开内容不超过 20vh 并内部滚动,标题不收缩。清单内容区可用键盘聚焦和滚动。
|
||||
|
||||
状态文字与条目首行按文字基线对齐,状态图标单独居中。CN 不推断进行中或优先级。
|
||||
新轮先选择本轮消息,再读取清单,不回溯寻找旧轮非空清单。后台更新保持展开状态和焦点。
|
||||
|
||||
## 5. 实施与验收
|
||||
|
||||
| 阶段 | 必须完成的工作 | 对应需求 |
|
||||
| --- | --- | --- |
|
||||
@@ -88,7 +124,7 @@ Main 使用现有请求到助手消息的映射投影清单,Renderer 顶部读
|
||||
| CL-P3 | 确认远程 OC 原生格式,补齐远程 CN 真实部署与事件接入,复用 Agent 传递和恢复 | FR-CL1、FR-CL4 |
|
||||
| CL-P4 | 本地与共享 Linux 真实 Host 逐项验收,记录版本、结果、调用口径与剩余阻塞 | FR-CL1–FR-CL4 |
|
||||
|
||||
以下均为待执行验收,本次文档工作不运行模型或代码测试:
|
||||
以下为产品验收范围;已执行项和未覆盖项分别记录在进度与远程验证记录中:
|
||||
|
||||
- 本地 OC/CN 各经桌面正常发送路径产生并连续修改清单,检查新增、修改、重排、删除、
|
||||
重复文本、有效清空及超过 4,000 字符的完整内容;CN 成功才替换,失败保留前值。
|
||||
|
||||
@@ -29,6 +29,10 @@ Host 环境。2026-09-14 的已发布 Desktop `0.13.2` / Agent `0.11.25` 已包
|
||||
- 回答或跳过成功后,会话保留只读的“问题与回答”,显示原问题及对应的选项答案、
|
||||
自定义文本或“已跳过”。同一任务的多轮问答逐次保留,随本地会话保存,重新打开后仍可查看。
|
||||
提交失败时保留输入与重试入口,不生成已回答记录;取消未回答的问题不记为主动跳过。
|
||||
- 新消息在提问到达时,将只含 `questionId` 的问题位置块插入当时的消息块序列;
|
||||
待答和回答后的卡片使用同一位置,后续正文、工具输出不再排到问题前面。重复问题不追加
|
||||
或移动位置块。位置块不保存待答表单;旧会话没有位置块时保留原有问答展示,不推测或
|
||||
回填历史顺序。该变更复用既有消息元数据,不升级数据库 schema。
|
||||
- 提交响应晚于任务结束时仍保存已提交的问答,但不覆盖完成或失败状态;晚于下一轮提问时
|
||||
不清除新的待答卡片。旧版会话未保存的问题与答案无法从“已提交”状态文案恢复。
|
||||
- 待答队列仅保存在当前桌面会话内存中,不写入持久会话快照。刷新仍在运行的消息时保留整个
|
||||
@@ -38,6 +42,8 @@ Host 环境。2026-09-14 的已发布 Desktop `0.13.2` / Agent `0.11.25` 已包
|
||||
- 未发布的进程复用源码在 Desktop 重启后由 Main 并行附加同项目的存活任务,将待答
|
||||
列表和 Stop 所需的请求 ID 投影到实时 `activeRequest`,不保存该字段或重新发送任务。
|
||||
回答/跳过成功由 Main 写回原远程助理消息的既有问答历史,Renderer 不覆盖远程正文或终态。
|
||||
Main 在前台与恢复请求收到实时问题时立即保存位置块;实时待答快照没有语义事件来源,
|
||||
不伪造来源或追加 transcript。带来源的提问事件仍通过既有事务投影并按问题 ID 去重。
|
||||
Runtime 设置、定制和能力更新让旧请求在原配置上结束,已有问题仍可回答;新请求使用
|
||||
新配置。专家团队例外:既有专家调度器在设置替换时取消子任务,团队父请求也随之停止,
|
||||
不使用替换后的模型汇总旧结果。详细实现见
|
||||
@@ -92,6 +98,8 @@ DeepSeek Harness 按调用 ID 保留名称、输入和摘要,结束事件省
|
||||
正文、推理或工具进展会清除旧状态;子任务更新不覆盖父请求的重试提示。
|
||||
- 完成、失败、取消后保留真实终态,迟到的状态事件不能把请求恢复成处理中。底部状态点
|
||||
为静态,不凭请求尚未结束暗示仍有进展;会话列表闪点和发送、停止按钮保持原有行为。
|
||||
持久化快照中的同一条消息仍为生成中时,也不能覆盖本地已收到的终态,即使快照的
|
||||
会话更新时间更晚;否则连续发送或刷新会让已结束的请求重新显示停止按钮。
|
||||
- Continue、DeepSeek Harness 和远端 ACP 保留其已接入的处理与工具事件,不伪造未接入
|
||||
的原生重试状态。直连模型的重试等待与请求开始文案见
|
||||
[直连模型 UI 设计](../direct-model-agent/ui-design.md#6-错误与通知);本次不改变任何
|
||||
|
||||
@@ -159,6 +159,10 @@ SelectedRuntimeManager
|
||||
|
||||
会话绑定记录规范化绝对目录及原生 sessionId。同一 conversation 的请求继续串行,
|
||||
不同 conversation 并行,不引入覆盖整个 Server 的 Prompt 队列。
|
||||
取消或失败时,每次请求只发送一次会话级 abort,并在该调用结束后才释放会话锁,避免
|
||||
迟到的 abort 中断下一条请求。abort 使用既有控制请求超时;失败或超时后移除对应绑定,
|
||||
下一条请求创建新 Session。这也适用于配置外部 OpenCode HTTP Server 的路径;托管 SSH
|
||||
使用独立的 ACP 取消路径。
|
||||
现有 `snapshot: false` 必须保留。
|
||||
|
||||
`mcpMutationTail` 目前属于 Runtime 对象;共享后同一原生目录的变更必须由同一 owner
|
||||
@@ -227,8 +231,9 @@ Agent 当前 Runtime installation + 兼容配置
|
||||
竞争读取同一 stdout。消息和 permission 按 sessionId 分派给 binding 的当前 operation。
|
||||
- `AgentAcpConnection` 统一连接级握手、stdio 解码与退出监听,Prompt
|
||||
生命周期仍留在 binding。同一 Session 串行,其他 Session 不等待它完成。
|
||||
- `closeAcpChannel`、取消和 release 只释放所属 binding/Session;最后一个 binding
|
||||
离开后空闲 60 秒关闭进程。关闭 Session 时等待原生 `closeSession` 和原始 Prompt
|
||||
- `closeAcpChannel`、取消和 release 只释放所属 binding/Session;以活动 operation
|
||||
判断共享进程是否空闲,最后一个任务结束即回收,不因历史 binding 保留进程。
|
||||
待答问题仍占用活动 operation。关闭 Session 时等待原生 `closeSession` 和原始 Prompt
|
||||
结算,不等待排在 backend 控制队列后面的完成回调。只有原生控制面不响应时才有界
|
||||
终止共享进程,并向其余请求报告中断,不声称这类故障可以逐 Session 隔离。
|
||||
- terminal 与物理进程状态分开:取消/失败的 Session 也可返回
|
||||
|
||||
@@ -90,7 +90,8 @@ SSH 连接池,但尚不存在工作栏应用实例模型、终端 IPC、终端
|
||||
工作栏 Tab 是应用的一次打开实例。每个实例具有独立 ID、标题、应用类型和状态。Tab 行只
|
||||
显示已经打开的实例,不把全部能力同时渲染为 Tab。
|
||||
|
||||
实例持久化字段只有 `id`、`appId`、`title` 和可选 `targetRef`。`targetRef` 使用公开引用:
|
||||
实例持久化字段为 `id`、`appId`、`title`,以及可选的 `targetRef`、`defaultTitleNumber`。
|
||||
默认标题编号的保存和兼容规则见第 10 节。`targetRef` 使用公开引用:
|
||||
终端接受 `local` 或 `projectId`,浏览器接受 `conversationId`。Main 签发的 Browser Tab ID、
|
||||
Terminal Session ID、元素引用、凭据、路径和运行状态不进入工作栏布局。
|
||||
|
||||
@@ -178,9 +179,13 @@ stdio fallback。首期远程终端随 SSH Channel 结束,不提供跨 SSH 或
|
||||
- 已打开实例位于一个语义化 `tablist` 中。
|
||||
- 每个实例使用独立 `tab` 和 `tabpanel`。
|
||||
- “+”固定在 Tab 行末尾,但位于 `tablist` 之外。
|
||||
- Tab 过多时实例区域单行横向滚动,“+”保持可见;不换行、不自动隐藏实例。
|
||||
- Tab 过多时实例区域单行横向滚动,不换行、不自动隐藏实例。“+”紧随最后一个标签并随标签一起滚动,滚到末尾时可见。
|
||||
- 标签全部可见时隐藏左右滚动按钮;溢出时左右箭头透明浮动于工作区两端,不占标签布局宽度。鼠标移入标签栏时显示箭头,移出后隐藏;键盘聚焦箭头时显示,无悬停能力的设备保持可见。悬停箭头时显示轻微底色。点击滚动约一屏,边界方向禁用且不拦截下方标签的指针操作。切换或新增激活标签时将其滚入可见区域。未激活标签使用低对比度灰蓝底色和细边框,激活标签保留亮底与强调色顶线。
|
||||
- 终端 Tab 默认使用递增标题,例如“终端 1”“终端 2”;状态栏持续显示项目、Host、
|
||||
工作目录和 Shell。用户可以重命名终端。
|
||||
- 任务中心、工作区、成果及新建终端的默认标题随界面语言切换,恢复布局时也使用当前语言。
|
||||
终端自定义名称保持原文;旧布局中的终端名称保持原值,即使看起来像“终端 1”。浏览器
|
||||
标题沿用现有行为,不在本次标题国际化范围内。
|
||||
- 当前 Tab 同时使用连续内容表面、选中边框、顶部强调线、图标和较高字重,不能只靠文字颜色。
|
||||
- Tab、关闭按钮和“+”入口沿用共享控件圆角;终端面板外框和终端顶部工具栏按钮使用直角,
|
||||
保持紧凑的停靠式终端界面。
|
||||
@@ -345,6 +350,7 @@ type WorkbarTabInstance = {
|
||||
id: string
|
||||
appId: WorkbarAppDefinition['id']
|
||||
title: string
|
||||
defaultTitleNumber?: number
|
||||
targetRef?:
|
||||
| { type: 'local' }
|
||||
| { type: 'project'; projectId: string }
|
||||
@@ -362,6 +368,8 @@ type WorkbarLayoutPreferences = {
|
||||
```
|
||||
|
||||
- 持久化工作栏打开状态、Tab 顺序、活动实例、应用 ID、自定义标题、公开目标引用和宽度比例。
|
||||
- `taskScope` 中的 `global` 仅保留旧布局解析兼容;UI 恢复时映射为 `all-projects`,此后只保存
|
||||
`current-project` 或 `all-projects`。范围规则见 [Task Center PRD](../task-and-job/task-center-prd.md#4-范围)。
|
||||
- 加载布局时根据 `required` 补回缺失实例,并拒绝重复的单实例应用;应用策略不写入实例
|
||||
数据,避免持久化状态覆盖注册表。
|
||||
- `reorderable` 已进入共享契约;当前 Tab 壳层尚未提供拖放或键盘重排,不能把声明属性视为
|
||||
@@ -373,6 +381,12 @@ type WorkbarLayoutPreferences = {
|
||||
Shell。
|
||||
- 运行期间切换项目不会改变已经创建的终端目标。用户需要新目标时通过“+”创建新终端。
|
||||
|
||||
新建终端保存正整数 `defaultTitleNumber`,沿用同一目标已有终端数量加一的编号规则。
|
||||
Sidebar 在渲染时按当前语言派生应用和默认终端标题,再交给 Tab 壳层与终端面板;切换语言
|
||||
不改写持久化标题。用户重命名时清除此字段并保存输入的 `title`。缺少标记的旧终端直接显示
|
||||
原 `title`,不通过名称模式推断默认标题。布局存储键保持 `goodbuddy.workbar-layout.v1`;
|
||||
旧版本使用严格 Schema,不能读取带新增字段的布局,本变更不提供降级兼容。
|
||||
|
||||
## 11. 生命周期与关闭语义
|
||||
|
||||
| 用户或系统动作 | Tab | Shell Session |
|
||||
@@ -548,7 +562,7 @@ Schema、假数据终端或不可用入口的中间产品。
|
||||
- [x] 连续选择浏览器会创建页面、导航状态和元素引用相互独立的浏览器 Tab。
|
||||
- [x] 连续选择终端会创建多个互相独立的终端 Tab。
|
||||
- [ ] 关闭可关闭实例、切换和重排实例后,“+”仍保持可用。
|
||||
- [ ] Tab 溢出时单行横向滚动,不隐藏“+”或把 Tab 换成下拉框。
|
||||
- [ ] Tab 溢出时单行横向滚动,两端箭头固定,“+”跟随最后一个 Tab,滚到末尾可见,不把 Tab 换成下拉框。
|
||||
- [x] 关闭可关闭的活动 Tab 后激活正确的相邻 Tab 并恢复焦点。
|
||||
- [x] 当前 Tab 的背景、边框、顶部指示和字重清晰可辨;关闭按钮仅在悬停、焦点或无悬停
|
||||
设备上出现,且不引发布局位移。
|
||||
|
||||
@@ -71,7 +71,7 @@ Main 仅在图片和会话引用持久化成功后发布完成事件。素材必
|
||||
| GoodBuddy Agent 远程适用 Runtime | 复用桌面与 Agent 通信及工具桥接,将请求绑定原会话,由 Main 调用图片模型 | 逐一验证实际支持的远程 Runtime,覆盖真实 Host 工具往返、生成编辑、本地与远程双向切换、断连及成果归属 |
|
||||
| 无工具能力的聊天模型 | 说明自动调用限制,可使用已有直连图片工作流 | 不解析普通文字伪装工具调用,不新增表单;Ask 不提交 |
|
||||
|
||||
本地 OpenCode、Continue 和 DeepSeek Harness 经内部 MCP gateway 注入;直连 Model 使用内置工具分发。远程通过 `main-image-tool-session.ts`、Agent 协议和 `image-tool-mcp.ts` 回到同一 Main 服务。现有 GoodBuddy Agent 的受管远程 Runtime 为 OpenCode,项目校验和 Runtime 创建均沿用此范围;共享契约包含其他 Runtime 不表示存在其远程执行路径。保留会话在目录清空时也刷新 MCP,Continue 准备失败时释放本次分配的令牌。工具发现成功或 Skill 注入成功不能代替真实生成与编辑。
|
||||
本地 OpenCode、Continue 和 DeepSeek Harness 经内部 MCP gateway 注入;直连 Model 使用内置工具分发。远程通过 `main-image-tool-session.ts`、Agent 协议和 `image-tool-mcp.ts` 回到同一 Main 服务。当前受管远程 Runtime 包括 OpenCode 和 Continue;Continue 模型桥的会话 MCP 交付及 Ask 边界见[远程 Runtime](../remote-host/technical-design.md#runtime)。保留会话在目录清空时也刷新 MCP,Continue 准备失败时释放本次分配的令牌。工具发现、无计费测试接收端往返或 Skill 注入成功不能代替真实生成与编辑。
|
||||
|
||||
Main 已接收的请求在桌面仍运行时独立于远程连接继续;未确认是否接收的请求不自动重放。聊天回合停止等待与用户取消图片是不同动作,须验证现有取消传播不会误杀 Main 图片请求。
|
||||
|
||||
|
||||
@@ -1,11 +1,25 @@
|
||||
# 会话监督
|
||||
# 监督者与会话监督
|
||||
|
||||
会话监督定义对长会话、工具执行和异常状态的观察、判断与介入边界。
|
||||
监督者统一工作回顾、会话监督与故事线图谱,以智能心跳作为定时唤醒机制。现有智能心跳页面承载监督者工作回顾入口,手动回顾、定时结果投影、来源原文、实体/关系确认、继续讨论、知识库预览提交和侧栏反馈已经接入。历史回顾定位到对应结果图谱;侧栏按会话或任务查询,并支持固定目标。同 scope 的跨 run 实体复用依赖模型显式选择 Main 提供的候选引用,Main 处理 UUID 和旧文本主键的精确映射,具体规则和验证边界见[实施进度](./progress.md)。
|
||||
|
||||
## 文档导航
|
||||
|
||||
主页面包含工作回顾、故事线图谱、自动监督、活动记录和设置。工作回顾阅读统一监督结果并选择历史,图谱定位同一结果;自动监督管理计划、概览、自动监督报告、记忆与行动建议和心跳审计。活动记录展示统一执行、进度及详情,可按计划 ID 筛选;设置只保留算法参数。当前布局以 [UI 设计](./ui-design.md#应用入口)为准。活动读取实际手动回顾及心跳执行,合并展示同一次心跳的报告与监督回顾阶段;状态规则、历史缺失和保留期限见[活动记录](./logic-design.md#活动记录)。
|
||||
|
||||
自动心跳报告和监督回顾已接入按来源版本及正文位置的增量处理,无变化时跳过模型。手动历史回顾继续重新整理所选区间;进度、失败重试和删除边界见[自动增量与手动重看](./logic-design.md#自动增量与手动重看)。
|
||||
|
||||
| 文档 | 权威职责 |
|
||||
| --- | --- |
|
||||
| [会话监督 PRD](./prd.md) | 监督目标、证据、介入方式和产品验收 |
|
||||
| [监督者应用产品设计](./supervisor-prd.md) | 应用定位、心跳分工、知识库关系与目标范围 |
|
||||
| [逻辑设计](./logic-design.md) | 整体架构图、图谱对象关系图、状态规则与需求映射 |
|
||||
| [UI 设计](./ui-design.md) | 应用导航、工作回顾、侧栏反馈及页面状态 |
|
||||
| [User Stories](./user-stories.md) | 监督者与故事线图谱的用户操作和验收场景 |
|
||||
| [技术设计](./technical-design.md) | Main/Renderer 分层、数据模型、IPC、安全边界和实施顺序 |
|
||||
| [回顾分块调度设计](./review-scheduling-design.md) | 第 0 节定义生产分页、批次持久化、续跑、当前设置及剩余边界;其余章节保留改造前基线、目标设计和原型证据 |
|
||||
| [实施进度](./progress.md) | 已验证实现、验证证据和当前边界 |
|
||||
| [故事线图谱设计](../story-graph/prd.md) | 图谱对象、环绕时间轴与实体交互 |
|
||||
|
||||
当前尚无独立 User Stories、功能逻辑、UI、技术设计和进度文档。
|
||||
生产入口已接通完整来源分页、叶子持久化、预算暂停、活动页继续及批次详情;设置可调页大小、批次正文/消息上限、软预算、两个模型超时和共享并发。实际合同及限制见[生产接线](./review-scheduling-design.md#0-生产接线与剩余边界)。真实 DeepSeek 验证已覆盖一个完整的 27 条消息会话;较早的 129 条消息实验仍未完成。两次实验共 5 次付费请求,详情见进度。原始 AbortError 原因尚未由日志确定。
|
||||
|
||||
实施状态以代码和[实施进度](./progress.md)为准。逻辑待定项见[逻辑设计](./logic-design.md),架构边界见[技术设计](./technical-design.md)。现有心跳行为见[智能心跳](../smart-heartbeat/README.md),原型范围见[Story Graph 演示](../story-graph/README.md)。
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
# 监督者逻辑设计
|
||||
|
||||
日期:2026-09-23。状态:部分规则已接入产品,验证范围见[实施进度](./progress.md)。图中的连线表示逻辑依赖与数据流,不代表所有目标接口均已实现。
|
||||
|
||||
本文承接[监督者产品设计](./supervisor-prd.md)、[故事线图谱设计](../story-graph/prd.md)和[User Stories](./user-stories.md),定义触发、整理、展示与用户后续操作之间的关系。既有会话监督的证据和介入边界继续由[会话监督 PRD](./prd.md)约束。
|
||||
|
||||
## 整体逻辑架构
|
||||
|
||||
报告、监督叶子与导航整理共用监督专用 FIFO 并发池,普通聊天不入池;监督服务外层 run 仍串行。报告在排队前冻结超时,获槽后领取,实际模型请求前校验并刷新租约;报告提交或无变化后先释放槽位,再执行监督。监督超时在 run 创建时保存,节点获槽并解析 Runtime 后计时。阶段超时、租约、传输时限、后台检查与活动轮询各自独立,取值及生效规则见[当前设置合同](./review-scheduling-design.md#当前设置合同)。
|
||||
|
||||
完整回顾的生产接线已增加分页来源清单、逐批输出/offset 事务和导航节点。预算耗尽显示 `paused`,模型或保存失败显示 `failed`;两者均保留此前成功批次,可继续同一 run。存在未读来源、覆盖空洞或未成功导航时不能标 `completed`。手动位置与自动 checkpoint 分开,自动位置只在最终发布事务推进。来源变更目前要求重新回顾,模型配置跨重启固定等差异见[生产接线与剩余边界](./review-scheduling-design.md#0-生产接线与剩余边界)。真实模型验证覆盖 27 条消息的完整会话,较大样本失败单独记录。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph Entry["入口与触发"]
|
||||
App["监督者应用"]
|
||||
Scope["关注范围、时间区间与整理策略"]
|
||||
Manual["用户主动回顾"]
|
||||
Heartbeat["智能心跳:按计划唤醒"]
|
||||
end
|
||||
|
||||
subgraph Sources["已有内容与状态"]
|
||||
Conversation["会话记录"]
|
||||
Notes["魔法笔记"]
|
||||
Work["任务状态与执行记录"]
|
||||
Knowledge["现有知识库资料与检索引用"]
|
||||
end
|
||||
|
||||
subgraph Supervisor["监督者 Agent"]
|
||||
Collect["收集范围内的有界可见证据"]
|
||||
Review["理解进展与变化,判断是否需要整理"]
|
||||
Result["带来源的整理结果"]
|
||||
Quiet["记录检查结果,保持安静"]
|
||||
end
|
||||
|
||||
subgraph Outputs["结果与展示"]
|
||||
Recap["工作回顾:阶段决定、变化、未解决事项"]
|
||||
Feedback["监督反馈:聊天右侧展示"]
|
||||
Graph["故事线图谱:事件、实体、关系与演变"]
|
||||
end
|
||||
|
||||
subgraph Actions["用户后续操作"]
|
||||
Verify["核对来源、确认或纠正整理结果"]
|
||||
Continue["预览所选上下文后继续讨论"]
|
||||
Save["预览并确认新建或补充本地知识条目"]
|
||||
end
|
||||
|
||||
App --> Scope
|
||||
App --> Manual
|
||||
Scope --> Collect
|
||||
Manual --> Collect
|
||||
Heartbeat --> Collect
|
||||
Conversation --> Collect
|
||||
Notes --> Collect
|
||||
Work --> Collect
|
||||
Knowledge --> Collect
|
||||
Collect --> Review
|
||||
Review -->|需要整理| Result
|
||||
Review -->|无有效新变化| Quiet
|
||||
Result --> Recap
|
||||
Result --> Feedback
|
||||
Result --> Graph
|
||||
Recap -->|定位阶段| Graph
|
||||
Feedback -->|定位相关对象| Graph
|
||||
Recap --> Verify
|
||||
Graph --> Verify
|
||||
Verify -->|更新确认状态或用户修订| Result
|
||||
Graph --> Continue
|
||||
Continue --> Conversation
|
||||
Graph --> Save
|
||||
Save -->|仅写入可写的本地条目| Knowledge
|
||||
```
|
||||
|
||||
心跳负责唤醒,监督者负责判断;模型不因被称为 Agent 而自动获得工具权限。收集证据由应用按所选范围提供,现有心跳不具备的知识库或笔记接入属于后续工作。
|
||||
|
||||
工作回顾、监督反馈和图谱引用同一次整理的结论与来源。展示可以裁剪为阶段摘要、会话卡片或图形,但不能分别维护相互矛盾的结论。一次整理可以没有图谱变更,也可以有整理结果但无需主动通知。
|
||||
|
||||
结果定位以结果 ID 为准。历史选择不能回退到其他项目的最新图谱;侧栏需同时匹配目标来源和范围,没有匹配结果时显示空态。固定目标失效时保留目标并显示读取错误,用户可取消固定。跨 run 仅允许引用同 scope 的 Main 候选实体,模型局部 ID 和同名均不构成身份。模型候选引用与存储身份分离;历史文本主键按 Main 精确映射保留,格式错误的旧模型身份字段只产生新实体,不猜测合并对象。每次结果保留当次描述,自动更新不得改写已确认字段或其他结果;人工修改仅更新当前对象与明确选定的结果。
|
||||
|
||||
应用维护来源正文;监督者维护其产生的阶段总结、事件关联、实体描述与关系。图谱是这些对象的视图。图中“纠正”只更新整理内容或关系,修改来源正文仍需进入对应应用的编辑流程。
|
||||
|
||||
## 图谱对象关系
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Story["故事线"]
|
||||
Event["时间事件"]
|
||||
Change["事件与实体的关联:提出、补充、验证、修订"]
|
||||
Entity["知识实体"]
|
||||
Relation["知识关系:类型、理由、确认状态"]
|
||||
Summary["阶段总结"]
|
||||
Interval["时间区间与来源范围"]
|
||||
Reference["来源引用:所属对象、位置、可用版本信息"]
|
||||
Original["原内容:会话、笔记、本地资料或已保存外部片段"]
|
||||
|
||||
Story -->|组织| Event
|
||||
Story -->|关联,可跨故事共享| Entity
|
||||
Story -->|按阶段回顾| Summary
|
||||
Summary -->|限定覆盖范围| Interval
|
||||
Summary -->|归纳范围内事件| Event
|
||||
Summary -->|概括当时的认识| Entity
|
||||
Event -->|一个事件可影响多个实体| Change
|
||||
Change -->|一个实体可经历多个事件| Entity
|
||||
Entity -->|关系起点| Relation
|
||||
Relation -->|关系终点| Entity
|
||||
Event -->|原始依据| Reference
|
||||
Change -->|变化依据| Reference
|
||||
Relation -->|关联依据| Reference
|
||||
Entity -->|支撑资料| Reference
|
||||
Summary -->|结论依据| Reference
|
||||
Reference -->|定位,不复制正文| Original
|
||||
```
|
||||
|
||||
这里的关联对象用于说明关系自身也有类型和依据,不指定存储表。知识实体与来源为多对多关系;实体可以被多个故事引用,不能仅凭同名就合并。来源可用的版本信息沿用原内容能力,不要求为此复制完整来源历史。
|
||||
|
||||
外圈时间轴呈现事件及阶段标记,内部平铺实体;时间到实体的连线对应事件关联,实体间连线对应知识关系。时间轴上的同日聚合点只负责显示密集事件,不形成新的知识实体。
|
||||
|
||||
## 应用启停与执行
|
||||
|
||||
以下规则对应 FR-S1、FR-S4、FR-S5 和 US-S14,适用于监督者与智能心跳共用的 `heartbeat` 应用。
|
||||
|
||||
- 新安装及设置缺失 `heartbeatEnabled` 时按 `false` 处理;已明确保存的 `true`、`false` 均保留。
|
||||
- 关闭应用后不领取新的定时心跳运行,拒绝新的手动心跳和监督回顾请求,也不启动心跳完成后的自动监督回顾。侧栏不挂载监督面板,不发起该面板的查询。
|
||||
- 启用应用后,只有已保存且启用的心跳计划才参与自动调度;没有此类计划时不产生自动工作。启动或启用应用不自动创建、补种或启用计划,用户仍可主动发起回顾。
|
||||
- 关闭前已经领取并在执行的运行可以完成并保存结果,关闭应用不强制取消;若完成时应用已关闭,不再追加自动监督回顾。已有计划的 `enabled`、历史结果及导航偏好保留。
|
||||
- 应用开关不影响普通任务的现有定时调度。取消常驻只改变导航入口,不改变应用启用或计划状态。
|
||||
|
||||
## 状态与处理规则
|
||||
|
||||
下表包含目标规则;当前活动执行状态以本节后的“活动记录”为准,取消及部分完成尚未接入。
|
||||
|
||||
| 规则 | 条件或状态 | 处理与用户可见结果 | 需求与场景 |
|
||||
| --- | --- | --- | --- |
|
||||
| L1 范围 | 手动或定时触发 | 按本次所选范围、时间和输入预算收集证据;启用应用不扩大来源范围 | FR-S3、S4;US-S01 |
|
||||
| L2 检查 | 没有有效新变化 | 记录检查结果,保留旧回顾,不重复通知;用户主动查看时仍可读取原结果 | FR-S4、S5;US-S03 |
|
||||
| L3 整理 | 产生新的阶段认识或关联 | 回顾、反馈和图谱引用同一整理结果;是否主动反馈按反馈策略决定 | FR-S2、S5;US-S02、S04 |
|
||||
| L4 失败 | 取消、失败或部分来源不可用 | 保留上次成功结果;显示当前运行状态、已覆盖与遗漏范围,支持重试;部分结果不标为完整 | FR-S6;US-S12 |
|
||||
| L5 纠正 | 用户修改描述或确认关系 | 后续自动整理提出补充或修订建议,不静默覆盖用户内容;移除关系可撤销且不删除来源 | FR-S6、S9、FR-G4;US-S06、S10 |
|
||||
| L6 历史 | 选择过去时刻或时间区间 | 使用当时已有的事件与认识;来源旧版不可恢复时提示,不以当前原文冒充旧版 | FR-S9、FR-G3、G5;US-S05、S07、S13 |
|
||||
| L7 目标 | 侧栏跟随或固定 | 跟随时显示当前会话,固定时保持所选目标;切换聊天不改变固定目标 | FR-S5;US-S25 |
|
||||
| L8 沉淀 | 用户选择写入知识库 | 先预览并确认,再新建或补充本地条目;已有条目提供打开入口,外部引用保持只读 | FR-S7、S8;US-S08、S09 |
|
||||
| L9 继续 | 用户选择继续讨论 | 预览并确认上下文后进入会话;新内容在后续整理时纳入,不立即作为已确认知识 | FR-S3、S5;US-S11 |
|
||||
| L10 展示 | 改变布局或输入方式 | 保留范围、回放时刻及选择;图形和文字列表访问相同对象与来源 | FR-G2、G5、G6;US-S15 |
|
||||
|
||||
整理运行状态与内容确认状态分开:一次运行成功不意味着自动归纳已经得到用户确认。手动重试创建新运行;心跳继续沿用原有运行领取和重试规则。
|
||||
|
||||
## 活动记录
|
||||
|
||||
对应 FR-S10、US-S26。监督运行在收集证据之前保存 `running`,结果事务成功后转为 `completed`;收集、模型、校验或保存失败转为 `failed`,保留错误及结束时间。重开应用将没有完成的监督运行标为失败,旧成功结果保持可读。
|
||||
|
||||
心跳活动按真实运行 ID 关联下游监督。报告阶段使用 `claimed / completed / failed / skipped / no_change`,活动将 `claimed` 显示为运行中。报告已结束而投影尚在执行时,整条活动仍为运行中;任一阶段失败,整条活动显示失败。两阶段都没有新输入时显示“无变化(未调用模型)”;任一阶段保存新结果且没有失败时显示完成。没有下游运行记录时明确显示“无执行记录”。
|
||||
|
||||
旧监督成功记录继续显示;旧心跳与监督之间未保存关联时分别呈现,不用时间接近猜测关系。旧心跳未保存的执行范围显示不可用,不从当前计划反推。此前未保存的手动运行与失败无法恢复。心跳报告沿用计划的历史保留和删除规则;监督结果独立保留,原心跳被清理后仍可读取监督记录。活动不是永久审计库。
|
||||
|
||||
当前活动没有 `cancelled` 或 `partial` 状态,也不提供取消按钮。实体和关系修改只有当前状态及结果内容,没有按时间追加的人工修改记录。
|
||||
|
||||
收集、叶子整理、导航合并和发布分别展示。阶段推进只依据服务记录,最终完成只依据结果事务;“六批已保存、剩余零来源”仍可处于合并失败或发布失败。继续保留成功叶子和导航节点,缺失阶段不得靠错误文本或来源数猜测。导航摘要允许省略事实数组,叶子缺少事实数组仍失败;导航不得生成替代叶子事实的非空数组。阶段及输出合同见[生产调度](./review-scheduling-design.md#已接入的调度与存储)。
|
||||
|
||||
### 自动增量与手动重看
|
||||
|
||||
自动心跳报告和下游监督分别记录已成功处理的来源版本及正文位置。首次自动运行只读取计划范围和回顾窗口内的来源;后续运行在同样边界内处理新增、修订或上次预算未覆盖的片段。项目列表顺序不改变自动进度;不同项目集合和 Global 分开处理。失败、未送入模型或结果事务未提交的输入保留待处理状态。
|
||||
|
||||
两个阶段都在调用模型前检查输入。没有待处理来源时记录 `no_change`,保留旧报告和图谱,不产生新结果或重复事件。心跳报告无变化仍进入受应用开关控制的下游检查,允许监督阶段重试之前的失败。已确认记忆、已有实体说明及最近摘要仅作为背景,不单独触发自动模型调用。
|
||||
|
||||
工作回顾的手动操作重新整理所选区间,沿用有界历史收集,不读取或推进自动进度。自动监督计划列表中的“立即运行”重新生成心跳报告,其下游监督仍按自动进度处理。删除报告或计划不重置来源进度;重看历史、删除报告和重建图谱是不同操作。当前没有清空或重建图谱功能。
|
||||
|
||||
计划 Modal 的新建草稿不产生持久化或执行副作用,显式保存时创建并启用;编辑沿用已有 enabled。保存失败保留草稿,提交期间禁止关闭,未保存关闭须确认。活动筛选只改变读取范围,不触发执行;同名同范围计划仍按 configId 区分,筛选在分页前应用,结果跳转继续按 resultId 定位。
|
||||
|
||||
规则冲突时,先满足所选范围与既有观察边界,再保留用户修订和来源事实,最后应用自动归纳和展示策略。新增证据与已确认结论冲突时显示待核对差异,不能自动断言旧观点失效。
|
||||
|
||||
## 完整性与待定项
|
||||
|
||||
本设计覆盖触发、证据输入、静默检查、正常整理、取消与失败、跨视图共享结果、用户纠正、知识库写入及历史回放。它补充产品逻辑,不代表接口、持久化或迁移方案已经完成。
|
||||
|
||||
仍待产品决策:事件触发与阶段识别条件、反馈频率与静默判定、实体合并与拆分的交互、时间轴比例和集中度含义。应用启停按[应用启停与执行](#应用启停与执行)处理;既有入口偏好、计划与运行记录的保留要求见 FR-S1、S2 和 US-S14。
|
||||
|
||||
来源旧版本和完整历史回放仍受原应用的保存能力限制。增量进度与同 scope 实体 ID 复用的实现见[技术设计](./technical-design.md#自动增量收集)。
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
## 1. 背景
|
||||
|
||||
本 PRD 继续定义会话监督能力。统一应用“监督者”提供工作回顾、故事线图谱、自动监督、活动记录与设置五个页签;实际能力及执行记录要求见[监督者应用产品设计](./supervisor-prd.md)。本文其余执行观察与介入目标仍按各节范围实施。
|
||||
|
||||
GoodBuddy 的魔法笔记已经提供一种有价值的交互:用户持续写作,AI 在右侧以长评、建议和
|
||||
警告进行评论,用户可以选择综合、扩展、润色、质疑或发散方向。该能力是内容分析,不是
|
||||
会话监督:
|
||||
@@ -252,7 +254,7 @@ type SupervisorDecision = {
|
||||
|
||||
### 13.1 工作栏监督栏目评论流
|
||||
|
||||
监督是助手工作栏中固定且始终可访问的栏目,不是只在聊天页面出现的附属面板。栏目默认
|
||||
监督应用启用后,助手工作栏提供监督栏目;关闭时不挂载或查询监督面板,见[应用启停与执行](./logic-design.md#应用启停与执行)。栏目默认
|
||||
跟随当前会话,用户也可以固定到其他普通 Conversation、Task 或 Experiment。
|
||||
切换页面不会改变固定目标;目标失效时必须显示修复状态,不能静默回到当前会话。
|
||||
|
||||
|
||||
@@ -0,0 +1,422 @@
|
||||
# 监督者实施进度
|
||||
|
||||
日期:2026-09-24。
|
||||
|
||||
当前记录以已验证生产行为为准。监督者尚未覆盖全部 user stories。
|
||||
|
||||
## 已验证
|
||||
|
||||
### 2026-09-24 长摘要与内容区对齐
|
||||
|
||||
针对全局七天回顾的 `summary max 2000` 报错,代码确认导航输出 schema 的 `max(2000)`、服务层对叶子及导航的二次 2000 字符检查,以及模型提示词中的同值要求。另发现监督旧摘要通过 SQL `substr` 和 JavaScript `slice` 截断。以上监督路径限制已移除;生成内容和事实集合的合同见[生产调度合同](./review-scheduling-design.md#已接入的调度与存储)。未读取本次 16:03 失败的原始模型响应,不能据此声明该运行的具体失败节点。
|
||||
|
||||
生产工厂回归使用隔离 SQLite 和离线 Runtime 流,验证 14,000 字符的叶子及导航摘要完整进入合并输入、存储、历史读取,发布失败后以已保存节点恢复且不再请求模型,叶子长描述仍进入最终图谱。单次超过 100,000 字节的响应明确失败,保留续跑位置。共享契约另覆盖原字段及数组数量边界,仍拒绝导航事实和叶子缺字段;旧身份兼容、跨范围拒绝和六叶合并恢复测试继续通过。IPC 暂停/继续测试改用 3,600 字符摘要,并修正已有 `persistedId` 提示词断言为当前 `candidateRef` 合同。
|
||||
|
||||
布局采用共享阅读、dashboard 宽度,范围、工具栏、历史和结果的对齐规则见[UI 设计](./ui-design.md#应用入口)。真实 Electron 加载生产 React 组件,使用隔离 fixture 验证 2000、1440、1024、390px 的浅深主题,覆盖长摘要、活动、运行中、失败共 32 组,并保存 8 张正文末尾截图。无页面横向溢出,正文末尾保留,宽屏阶段行低于 40px,刷新紧邻筛选。另运行既有活动分支,验证原生键盘展开、完整错误滚动、六个叶子及 12 批分页。已人工查看宽窄屏、浅深主题、运行/失败、正文末尾及批次树截图。
|
||||
|
||||
截图分别保存在本机临时目录 `opencode/supervision-content-layout` 与 `opencode/supervision-content-activity`。复现命令为设置 `GOODBUDDY_SUPERVISOR_CONTENT_LAYOUT=1` 或 `GOODBUDDY_SUPERVISOR_ACTIVITY=1` 后运行 `npx vitest run tests/supervisor-layout.electron.test.ts`,可用 `GOODBUDDY_SUPERVISOR_ARTIFACTS` 指定保留目录。模型调用 0 次,未改写用户数据库、运行全量测试或提交。
|
||||
|
||||
验证记录:首轮服务、分块、历史和工作区 6 文件 68 项通过;补充共享契约、生产工厂、活动存储/preload、心跳中心及工作区共 8 文件 112 项通过。`npx vitest run src/main/ipc.test.ts -t 'supervis'` 为 17 项通过;修正测试等待的 TypeScript 类型后,暂停/继续单项再次通过。最终 `npm run typecheck`、本轮修改 TS/TSX/MJS 的定向 ESLint 和 `git diff --check` 通过。中文变更按 `deai-writing` 清单人工审校。
|
||||
|
||||
### 2026-09-24 活动阶段、批次层级与导航输出修复
|
||||
|
||||
对应 FR-S4、FR-S6、FR-S10、US-S26。活动记录改为紧凑运行摘要、实际阶段、恢复操作、折叠错误、按需批次层级和次要配置详情。阶段存入既有运行 JSON;叶子与导航分别校验,叶子缺数组仍失败。当前字段与缺失数据规则见[生产调度合同](./review-scheduling-design.md#已接入的调度与存储),布局见[活动设计](./ui-design.md#活动)。
|
||||
|
||||
只读检查 portable 原库的最新失败运行 `f6800335-5765-4163-9f55-a30c8ab18a94`,确认 32 个来源、剩余 0、6 个成功批次、4,515 字符和 1 个已保存导航节点。错误仅包含 `events / entities / entityChanges / relations` 四个缺失数组。旧代码把导航响应交给叶子 schema,且提示词同时要求“只返回摘要字段”和“事实数组必须为空”,合同矛盾已确认。原始失败响应和阶段没有保存,因此该次失败发生在导航解析仍属有数据支持的推断,不能声称取回了原响应。
|
||||
|
||||
使用 SQLite 一致性备份和关联笔记副本执行 `scripts/supervision-navigation-replay.ts`。仅私有副本初始化;第一次因缺少关联笔记停止,补齐后完成。生产工厂继续同一 run,复用已存导航节点,4 次离线响应完成剩余合并及发布,6 个叶子逐项未变。回放文本来自该 run 已保存的成功导航,只保留 summary、changeDigest、openItems,以验证缺数组的合法导航形状;它不验证新摘要质量,也不是原失败响应回放。原库只读,原失败 run 前后相同,付费模型调用 0 次。
|
||||
|
||||
本轮验证:
|
||||
|
||||
- `npx vitest run src/main/assistant/supervision-production.test.ts src/main/assistant/supervision-review.test.ts src/main/assistant/supervisor-service.test.ts src/main/assistant/supervision-activity.test.ts src/preload/supervision-activity.test.ts src/renderer/src/SupervisorActivity.test.tsx src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/SupervisorWorkspace.test.tsx`:8 文件、116 项通过。包含六叶零剩余后的合并失败与继续、发布失败后无模型继续、缺数组叶子拒绝、未知来源/实体拒绝、实际阶段呈现、旧阶段缺失、错误与配置折叠、树分页及读取失败重试。
|
||||
- `npm run typecheck` 和本轮变更 TS、TSX、MJS 文件的定向 ESLint 通过;未运行全量测试。
|
||||
- `GOODBUDDY_SUPERVISOR_ACTIVITY=1` 下运行 `npx vitest run tests/supervisor-layout.electron.test.ts`:1 项通过。真实 Electron 加载生产 React 组件,以多项目、多会话的隔离数据覆盖 1440×1000、390×1000 和浅深主题,分别截取运行摘要、错误展开、批次树,共 12 张截图。检查原生 Enter 展开、240px 错误滚动、全部六个已保存叶子及 12 批跨页读取,无横向溢出和控制台错误。失败运行摘要高度为桌面 366px、窄屏 476px,两主题一致;已人工查看截图。
|
||||
|
||||
私有数据库、回放报告及截图位于 `C:/Users/jiang/AppData/Local/Temp/opencode/activity-incident-20260924/`,截图测量文件为 `screenshots/review-activity-measurements.json`。收尾检查修正成功返回 coverage 沿用合并前阶段的问题,3 个生产服务测试文件共 33 项复验通过,活动与返回结果均使用发布阶段投影。`git diff --check` 通过,仅提示现存行尾转换。中文文档经 deai-writing 扫描与人工复核,本轮新增段落无阻断项;全文件扫描的 7 项阻断均在既有非本轮段落,未改动其内容。
|
||||
|
||||
历史缺失 phase 仍显示未记录;树只列成功批次,没有持久化的单任务失败/在途状态,项目和会话数量仅针对本页。收集与发布仍为同步 SQLite 阶段,不保证轮询可见。未修改 Agent、远程 Runtime、传输或凭据逻辑;原暂存及未暂存修改保留,未暂存、提交或替换 portable 构建。
|
||||
|
||||
### 2026-09-24 工作回顾与自动报告分区
|
||||
|
||||
对应 FR-S2、US-S22 至 US-S24。工作回顾仅阅读统一监督结果,历史选择和精确结果图谱入口移到正文前;手动新回顾控件保持内联,390px 下使用双列。结果常显生成时间、覆盖区间和范围,正文按原始段落及已有结构化字段展示,限制阅读宽度。新请求配置与运行状态不改写旧结果。自动报告、记忆与行动建议全部归入自动监督,保留展开、确认/忽略、带入对话、标记完成和每次追加 20 条;移除独立最近报告副本。五页签当前布局以 [UI 设计](./ui-design.md#应用入口)为准,下方各轮记录保留为历史证据。
|
||||
|
||||
- `npx vitest run src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/SupervisorWorkspace.test.tsx src/renderer/src/SupervisorActivity.test.tsx`:66 项通过。覆盖非空结果与空自动报告不矛盾、完整建议操作、长内容展开与分页保留、历史精确 ID、变更新请求范围不重标旧结果、运行中保留正文和活动入口。
|
||||
- `npx vitest run src/renderer/src/App.test.tsx -t 'graph navigation opens the pinned sidebar result|keeps heartbeat plans independent|saves Supervisor timeouts'`:3 项通过,284 项按名称跳过。校正已有设置保存测试的旧字段选择器,保留实际 App 接线验证。
|
||||
- `npm run typecheck` 与本轮修改的 Renderer、测试及 Electron driver 定向 ESLint 通过。中文 UI 文档经 deai-writing 扫描,无阻断项;复核项涉及既有状态与实现边界,保留事实限定。
|
||||
- 设置 `GOODBUDDY_SUPERVISOR_RECAP=1` 后运行 `npx vitest run tests/supervisor-layout.electron.test.ts`:1 项通过。现有生产组件 fixture 覆盖 1440×1100、390×1100 的工作回顾与自动监督,含两类结果均非空、自动报告为空、两者均空,共 12 个页面场景;另验证历史结果运行中保持可见及活动跳转。每页一处刷新、横向溢出及正文宽度断言通过。
|
||||
|
||||
截图与 `recap-measurements.json` 保存在 `C:/Users/jiang/AppData/Local/Temp/opencode/supervisor-recap/`。已人工查看桌面与窄屏正文、运行中的历史结果、自动报告展开、建议操作和独立空态截图。此次验证使用隔离 fixture,未读取用户数据库,模型调用 0 次,未运行全量测试;未修改 Main、Preload、数据库、Agent 或远程 Runtime。保留已有暂存和未暂存修改,未暂存或提交。
|
||||
|
||||
### 2026-09-24 自动监督概览归属修正
|
||||
|
||||
对应 FR-S2、US-S22 至 US-S24。当前状态、范围与刷新、成功率、记忆、洞察、行动指标、报告趋势和心跳运行审计已整体移到“自动监督”,与计划列表同页。当前状态下只保留计划列表的创建按钮和未配置说明,点击直接打开原 Modal。活动页只显示统一监督活动、进度、详情及结果入口,计划 ID 筛选、清除、分页和精确结果跳转沿用原路径;活动筛选不改变自动监督概览。当前归属见 [UI 设计](./ui-design.md#应用入口),下方旧方案的截图与测试记录保留为历史证据。
|
||||
|
||||
- `npx vitest run src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/SupervisorActivity.test.tsx`:48 项通过。覆盖空/非空时整块归属、唯一创建入口、独立刷新回调、原 Modal 的 X/footer/关闭保护、计划操作、审计分页、同名计划筛选和结果跳转。
|
||||
- `npx vitest run src/renderer/src/App.test.tsx -t 'keeps heartbeat plans independent of the active project'`:1 项通过,286 项按名称跳过,保留实际 App 的跨项目计划列表与暂停计划 Modal 保存验证。
|
||||
- `npm run typecheck`、本轮修改的 `HeartbeatCenter.tsx`、`HeartbeatCenter.test.tsx`、`supervisor-layout-driver.mjs` 定向 ESLint 通过;`git diff --check` 无空白错误,仅提示现存行尾转换。
|
||||
- 设置 `GOODBUDDY_SUPERVISOR_AUTOMATIC_OVERVIEW=1` 和产物目录后运行 `npx vitest run tests/supervisor-layout.electron.test.ts`:1 项通过,约 10 秒,仅运行本轮范围。现有生产组件 fixture 覆盖浅色 1440×1100、390×1100,空/非空计划与自动监督/活动共 8 个页面场景;检查整块归属、唯一创建按钮、横向溢出、原 Modal 打开关闭及精确计划活动筛选。
|
||||
|
||||
截图和 `automatic-overview-measurements.json` 位于 `C:/Users/jiang/AppData/Local/Temp/opencode/supervisor-automatic-overview-20260924/`。已查看桌面、窄屏空/非空计划、活动及滚动后的趋势和运行审计截图;桌面指标四列、窄屏单列,内容无横向裁切,活动页无重复心跳列表。本轮中文修改按 deai-writing 清单人工审校。未运行全量测试,模型调用 0 次,未读取用户数据库;未修改 Agent/远程 Runtime、Modal 实现或共享样式,保留已有暂存和未暂存修改,未暂存或提交。
|
||||
|
||||
### 2026-09-24 真实失败运行身份修复
|
||||
|
||||
只读定位到 `dist/GoodBuddy-windows-x64/data/assistant.sqlite` 中的运行 `db9902ab-22c8-4aae-86ae-7850bfc07b76`。Roaming 库不含该运行。原运行是全局手动回顾,范围为 2026-09-17 14:33:02 至 09-24 14:33:02,开始于 14:33:02,失败于 14:44:52(本机 UTC+8)。保存配置为分页 50、批次文本量 8000、消息数 20、执行预算 300 秒、模型超时 240 秒、并发 1;清单 32 个来源,成功批次 0。
|
||||
|
||||
原错误指向 `entities[0].persistedId` 的 UUID 校验。原始模型响应未保存,诊断文件没有该次响应,无法确认原错误值及 710 秒耗时的模型、排队、重试分解。只读候选检查发现 15 个旧实体均使用文本主键;隔离真实调用再次证明,即使模型选择提供的候选,旧主键仍会触发严格 UUID 校验。修复采用候选短引用、Main UUID 别名及旧键精确映射,详细规则见[技术设计](./technical-design.md)。
|
||||
|
||||
通过 SQLite 一致性备份复制数据库和关联笔记文件,仅隔离副本运行初始化及生产工厂 `createProductionSupervisorService`、`ModelAgentRuntime`、批次事务和结果保存。首次隔离启动因缺少笔记文件停止,付费调用 0 次;补齐副本后开始模型验证。使用 `.env.deepseek.local` 的 `deepseek-flash`,总计 3 次 HTTP 请求,无自动付费重试或模型回退:
|
||||
|
||||
| 验证 | 本机开始时间 | HTTP | 响应耗时 | 结果 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 原运行首叶提取 | 14:53:57 | 200 | 5.994 秒 | 首次暴露旧键校验失败,响应私存;修复后缓存复验并保存 |
|
||||
| 真实来源有界回顾 | 14:58:25 | 200 | 4.445 秒 | 2 个来源、402 字符、1 批,完成并发布 2 个实体 |
|
||||
| 同一来源候选复用 | 14:58:30 | 200 | 4.703 秒 | 完成并复用 2 个既有实体 |
|
||||
|
||||
缓存复验要求请求除 Runtime 当前时间外一致,没有新 HTTP。隔离副本中原失败 run 保存 1 批、3 个精确匹配的原文片段、490 字符及 3 个完整来源位置后主动暂停;32 个来源还剩 29 个,不冒充原全局回顾已完成。两个有界回顾分别完成结果发布,实体总数保持 15。手动回顾不写自动检查用的 `review_checkpoints`。只读复核原 run、来源清单及批次逐项未变;副本完整性检查通过,原有 1 个受保护实体保持不变。
|
||||
|
||||
私有产物在 `C:/Users/jiang/AppData/Local/Temp/opencode/supervision-incident-real-20260924/`:`incident.private.json`、`candidates.private.json`、3 份请求与 SSE 响应、`live-metrics.json`、`audit.json` 和副本 `assistant.sqlite`。原库只读打开,无初始化、迁移或业务写入;原失败状态及原有数据保留。验证脚本为 `scripts/supervision-incident-{inspect,launch,audit}.cjs` 与 `scripts/supervision-incident-live.ts`,重复付费启动受检查限制。
|
||||
|
||||
定向 7 文件共 47 项测试、`npm run typecheck`、本轮改动文件 ESLint 和 `git diff --check` 通过,后者仅提示现存行尾转换。回归包含真实旧键形状、严格 UUID 别名、空值和占位符的新事实保留、未知候选/跨 scope 拒绝、确认字段及图谱端点保护、失败后成功叶子不重算,以及中英文可展开错误详情。活动页显示本地化失败原因,技术错误保留在可选择文本的折叠详情;进度与设置不再直接展示 UTF-16、Unicode 或共享池术语。新增中文文档按 deai-writing 清单人工复核。未运行全量测试,没有提交、暂存或构建替换应用。此修复属于桌面监督身份边界,未修改 Agent 或远程 Runtime 执行实现;正在运行的 portable 和 D 盘安装版均需包含新源码的构建并重启后才会生效。
|
||||
|
||||
### 2026-09-24 计划 Modal 标题与操作区统一
|
||||
|
||||
对应 FR-S2、US-S22 至 US-S24。`HeartbeatSettings` 复用既有 `custom-task-dialog__header/content/actions` 和按钮样式,标题在左、带本地化可访问名称及工具提示的 X 在右,取消与保存位于右对齐 footer。采用共享 Grid 的固定标题、滚动正文、固定操作区,没有新增 Modal 框架或页面专属 CSS。共享规范见根目录 [表单 Modal](../../../UI-DESIGN.md#615-表单-modal),计划交互见[自动监督设置](./ui-design.md#自动监督设置)。
|
||||
|
||||
本轮验证:
|
||||
|
||||
- `npx vitest run src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/CustomTaskDialog.test.tsx`:50 项通过。覆盖中英文创建/编辑结构、X/取消/Escape/遮罩的干净关闭与未保存确认、Tab 循环、焦点恢复、提交锁定、失败草稿及暂停计划保存重试。
|
||||
- `npx vitest run src/renderer/src/App.test.tsx -t 'keeps heartbeat plans independent of the active project'`:1 项通过,286 项按名称跳过;验证实际 App 的暂停计划编辑提交、Portal 和背景隔离恢复。
|
||||
- `npm run typecheck` 通过;本轮修改的 Renderer、翻译和 Electron driver 文件定向 ESLint 通过;`git diff --check` 无空白错误。
|
||||
- `GOODBUDDY_SUPERVISOR_PLAN_MODAL=1`、`GOODBUDDY_SUPERVISOR_ARTIFACTS=C:/Users/jiang/AppData/Local/Temp/opencode/supervisor-plan-modal-standard` 下运行 `npx vitest run tests/supervisor-layout.electron.test.ts`:1 项通过,约 13 秒。创建/编辑 × 浅/深色 × 1440×1100、390×1100、390×480 共 12 个截图场景,测量无横向溢出、标题与底部按钮不随正文滚动,关闭及底部操作可命中;原生 Shift+Tab、Enter、Escape 验证焦点循环、取消确认和返回编辑。首次两次运行暴露测试驱动的 Enter 字符事件缺失及窗口焦点不稳定,补充字符事件和输入前显式聚焦后通过。
|
||||
|
||||
截图与 `plan-modal-measurements.json` 保存在上述产物目录。已查看宽窗口、390px 单列及短窗口的浅深主题创建/编辑截图,X 保持右上、取消与保存保持右下,短窗口只滚动正文。fixture 使用现有五页签和生产 React 组件,未读取用户数据库;模型调用 0 次。未运行全量测试,保留原暂存区与工作区改动,未提交或推送。
|
||||
|
||||
### 2026-09-24 监督者文案整理
|
||||
|
||||
设置帮助删除“提供商 RPM、TPM、Retry-After 控制及费用预算尚未实现”,保留执行软预算到期后保存正在处理的批次、暂停和从活动记录继续的说明。活动说明删除人工修改审计缺失提示;计划帮助改为选择每日或每周时间、保存并启用后运行。图谱说明实体状态以所选回顾结果为准,事实详情提示展开批次并对照来源核对模型内容。中英文同步,不承诺全局限流或完整语义识别。
|
||||
|
||||
已检查监督者设置、帮助、空态、错误展示及相关测试。真实运行错误、失败、超时、暂停和来源失效仍按原路径显示,保留恢复操作。根目录 UI-DESIGN 的既有监督者章节记录文案原则,UI 设计和用户故事同步;内部技术设计与验证记录仍保留事实边界。
|
||||
|
||||
本轮验证:
|
||||
|
||||
- `npx vitest run src/renderer/src/SupervisionReviewSettings.test.tsx src/renderer/src/SupervisorActivity.test.tsx src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/SupervisorWorkspace.test.tsx`:4 文件、57 项通过。新增中英文用例覆盖帮助文案、监督并发范围、来源核对提示,以及真实 HTTP 429 / Retry-After 错误、失败状态和继续入口的保留。
|
||||
- `npx eslint src/renderer/src/SupervisionReviewSettings.tsx src/renderer/src/SupervisionReviewSettings.test.tsx src/renderer/src/SupervisorActivity.test.tsx src/renderer/src/i18n/locales/zh-CN/heartbeat.ts src/renderer/src/i18n/locales/en-US/heartbeat.ts`:通过。`git diff --check` 通过,仅有现存行尾转换提示。
|
||||
- 中文 UI 文档扫描无阻断项,9 项复核提示均涉及实际行为边界,按事实保留;新增规则和技术记录另经人工审校。未运行全量测试、类型检查、Electron 或真实模型调用,本轮模型调用 0 次。
|
||||
|
||||
本轮仅修改监督者 Renderer、i18n、相关测试和文档;Main、Shared 只读核对。保留已有暂存及未暂存修改,未暂存、提交或推送。
|
||||
|
||||
### 2026-09-24 persistedId 并行修复记录
|
||||
|
||||
此段记录被上文真实旧库修复替代的早期方案。当时静态核对并行 Main 改动:新实体示例省略 `persistedId`,空字符串和 `null` 视为省略,其他格式错误值仍拒绝。该方案未处理真实候选中的旧文本主键,不能证明用户故障已解决。当前合同见[技术设计](./technical-design.md)。原始失败响应的具体坏值仍未知。
|
||||
|
||||
用户提供的并行验证结果为 29 项定向测试及另 1 项 IPC 测试通过;两次真实 DeepSeek 请求分别验证创建和复用,实体数保持 2,实体 ID 稳定。本轮仅记录并静态核对修复,没有复跑这些测试或模型请求;此证据不证明全部事实的语义识别,也不替代此前完整会话实验的范围与计数。
|
||||
|
||||
### 2026-09-24 五页签与计划 Modal 修正
|
||||
|
||||
对应 FR-S2、US-S22 至 US-S24。当前导航为工作回顾、故事线图谱、自动监督、活动记录、设置。计划列表移入自动监督,原有表单移入共享 Modal;设置只保留算法参数。保存失败保留草稿,未保存关闭确认,暂停计划编辑后仍暂停;计划执行记录按 configId 进入活动筛选,数据库在分页前过滤。报告、建议、概览、审计及精确结果跳转按[当前 UI 设计](./ui-design.md#应用入口)归属。
|
||||
|
||||
- 六文件定向组最终 61 项通过:HeartbeatCenter、SupervisorWorkspace、SupervisorActivity、SupervisionReviewSettings、Main supervision-activity、Preload supervision-activity。包含计划创建/编辑、失败重试、放弃确认、焦点循环与恢复、同名计划筛选、报告/审计分页,以及真实 SQLite 分页前过滤。新增换计划重置分页和忽略旧响应回归,筛选与清除保留键盘焦点。
|
||||
- App 筛选 3 项通过:跨项目计划列表与暂停计划 Modal 保存、设置保存后重开、精确结果跳转。IPC 筛选 2 项通过,验证活动 configId 经真实 handler/SQLite 过滤并拒绝空 ID;其余 App/IPC 测试未运行。
|
||||
- 最终 `npm run typecheck`、`npm run lint` 和 `git diff --check` 通过;按要求未运行全量测试。中文 UI 文档审校无阻断项,复核提示涉及执行限制和未实现边界,按事实保留。
|
||||
- Electron 生产组件 fixture 测试 1 项通过,约 66 秒,覆盖 1440/1024/390px、浅深主题、五页签归属、Modal 焦点与放弃、390×720 短窗口保存按钮命中、计划活动过滤和原生方向键。首次运行被终端 60 秒上限中断,延长后通过。
|
||||
|
||||
截图位于 `C:/Users/jiang/AppData/Local/Temp/opencode/supervisor-five-tabs/`,包括 `menu-plans-light-390.png`、`plan-modal-dark-390-short.png`、各页签浅深主题截图及 `measurements.json`。人工检查计划、Modal 和设置截图,无横向裁切;短窗口长表单内部滚动。fixture 复用实际生产组件与已有模拟数据,未读取用户数据库,未调用模型。此次不修改 Agent/远程 Runtime,无需远程执行验证。
|
||||
|
||||
开始检查时 HEAD 为 `7adae97`,监督改动仍在暂存区与工作区,未发现上轮计划提交;本轮保留其他 dirty 修改,未提交、amend 或推送。
|
||||
|
||||
### 2026-09-24 菜单归属调整
|
||||
|
||||
此段记录上轮四页签方案,已由上文五页签修正替代。当时设置包含自动监督计划、模型超时与并发、回顾算法三个纵向区块;报告与建议迁入工作回顾,运行概览和心跳审计迁入活动记录。以下保留当时验证证据,当前交互以[应用入口](./ui-design.md#应用入口)为准。
|
||||
|
||||
本轮验证:
|
||||
|
||||
- `npx vitest run src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/SupervisorWorkspace.test.tsx src/renderer/src/SupervisorActivity.test.tsx src/renderer/src/SupervisionReviewSettings.test.tsx`:50 项通过,覆盖双语菜单归属、设置无嵌套导航、计划操作、建议确认/忽略、报告及审计分页、页签切换保留展开状态、快捷按钮焦点、图谱来源与持久进度。
|
||||
- App 按测试名称筛选运行 4 项并通过:页面读取失败重试、侧栏结果跳转与 keepalive、计划独立于当前项目、模型及回顾参数保存后重开。其余 App 测试未运行。
|
||||
- `npm run typecheck`、本轮修改的 TS/TSX/MJS 文件 ESLint、`git diff --check` 通过;未运行全量测试。
|
||||
- `npx vitest run tests/supervisor-layout.electron.test.ts`:最终 1 项通过,约 51 秒。保留原图谱、异常状态、设置及活动的 1440/1024/390px 验证,新增非空菜单在 1440/390px 浅深主题的 12 个页面场景、区块归属、报告全宽、设置无范围徽标/刷新行和原生方向键导航检查。人工查看截图后修正了报告遗留双栏宽度,并补全 fixture 下次运行时间。一次复验受终端 60 秒限制中断,延长执行上限后通过。
|
||||
|
||||
截图及测量保存在 `C:/Users/jiang/AppData/Local/Temp/opencode/supervisor-menu-20260924/`,菜单截图以 `menu-` 开头,`measurements.json` 包含布局结果。Electron 使用生产 React 组件和隔离 fixture,未读取用户库、调用模型或验证真实模型产出。本轮模型调用 0 次,无提交或推送。
|
||||
|
||||
### 2026-09-24 完整会话生产验证与收尾
|
||||
|
||||
生产链路已通过一个独立完整会话的真实模型测试;较早的 129 条消息实验仍为未完成,不能据本次成功改写其状态。按用户要求,没有重发此前的两个请求,也没有重新计算其已保存批次。
|
||||
|
||||
只读选择器 `scripts/supervision-select-readonly.cjs` 在 `readOnly`、`query_only` 和读取事务下选择另一条完整会话,筛选条件为 21..40 条 user/assistant 消息、8,001..14,000 码点,并排除旧目标 ID。命中会话共 27 条消息、9,677 码点;选取的是整条会话,没有截取其头尾。仅该会话进入外部临时快照及隔离产品库,原用户库未初始化、迁移或写入。
|
||||
|
||||
首次请求输出有效 JSON,提供商以 `stop` 结束,但使用输入的 `locator.source` 作为引用,未使用带范围的 fragment ID。生产校验因此拒收。修正为:引用若是本节点提供的 source token,且只匹配一个输入片段,就映射到该片段 ID;同源多片歧义或未知来源仍拒绝。映射不扩展引用范围,不把片段升级为整条消息。针对唯一映射、歧义拒绝补了确定性测试。
|
||||
|
||||
原始 SSE 已保存在外部临时目录。继续测试时通过同一生产工厂、`SupervisorService` 和 `ModelAgentRuntime` 重放该响应,校验相同来源快照、运行配置及请求字节数;这是一次本地响应复核,没有新增付费请求。之后只为未调用的第二叶子和导航节点请求 DeepSeek。最终与每条原消息逐字比较,并检查码点区间连续性。
|
||||
|
||||
| 实测项 | 结果 |
|
||||
| --- | --- |
|
||||
| 完整覆盖 | 27 / 27 条消息,9,677 / 9,677 码点,27 个片段;重建差异及剩余来源均为 0 |
|
||||
| 生产产物 | 2 个叶子、1 个导航合并、1 个完整发布结果 |
|
||||
| 叶子记录 | 19 个事件、15 个实体、8 个变化、11 个关系,共 53 条;摘要未替换叶子 |
|
||||
| HTTP | 本会话共 3 次 HTTP 200:两个叶子和一个导航;另有一次缓存响应复核,HTTP 为 0 |
|
||||
| 本轮总付费请求 | 较早失败实验 2 次 + 本会话 3 次 = 5 次;无重复付费请求、无自动重试、无回退模型 |
|
||||
| 配置 | `deepseek-flash`;页大小 17;默认正文批次 8000、默认消息上限 20;模型超时 180 秒、软预算 600 秒、接纳并发 2 |
|
||||
| 耗时 | 首次 20,164ms,缓存复核及剩余执行 19,584ms;二者不含人工修正间隔 |
|
||||
| 手动隔离 | 自动 checkpoint 为 0;SQLite integrity_check 为 ok |
|
||||
|
||||
产物目录为系统临时目录 `opencode/review-production-complete-20260924`;来源快照在 `opencode/review-production-selected-20260924`。计数见 `metrics.json` 和只读审计入口,响应 `.private.sse` 仅供本机诊断,未进入仓库。模型输入包含完整选中正文,但未进行独立语义召回评估;“覆盖完成”不表示模型识别了每个事实。
|
||||
|
||||
最终确定性验证:12 文件定向组 136 项通过,覆盖服务/存储、迁移、设置、Preload 和页面;生产 IPC 筛选 22 项通过,其余 128 项未运行。完整 App 的设置保存及重开 1 项、AssistantDatabase 的监督历史筛选 4 项通过。Electron 使用生产组件,在 1440/1024/390px、浅深主题通过布局测试。首次视觉测试发现脚本仍只选中第一个设置表单,改为测量整个设置面板的可见控件后通过;两个表单同名保存按钮也已分别命名。
|
||||
|
||||
收尾新增用例确认 schema 46 已提交的 2,000 码点前缀不重读,只处理后续 1,300 码点;来源变化保留旧批次并要求新 run,后续自动检查不会永久卡在旧版本。来源上下文按已保存 messageId 和片段返回,避免同时间消息误定位或用当前正文冒充历史片段。`npm run typecheck`、本轮修改及新增 TS/TSX/CJS/MJS 文件的定向 ESLint 均通过。没有运行全量测试、提交或推送。
|
||||
|
||||
最终定向命令:
|
||||
|
||||
```text
|
||||
npx vitest run src/main/assistant/supervision-review.test.ts src/main/assistant/supervisor-service.test.ts src/main/assistant/supervision-history.test.ts src/main/assistant/supervision-activity.test.ts src/main/assistant/incremental-review.test.ts src/main/assistant/supervision-model-pool.test.ts src/main/application-settings-store.test.ts src/preload/supervision-activity.test.ts src/renderer/src/SupervisorActivity.test.tsx src/renderer/src/SupervisionReviewSettings.test.tsx src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/SupervisorWorkspace.test.tsx
|
||||
npx vitest run src/main/ipc.test.ts -t "supervision|heartbeat enabled gates|admits .*reports before claiming"
|
||||
npx vitest run src/renderer/src/App.test.tsx -t "saves Supervisor timeouts and concurrency"
|
||||
npx vitest run tests/supervisor-layout.electron.test.ts
|
||||
npm run typecheck
|
||||
```
|
||||
|
||||
### 2026-09-24 生产接线与真实模型失败
|
||||
|
||||
本节记录首轮生产接线及较大样本失败;最终补充证据见上一节。下方同日的原型、设计及旧 collector 记录是更早阶段证据。
|
||||
|
||||
已接线:Main 手动 IPC 与心跳回调共用 `createProductionSupervisorService`;schema 47 保存来源清单、配置、叶子和导航节点;稳定键分页、项目/会话轮转、Unicode 片段、逐批事务、失败/预算后继续、严格发布判定已进入生产服务。活动及 Preload 增加暂停、继续、分页批次详情;设置存储、App 回调和中英文表单已接通页大小、字符/消息批次上限及软预算。应用仍默认关闭。实现合同和未完成项见[生产接线与剩余边界](./review-scheduling-design.md#0-生产接线与剩余边界)。
|
||||
|
||||
确定性验证使用隔离 SQLite、生产 IPC 和可控 Runtime,先于真实请求执行。覆盖超过 20 会话/20 消息的 506 条来源、Unicode 精确重建、手动/自动位置隔离、失败后重开、冻结配置、事务回滚、暂停 signal 到 Runtime、成功批次不重算及历史结果来源。后续增加项目轮转、分页大小不改变语义块、发布失败后复用导航节点、Preload 方法和 UI 操作验证。
|
||||
|
||||
真实验证入口为 `scripts/supervision-production-live.cjs` / `.ts`,使用与 Main 相同的生产工厂及 `ModelAgentRuntime`,不使用 `review-algorithm.mjs`。输入为已有只读快照中的完整目标会话,截止时间为 2026-09-23 16:09:45.919 UTC,共 129 条消息;逐消息集合 SHA-256 与原探测清单一致。只将选中会话导入临时产品库,没有复制整份用户库,未打开或改写原库及用户设置。
|
||||
|
||||
| 实测项 | 结果 |
|
||||
| --- | --- |
|
||||
| 配置 | DeepSeek `.env` 中的 `deepseek-flash`;页大小 17、正文批次 16000、消息上限 50、并发接纳 2、模型超时 180 秒、软预算 600 秒 |
|
||||
| HTTP | 2 次,均 200;无付费重试、无模型回退、无工具 |
|
||||
| 结果 | 第 1 批校验并保存,第 2 次输出 JSON 无效;逻辑 run 为 failed,没有完整结果 |
|
||||
| 来源清单 | 129 条消息,54,979 个 Unicode 码点 |
|
||||
| 已持久化覆盖 | 16,000 码点;28 条完整消息及 1 条部分消息,共 29 个片段;101 个来源未完成 |
|
||||
| 已保留输出 | 10 个事件、9 个实体、9 个变化、9 个关系,共 37 条结构化记录 |
|
||||
| 完整发布 / 自动 checkpoint | 均为 0;此次为手动运行,失败批次没有推进位置 |
|
||||
| 耗时 | 56,775ms;HTTP 测量仅记录响应头延迟,不能当作完整模型耗时 |
|
||||
| 完整性 | 临时库 `PRAGMA integrity_check` 为 ok |
|
||||
|
||||
私有产物位于系统临时目录 `opencode/review-production-20260924`。`metrics.json` 与只读命令 `node scripts/supervision-production-audit.cjs <外部临时目录>/assistant.sqlite` 可核对上述计数;不在仓库保存正文或凭据。失败响应正文没有保存,尚不能确定 JSON 无效是格式问题还是输出截断;不补造原因。按用户要求未重发失败付费请求,**该 129 条消息样本没有完成真实模型全会话覆盖验收**。16000 字符测试上限也不能视为经过可靠性验证的推荐默认。
|
||||
|
||||
已完成的定向命令包括:
|
||||
|
||||
```text
|
||||
npx vitest run src/main/ipc.test.ts -t "production supervision IPC|collects supervision within the exact UI timeRange"
|
||||
npx vitest run src/main/assistant/supervision-review.test.ts src/main/assistant/incremental-review.test.ts src/main/assistant/supervision-history.test.ts src/main/assistant/supervision-model-pool.test.ts src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/SupervisionReviewSettings.test.tsx src/preload/supervision-activity.test.ts
|
||||
npx vitest run src/main/assistant/assistant-database.test.ts -t supervision
|
||||
```
|
||||
|
||||
前述 IPC 3 项、七文件 45 项及数据库历史 4 项通过。更早一次十文件定向运行 116 项通过,2 项因两个表单保存按钮同名失败;已分别命名并复查相关文件。类型检查、最终定向 lint 和收尾验证在本节后续记录。未运行全量测试、提交或推送,保留已有 dirty 改动。此次没有修改 Agent、远程 Runtime 协议或其取消实现;监督父 signal 使用既有 Runtime 接口,本轮不声称完成远程真实验证。
|
||||
|
||||
### 2026-09-24 DeepSeek 全目标会话隔离原型
|
||||
|
||||
使用 `.env.deepseek.local` 的 `DEEPSEEK_BASE_URL`、`DEEPSEEK_MODEL`、`DEEPSEEK_API_KEY`;配置模型及 12 次响应的实际模型均为 `deepseek-flash`。直接调用配置端点,没有 GPT 回退、自动付费重试或工具调用。原型复用 `SupervisionModelPool`,实验并发参数设为 2,实测峰值为 2;没有修改应用设置。单请求超时 180 秒,输出预留 16,384 token,12 次均以 `stop` 结束。
|
||||
|
||||
启动前核对旧探测的 PID、创建时间、启动记录及命令路径,终止已取消的全会话 GPT 探测。旧尝试记录 10 次已插桩 HTTP 及 2 次未插桩调用,不计入下表的 DeepSeek 12 次。原始消息复用 `review-full-20260924/sources.private.json`,核验冻结消息集合的 SHA-256 及逐消息重建;未沿用旧 GPT 事实或导航。该快照来自既有 `readOnly`、`query_only` 采集入口,本轮没有打开原库,也未初始化、迁移或写入原库。
|
||||
|
||||
| 测量 | 结果 |
|
||||
| --- | --- |
|
||||
| 完整目标范围 | 1 个会话,129 / 129 条 user/assistant 消息,8 页;截至 2026-09-23 16:09:45.919 UTC 的冻结快照 |
|
||||
| 正文覆盖 | 54,979 / 54,979 个 Unicode 码点;本样本 UTF-16 单元数相同;135 个片段、7 个叶子批次,每批正文不超过 8,000 UTF-16 单元 |
|
||||
| 来源核验 | 重建差异 0、覆盖空洞 0、引用码点区间错误 0 |
|
||||
| 持久化结果 | 354 条叶子事实、503 条引用;其中一个重复短引文保留 2 个明确标为歧义的候选位置,没有猜选唯一位置 |
|
||||
| 导航 | 18 组完整索引 354 条事实,每条一次;生成导航前后叶子内容散列一致 |
|
||||
| 真实调用 | 7 次叶子提取 + 2 次独立检查点选择 + 1 次导航 + 2 次语义评分,共 12 次 HTTP 200 |
|
||||
| 首批速度 | 并发两请求分别 20.376 / 9.236 秒,首轮 20.420 秒;先保存 16,000 码点并正确返回 partial |
|
||||
| 叶子速度 | 单请求 9.236..20.376 秒;七请求耗时之和 107.334 秒,含并发重叠,不能当作墙钟耗时 |
|
||||
| 全实验时间 | 四次执行的进程内耗时合计 111.955 秒;首次请求至末次响应约 333.869 秒,包含检查、修改及人工重启间隔;另两次离线验证合计约 38ms |
|
||||
| token | 提供商报告输入 188,377、输出 56,009;其中叶子提取为 34,771 / 35,852,其余用于评估及导航;未换算货币费用 |
|
||||
| 续跑与无变化 | 三个已保存叶子在校验失败后复用;失败批次的已有响应重新本地校验,未再次请求。最终重启复用七批,无变化检查 4.362ms、模型调用 0;独立离线验证 19.826ms、模型调用 0 |
|
||||
|
||||
目标会话在旧探测后继续增长,先前记录的 119、123 条与本轮 129 条属于不同读取时点,不能混为同一分母。本轮覆盖完整目标会话,不等于覆盖旧全局区间的 830 条消息、63 个会话,也未测试任务、记忆和知识引用的全量采集。
|
||||
|
||||
独立检查点选择请求只看到两半完整原文,没有看到叶子结果,也未注入指定引文。模型返回 125 个检查点,超过要求的每半 24..32 个;排除 2 个原消息中不存在的逐字引文后,评分采用 123 个。第二半的消息回执虽有 64 项,但 ID 集合不匹配,因此仅声称全部 129 条已提交给评估器,不以模型回执证明它逐条读懂。
|
||||
|
||||
| 独立检查类别 | 有效检查点 | 模型判为保留 | 遗漏 / 失真 |
|
||||
| --- | --- | --- | --- |
|
||||
| 决策 | 46 | 46 | 0 / 0 |
|
||||
| 修正 | 9 | 9 | 0 / 0 |
|
||||
| 否定约束 | 1 | 1 | 0 / 0 |
|
||||
| 未解决事项 | 5 | 5 | 0 / 0 |
|
||||
| 助手陈述归属 | 62 | 62 | 0 / 0 |
|
||||
|
||||
模型评分未报告不受引用支持的事实;离线核验 123 项均有原消息的引用,118 项在所引用叶子中保留完整逐字检查引文,另 5 项依赖语义比较。评估器报告导航短摘要遗漏 69 项叶子细节,相关叶子仍完整保留。评估独立性仅指先从原文选择检查点、再比较输出,提取与评分使用同一模型;缺少人工金标准,否定类仅一项,跨两半的修正关系没有单独审计。这些数字不构成全部事实召回率或“无语义遗漏”的证明。
|
||||
|
||||
实际出现两次本地校验失败:重复引文导致叶子拒收,以及评估输出违反检查点数量与引用合同。成功批次在失败后仍可读取;原型为重复引文保存全部候选定位,对无效评估引文明确排除并计数。原始响应保留在临时目录,三次缓存读取不产生 HTTP。确定性测试另外覆盖批次失败、截断、取消和预算暂停后的恢复,未刻意制造提供商失败来增加付费调用。
|
||||
|
||||
执行入口为 `scripts/review-deepseek.cjs` / `scripts/review-deepseek.mjs`,共享原型为 `scripts/review-algorithm.mjs`。私有正文、来源、模型响应及 SQLite 均位于系统临时目录;仓库仅包含脚本、合成测试和不含正文的测量记录。产物目录:`C:/Users/jiang/AppData/Local/Temp/opencode/review-deepseek-20260924`。可公开计数见 `metrics.json`、`coverage.json`;私有依据见 `batches.sqlite`、`facts.private.json`、`navigation.private.json`、`gold-*.validated.private.json`、`evaluation-*.private.json`。
|
||||
|
||||
复现命令如下。`first`、`finish`、`verify` 启动独立进程,须待上一次退出再运行下一条;`status` 仅读取计数。现有输出目录会复用已保存产物;更换输出目录重跑会新增最多 12 次付费调用。
|
||||
|
||||
```powershell
|
||||
$source = "$env:TEMP/opencode/review-full-20260924"
|
||||
$output = "$env:TEMP/opencode/review-deepseek-20260924"
|
||||
node scripts/review-deepseek.cjs first $source $output .env.deepseek.local 2
|
||||
node scripts/review-deepseek.cjs status $output
|
||||
node scripts/review-deepseek.cjs finish $source $output .env.deepseek.local 2
|
||||
node scripts/review-deepseek.cjs verify $source $output .env.deepseek.local 2
|
||||
node --test scripts/review-algorithm.test.mjs
|
||||
npx vitest run src/main/assistant/supervision-model-pool.test.ts
|
||||
npx eslint scripts/review-algorithm.mjs scripts/review-algorithm.test.mjs scripts/review-deepseek.cjs scripts/review-deepseek.mjs
|
||||
```
|
||||
|
||||
10 项原型测试、3 项共享池测试及上述定向 ESLint 通过;没有运行全量测试、提交或推送。测试覆盖分页同序键、Unicode 重建和引用位置、空洞、预算暂停、失败后重开、无变化零调用、来源版本变化、输出拒收、取消以及导航不删除冲突叶子。该原型未接生产 IPC/UI、自动 checkpoint、全局多项目调度或多层汇总;真实请求绕过产品 ModelRuntime。生产实现仍存在下节记录的采集遗漏,未来实现边界见[分块调度设计](./review-scheduling-design.md#12-隔离原型的设计结论)。
|
||||
|
||||
### 2026-09-24 模型阶段超时、并发与设计边界
|
||||
|
||||
- 本次静态核对当前新代码:报告 `heartbeatReportTimeoutSeconds`、监督 `supervisorOrganizeTimeoutSeconds` 已接入共享校验、设置存储、App/设置表单及 Main 摘要器;整数 30..600 秒、默认 240。报告在等待池前冻结值,监督在摘要器入池前读取值,均在获槽及 Runtime 解析后才计时。完整控制及未覆盖环节见[控制清单](./review-scheduling-design.md#当前超时与调度控制清单已实现)。
|
||||
- `supervisorModelConcurrency` 已接入同一设置链路,整数 1..4、默认 1;报告与监督共用 FIFO 池,普通聊天排除,降限不终止在途项。报告获槽后才领取,每次到期领取最多一条;租约为 `max(300, 报告超时 + 60)` 秒,实际请求开始前按运行 ID、owner、attempt、claimed 和未过期条件校验并刷新。报告提交或无变化后先释放槽位,再进入下游监督。尚无提供商全局并发、RPM、TPM 或监督层 Retry-After 控制。
|
||||
- 本轮协作提供的新增验证记录:24 项并发相关测试、33 项租约相关测试及 typecheck、lint 通过。本次文档修订仅核对实现,没有重新执行这些测试;记录未附精确过滤命令,不补造命令或与此前批次相加。
|
||||
- 实现代理报告:87 项测试、App/IPC 聚焦用例、另 19 项取消测试,以及 typecheck、lint 通过。本次文档修订未复跑这些命令,不将数量相加当作独立用例总数,也不推断全量测试已通过。
|
||||
- 实现代理报告的真实远程取消验证耗时 30.75 秒,清理后本次拥有的进程为 0。该证据验证相应 Runtime 取消/清理路径,不证明完整分页调度、父级取消、批次持久化或跨重启续跑已经实现。
|
||||
- 真实用户数据探测结果见下节。两次原始失败耗时与旧 240 秒时限一致,但缺少 abort 原因记录,尚不能证明证据规模是超时根因。
|
||||
- [回顾分块调度设计](./review-scheduling-design.md)已按用户授权加入完整分页、保留叶子事实/来源、成功批次及位置持久化、预算/失败后继续和严格完成判定。上述调度、存储合同、状态及 UI 仍待实现;当前有界单次成功不等于整个时间区间完整覆盖。
|
||||
|
||||
### 2026-09-24 真实会话覆盖与模型小样本
|
||||
|
||||
原始数据库以 `readOnly` 和 `query_only` 打开,只读事务内分析,未初始化或迁移。用户所述安装路径不存在;实际安装位于 `D:\Program Files (x86)\GoodBuddy`,数据位于用户配置目录。统计使用当前数据库内 9 月 16 至 23 日记录,并非还原该次运行时的数据库快照。
|
||||
|
||||
| 测量 | 结果 |
|
||||
| --- | --- |
|
||||
| 区间内会话消息 | 830 条、63 个会话、5 个项目 |
|
||||
| 手动采集器返回 | 202 条消息、70,491 个 UTF-16 单元 |
|
||||
| 实际进入当前摘要器 | 100 条消息、17,714 个 UTF-16 单元;覆盖 9 个会话 |
|
||||
| 目标会话 | 区间内 80 条中仅 20 条进入摘要器;正文覆盖 3,693 / 33,247,即 11.11% |
|
||||
| 离线完整分块审计 | 123 条消息分为 127 块,重建差异为 0;未对这些块执行模型提取 |
|
||||
| 只读采集耗时 | 手动 182ms、单批增量 375ms、整个探测 805ms |
|
||||
|
||||
整条消息遗漏发生在 SQL 数量限制及服务取前 100 项处;本样本进入摘要器的消息未再发生正文裁剪。100 条来源均缺少 `locator.messageId`,现有会话定位不能代替逐消息覆盖凭据。完整分页与消息定位仍为待实现项。
|
||||
|
||||
隔离模型实验使用两条已保存的真实样本,共 814 个 UTF-16 单元,经 `createDefaultModelRuntime` 调用 `gpt-6-astra`。3 次 HTTP 请求均为 200,未付费重试;两次提取耗时 34.665 / 35.413 秒,导航概述 6.703 秒,实验总耗时 76.813 秒。模型输入与原消息逐字一致,要求保留的两处引用及消息引用均核验通过,导航引用两项已保留事实;无变化复查耗时 5.99ms,模型请求为 0。原应用设置在完成时逐字节未变。
|
||||
|
||||
两处引文已在提示中明确指定,因此该实验仅验证小样本的引文保留、引用正确性与保留产物,不能证明自由提取完整性、全会话无失真或未来调度器性能。实验采用自定义单来源准入及直接模型 Runtime,未经过完整生产 IPC/UI。原库两次失败运行分别耗时 240.211 / 240.216 秒,监督结果仍为 0;样本成功不等于原问题已修复。
|
||||
|
||||
复核产物保存在本机临时目录 `opencode/review-fidelity-20260923-current`:`verification-current/coverage.json`、`live-resolved/live-metrics.json`、`verification-current/verification.json`。执行入口为私有临时脚本 `resume-supervision-probe.cjs` 和 `verify-supervision-probe.cjs`;核验退出码为 0。启动器等待曾超时,最终产物记录三次请求完成且进程随后退出,未因此重发请求。此前依赖加载失败均发生在请求发送前。会话正文及配置副本未加入仓库。
|
||||
|
||||
### 既有实现
|
||||
|
||||
- 共享监督者契约已接入 Main 服务,模型请求包含系统指令;证据总字符预算为 48,000,输出仍限制为 100KB,并校验本次证据集内的来源引用、输出局部实体 ID 和关系端点。手动回顾当前收集会话、任务、消息已有的知识引用和已确认记忆;记忆使用 `memory` 来源类型。
|
||||
- SQLite schema 43 包含监督运行、结果、来源、故事线、事件、实体、实体关联和关系表,并保存每次结果的实体与关系内容。来源保存本地 library/document/chunk 或外部 locator 元数据;持久化与升级规则见[技术设计](./technical-design.md)。
|
||||
- 初版监督者入口只有一个页面页头和四项顶层页签,原有计划、概览、建议及报告当时保留在设置页内部。2026-09-24 已按上文菜单调整迁移内容;手动运行仍通过 Supervisor 通道,未绑定自动计划时也可用。
|
||||
- 工作回顾显示结果自身冻结的 scope/time range。故事线视图使用真实数据库结果,外围事件按实际时间逆时针排列并保留缺口,事件实体连线和来源过滤来自 Main 查询投影,图形不可用时仍可通过事件/实体/关系列表操作。
|
||||
- `HeartbeatService` 在定时或手动心跳成功落库后调用同一个 `SupervisorService`,按心跳范围和回顾窗口自动保存监督结果;投影失败不会改写已经成功的心跳状态。
|
||||
- 监督图谱提供 Main/Preload IPC 的来源读取、实体确认/修订/撤销和关系确认/移除;关系移除保存 `revoked` 状态,不删除来源,后续自动 run 不恢复相同身份的关系。
|
||||
- 右侧助手栏已读取监督反馈,支持来源查看、继续讨论预览与确认发送,以及本地知识库实体写入预览和确认提交。
|
||||
- 本地会话来源可按会话 ID 和发生时间读取上下文;没有当前会话时,继续讨论操作明确禁用。
|
||||
- 应用中心已增加监督者开关,继续复用 `heartbeat` 导航 ID;关闭后已打开页面显示关闭状态并禁用工作区操作,不删除既有心跳计划或历史数据。导航显示同时遵守 enabled 和 pinned 语义。默认值及执行边界以[应用启停与执行](./logic-design.md#应用启停与执行)为准。
|
||||
|
||||
## 验证证据
|
||||
|
||||
### 2026-09-23 Desktop 0.13.13 侧栏布局回归
|
||||
|
||||
- 固定目标与刷新改用标题旁的共享图标按钮;目标显示会话/任务名称,缺失名称使用未命名文案。
|
||||
对应 US-S25,行为见[会话侧栏](./ui-design.md#会话侧栏),未改变监督运行或取消逻辑。
|
||||
- `RightAssistantSidebar.resize.test.tsx` 66 项和 `SupervisionCard.test.tsx` 4 项通过。
|
||||
`tests/supervisor-layout.electron.test.ts` 的常规工作区场景通过,额外设置
|
||||
`GOODBUDDY_SUPERVISOR_SIDEBAR=1` 后的侧栏场景也通过:480/300/200px、
|
||||
浅深主题、长标题、34px 按钮、键盘焦点与无横向溢出,共 6 种布局。
|
||||
- 完整 typecheck、lint 通过,真实模型调用 0 次。使用隔离组件 fixture,不代表真实
|
||||
用户数据库端到端验收;发布验证范围见[候选记录](../../development/release-preparation-0.13.13.md)。
|
||||
|
||||
### 既有实现验证
|
||||
|
||||
- `npx vitest run src/renderer/src/SupervisorWorkspace.test.tsx src/renderer/src/HeartbeatCenter.test.tsx`:17 个测试通过,覆盖统一页签、无计划手动回顾、加载状态、图谱真实连线与来源过滤。
|
||||
- `npx vitest run src/main/assistant/supervisor-service.test.ts src/main/assistant/assistant-database.test.ts src/main/application-settings-store.test.ts src/renderer/src/ApplicationCenter.test.tsx`:166 个测试通过。
|
||||
- `npm run typecheck`:通过。
|
||||
- `npm run lint`:通过。
|
||||
- `git diff --check`:通过;仅有现有文件的行尾转换提示。
|
||||
|
||||
### 2026-09-23 页面与视觉验证
|
||||
|
||||
- 生产页面按 [UI 设计](./ui-design.md) 使用三栏工作台、真实实体名称、逆时针事件环、实际关联连线、选中强调与来源详情。颜色复用现有图谱令牌,未修改全局主题。密集数据按批显示,列表保留全部返回记录;事件浏览不还原历史实体状态。
|
||||
- 设置只保留一个一级标题,缺少监督者 bridge 不再回退旧概览;无计划手动回顾、项目/时间范围、来源关联过滤均有组件回归。来源异步读取期间切换事件时,迟到正文不会出现在无关联事件下。
|
||||
- `npx vitest run tests/supervisor-layout.electron.test.ts src/renderer/src/SupervisorWorkspace.test.tsx src/renderer/src/HeartbeatCenter.test.tsx src/main/assistant/supervisor-service.test.ts src/main/assistant/assistant-database.test.ts`:134 项通过。Main 测试使用隔离数据库和注入摘要器;视觉入口渲染生产 React 组件,未读取真实用户数据或调用付费模型。
|
||||
- Electron 实测 1440、1024、390px 与浅深主题,页面、壳层和内容容器均无横向溢出;1440px 三栏实测为 216 / 863 / 280px。窄屏画布内部滚动保留可读字号,缩放后 SVG 文字不低于 11px;另验证 9 个容器临界宽度、长标题、80 个同时间事件、40 个实体、来源详情、设置表单、加载/空/错误/缺 bridge 状态。
|
||||
- 截图与 `measurements.json` 输出到本次指定的临时目录 `opencode/supervisor-visual`。代表文件为 `selected-source-1440.png`、`light-1024.png`、`light-390.png`、`dark-1440.png`、`dense-selected.png`、`selected-source-390.png`、`settings-plans-390.png`。可复用的验证和交互预览入口见 UI 设计末段。
|
||||
- 全量 `npm test`:416 个文件通过、9 个跳过、1 个失败;4902 项通过、67 项跳过、1 项失败。失败为 `local-runtime-reuse.test.ts` 的十项目 OpenCode 压缩断言(`compacted: false`),单独复跑该文件的 2 项均通过。本次未改动 Runtime 实现,未将这次全量运行记为全绿。
|
||||
- 页面收尾后重新执行 `npm run typecheck`、`npm run lint` 通过。App 已在上述全量测试中通过;一次包含 App 的额外定向命令超过终端 120 秒限制,随后缩小到上述 5 个相关文件完成验证。
|
||||
|
||||
## 当前边界
|
||||
|
||||
### 2026-09-24 算法设置文档补充
|
||||
|
||||
已静态核对共享设置合同、设置存储及隔离原型参数,补充[设置配置清单](./review-scheduling-design.md#4-设置配置清单)和 [UI 交互目标](./ui-design.md#算法配置设置目标)。本次仅更新文档;新增分页、分块、预算、续跑及额度控件均未实现,原型值不作为产品默认。未运行代码、测试、探测或模型调用,未修改应用配置和用户数据库。
|
||||
|
||||
### 2026-09-23 Main 与侧栏定向修复
|
||||
|
||||
- 已修复:继续讨论不再把数组写入要求字符串的 `serializedContexts`;请求省略 Runtime 时复用会话选择。真实 SQLite 入队后,生产队列解析器成功派发请求。
|
||||
- 已修复:每次结果独立分配实体和来源 UUID,映射事件、关系、实体变化及来源引用。两次重叠 run 和跨项目相同模型 ID 均有 SQLite 回归,用户修订的名称和说明保留,所有结果均可读取自己的来源及 locator。schema 41 升级并重复打开后保留旧记录和确认状态。
|
||||
- 已修复:知识库预览与提交均检查本地库及实体所属库;侧栏确认框展示实际返回内容,取消与写入失败后可重新预览。测试覆盖真实本地实体读写,外部只读及提交前归属变化由注入状态验证。
|
||||
- 定向验证:`npx vitest run src/main/assistant/assistant-database.test.ts src/main/ipc.test.ts src/renderer/src/RightAssistantSidebar.resize.test.tsx src/main/assistant/supervisor-service.test.ts -t 'supervision|shares bounded evidence'`,4 个文件中选中的 9 项通过,其余 289 项未运行。本轮未运行全量测试、typecheck、lint 或真实模型请求;前文历史验证不能代替本轮验证。
|
||||
- 当时未修复的双边时间范围及记忆背景语义已由下节定向修复覆盖。跨 run 实体识别由本轮候选身份实现补齐;旧实现已经覆盖或忽略的历史内容无法从现有记录自动还原。
|
||||
|
||||
### 2026-09-23 第 4 问题:双边时间范围
|
||||
|
||||
- 已接通真实 `supervisionRun` IPC handler → collector → SQLite → 摘要输入 → 结果保存。监督者显式传递请求时间区间,不再扩大到一小时;消息与会话在 LIMIT 前按闭区间筛选,任务按创建或完成时间筛选。所选项目复用存在且 active 的检查;普通 Heartbeat 不传明确区间时仍使用原下界行为。
|
||||
- 记忆证据使用真实 `updated_at`,locator 保留 `created_at`/`updated_at` 并标明当前背景;摘要指令禁止据此推断历史内容。任务的当前状态也不作为历史状态快照。实现规则与剩余容量边界见[技术设计](./technical-design.md)。
|
||||
- 定向验证:`npx vitest run src/main/ipc.test.ts src/main/assistant/heartbeat-database.test.ts -t 'collects supervision within the exact UI timeRange|builds one bounded snapshot across only the selected projects'`,2 个文件中选中的 3 项通过,133 项未运行。两项新 IPC 测试分别覆盖 global/projects:15 分钟精确范围与端点、25 条区间后消息及 21 个区间后会话不挤掉区间消息、旧任务期间完成、期内创建但期后完成的任务选用创建时间、记忆非采集时刻、archived 项目拒绝。已有 Heartbeat 项目快照用例通过。
|
||||
- 首两次定向运行因新增测试 fixture 缺少 `description`、消息 `state` 失败,补齐实际契约后通过。本轮所改 tracked 文件的 `git diff --check` 通过,仅有行尾转换提示。验证使用真实隔离 SQLite 与生产 IPC/collector,模型 Runtime 为测试替身;未运行 UI/Electron、全量测试、typecheck 或 lint,真实模型调用为 0。未改 Renderer/CSS、数据库 schema、远端 Agent/Runtime,不涉及独立远程执行逻辑;未提交。
|
||||
|
||||
### 2026-09-23 结果定位、目标过滤与候选身份
|
||||
|
||||
- 对应 US-S02、US-S05、US-S10、US-S13、US-S25:overview 返回稳定结果/故事线 ID;Main 校验 graph 请求归属,并按结果读取当次事件、对象与来源。历史选择和图谱共享结果定位,请求序号阻止迟到响应覆盖新选择。
|
||||
- 侧栏 overview 通过类型化 Conversation/Task 目标查询;SQLite 在 LIMIT 前同时校验匹配来源和项目范围。无匹配结果不展示全局第一条。卡片展示结果范围和时间区间,目标切换清空来源与预览并忽略旧请求。
|
||||
- tasks 工作栏实例新增可持久化的会话/任务监督绑定;固定、切换聊天、取消固定均连接到实际卡片查询,任务列表仍使用原项目范围。继续讨论预览展示真实发送目标,回调显式传会话 ID,Main 复用该会话配置。固定任务找不到关联会话时不启用发送。
|
||||
- 模型只能用 `persistedId` 引用 Main 提供的同 scope 候选 UUID;服务与保存事务分别校验。未显式引用候选的实体和所有来源继续分配新 UUID。SQLite 回归覆盖重复 run 复用、不同项目拒绝、裸模型 ID 不合并、确认/修订字段及关系理由保护、移除关系后不自动恢复。
|
||||
- schema 43 升级保存现有可定位的结果成员,后续 run 不改写旧结果内容;测试从 schema 42 升级并重复打开,比较历史图谱与来源,包括没有直接来源引用、但两端实体归属明确的关系。已有 schema 41 保留数据用例继续通过。缺失归属、曾被覆盖的描述仍无法重建。
|
||||
- 定向测试共覆盖 32 个不同用例,最终均通过。主命令为 `npx vitest run src/main/assistant/supervision-history.test.ts src/main/assistant/assistant-database.test.ts src/main/assistant/supervisor-service.test.ts src/main/ipc.test.ts src/renderer/src/SupervisorWorkspace.test.tsx src/renderer/src/SupervisionCard.test.tsx src/renderer/src/RightAssistantSidebar.resize.test.tsx src/renderer/src/HeartbeatCenter.test.tsx -t '[sS]upervis'`:29 项通过,2 项失败来自新增同步 IPC 拒绝断言误用 `.rejects`。修正后仅复跑 `src/main/ipc.test.ts src/shared/workbar-contracts.test.ts -t '[sS]upervis'`,5 项通过,包含新增工作栏绑定契约用例。期间修正了旧侧栏 fixture 缺少目标、迁移比较未忽略成员顺序和图谱按钮查询歧义。
|
||||
- 收尾增加同来源但项目范围不匹配的过滤断言,以及无直接来源引用的关系迁移断言;仅复跑 `src/main/assistant/supervision-history.test.ts src/main/assistant/assistant-database.test.ts -t supervision`,5 项通过,其余 103 项未运行。
|
||||
- `npm run typecheck` 运行一次,通过。对本轮涉及的 TS/TSX 执行一次定向 ESLint,0 error、1 条 ref 清理 warning;调整后仅复查该文件及新增契约测试,迁移收尾只复查数据库与对应测试,均为 0 error、0 warning。未重复检查整个 App。`git diff --check` 通过,仅有现有行尾转换提示。
|
||||
- 本轮未跑全量测试或 App 整文件,未启动 Electron 或调用真实模型;模型请求数为 0。验证包含生产 IPC/collector 与真实隔离 SQLite,以及真实 React 组件的异步交互测试,不能据此声称已完成真实模型或完整桌面端到端验收。未修改 Agent/远程执行协议或 Runtime 生命周期;此次改动在桌面 Main 的监督者输入、存储和查询层生效。全部 dirty 改动保留,未提交。
|
||||
|
||||
### 2026-09-23 侧栏结果直达图谱
|
||||
|
||||
- 对应 US-S25:SupervisionCard 有匹配结果时提供中英文“在图谱中查看 / View in graph”原生按钮。App 通过显式回调接收 resultId,遵守现有离页检查并打开 heartbeat 路由,向 HeartbeatCenter / SupervisorWorkspace 传递类型化导航请求;没有新增页面或 window 事件。沿用 schema 43 与现有 graph IPC,未改 Main、Preload 或存储。
|
||||
- 图谱页签、键盘焦点和历史结果选择共用现有页面状态;同一结果再次点击、不同结果切换及 keepalive 再进入均重新定位,固定监督目标保持。导航切换立即清空旧图谱和详情;overview、graph、来源及操作的迟到响应不会改写新选择。overview 没有返回指定结果时仍按该 ID 请求图谱;失败可重试原 ID,不回退默认最新结果。
|
||||
- 新增 7 个定向用例,最终均通过。首轮命令:`npx vitest run src/renderer/src/SupervisionCard.test.tsx src/renderer/src/SupervisorWorkspace.test.tsx src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/App.test.tsx -t 'graph navigation'`,3 项通过、2 项因测试使用旧文案失败。修正为实际“工作栏”“历史结果”文案并补充两项竞态测试后,仅运行 `src/renderer/src/SupervisorWorkspace.test.tsx src/renderer/src/App.test.tsx -t 'graph navigation'`,5 项通过、296 项未运行。请求状态收尾后仅复跑 SupervisorWorkspace 的同一筛选,4 项通过、11 项未运行。
|
||||
- 测试覆盖真实 React 组件与 App 路由接线:首次只请求侧栏结果、固定目标、同结果重复点击、不同结果、keepalive 页面实例不变、历史选择同步、页签方向键及焦点、中英文/空态入口、旧 overview/graph/来源迟到、指定 ID 不在 overview 和失败重试。IPC 在这些 UI 测试中为替身,未声称真实桌面端到端验收。
|
||||
- 本轮涉及的 10 个 TS/TSX 文件定向 ESLint 通过,0 error、0 warning。`npx tsc --noEmit -p tsconfig.node.json` 与 `npx tsc --noEmit -p tsconfig.web.json` 各一次有效源码检查通过;此前附加 `--incremental false` 的启动因 composite 配置报 TS6379,未执行源码检查。`git diff --check` 通过,仅有现有行尾提示。
|
||||
- 未运行 App 整文件、全量测试、全量 lint、Agent typecheck、Electron 或真实模型请求;模型调用为 0。此次仅修改桌面 Renderer 导航和异步展示,不影响 Agent/远程 Runtime。保留全部其他 dirty 改动,未提交。
|
||||
|
||||
### 2026-09-23 自动监督设置
|
||||
|
||||
- 对应 US-S22 至 US-S24:当时设置直接显示完整自动监督配置,并保留运行概览、建议、报告与记录次要面板;2026-09-24 已迁移这些面板并移除内部导航。界面统一中英文命名,保留内部 heartbeat 标识和已有内容。无计划时明确显示仅支持手动回顾,填写草稿不触发保存或运行。
|
||||
- 核对共享 recurrence 合同及实际调度器后,仅展示已支持的每日、每周指定时间;页面明确说明不支持分钟间隔。编辑保留原时区和暂停状态,计划列表显示范围、周期、时区、回顾窗口与保留期限,继续提供编辑、暂停、恢复、运行及确认删除。
|
||||
- 已核对 App、Preload、Main 和 HeartbeatService 的既有 CRUD 接线,本轮未修改调度器或创建默认计划。保留工作区原有默认关闭及 Main 启用检查改动。
|
||||
- 定向测试:`HeartbeatCenter.test.tsx` 23 项、`application-settings-store.test.ts` 50 项通过;`heartbeat-recurrence.test.ts`、`heartbeat-service.test.ts`、`heartbeat-database.test.ts` 合计 17 项通过;`ipc.test.ts -t 'heartbeat enabled gates'` 4 项通过、128 项未运行。覆盖中英文直接入口、每日/每周提交、多项目、无效窗口、失败保留草稿、时区及暂停状态、各项计划操作,以及默认关闭、无计划不运行和启停行为。首轮整文件运行发现原有顶层图谱测试未清理 DOM,补充文件级 cleanup 后通过。
|
||||
- `tests/supervisor-layout.electron.test.ts` 通过:生产 React 组件的自动监督设置在 1440、1024、390px 和浅深主题下测量无横向溢出,周频表单全部控件边界可达,默认配置页及未配置说明存在。该测试使用隔离 fixture;未将其表述为真实模型或用户数据库端到端验收。
|
||||
- `npm run typecheck` 通过;本轮 9 个 TS/TSX 文件及 Electron driver 的定向 ESLint 通过。按要求未运行全量测试,真实模型调用 0 次;没有 Agent 或远程 Runtime 改动,未提交或推送。
|
||||
|
||||
### 2026-09-23 活动页签
|
||||
|
||||
- 对应 FR-S10、US-S26:schema 45 复用运行表保存监督开始、完成和失败,并按真实心跳 ID 合并报告和下游监督。手动失败、报告完成但下游运行中/失败、回调失败、应用重开后的未完成记录均有真实 SQLite 测试。没有新的执行引擎或取消协议。
|
||||
- 类型化活动 IPC/Preload 已接入生产页面;关闭应用不查询,页签或路由离开停止刷新并忽略迟到响应。每页 50 条,串行轮询,读取失败可重试。历史结果通过 resultId 定位,保留现有自动监督设置。
|
||||
- `npx vitest run src/main/assistant/supervision-activity.test.ts src/main/assistant/supervisor-service.test.ts src/main/assistant/heartbeat-service.test.ts src/preload/supervision-activity.test.ts src/renderer/src/SupervisorActivity.test.tsx src/renderer/src/HeartbeatCenter.test.tsx src/renderer/src/SupervisorWorkspace.test.tsx`:7 个文件、58 项通过。
|
||||
- `npx vitest run src/main/assistant/supervision-activity.test.ts src/main/assistant/heartbeat-database.test.ts src/main/assistant/supervision-history.test.ts`:3 个文件、14 项通过,包含 schema 44 升级及重开保留。
|
||||
- `npx vitest run src/main/ipc.test.ts -t 'supervision|heartbeat enabled gates'`:8 项通过、124 项未运行。新增活动查询首次运行暴露 UNION 排序列缺少别名,修正后真实 SQLite 和 IPC 均通过。
|
||||
- `npx vitest run tests/supervisor-layout.electron.test.ts`:1 项通过,新增活动页 1440、1024、390px 浅深主题测量,长错误、元数据和操作无横向溢出,短窗口页签保持高度。测试使用生产组件和隔离 fixture,没有读取用户库。
|
||||
- `npm run typecheck` 通过;定向 ESLint 通过。未跑全量测试或真实模型,模型调用 0 次。没有修改 Agent/远程 Runtime;工作区其他改动保留,未提交或推送。
|
||||
- App 定向命令 `npx vitest run src/renderer/src/App.test.tsx -t 'Supervisor|graph navigation'`:5 项通过、281 项未运行。刷新互斥及焦点收尾后,`SupervisorActivity.test.tsx`、`HeartbeatCenter.test.tsx`、`supervisor-service.test.ts` 合计 31 项通过;22 个相关 TS/TSX/MJS 文件的定向 ESLint 为 0 error、0 warning。
|
||||
- 结束时间收尾:手动心跳使用真实报告完成时间,下游失败单独记录结束时间。`npx vitest run src/main/assistant/supervision-activity.test.ts src/main/assistant/heartbeat-service.test.ts src/main/assistant/heartbeat-database.test.ts src/main/assistant/supervision-history.test.ts`:4 个文件、21 项通过,新增受控时钟用例区分 10:00 开始、10:02 报告完成、10:03 下游失败。随后 Node typecheck 和这 3 个改动源码/测试文件的 ESLint 通过。
|
||||
- 已存在的手动失败无法补回;旧心跳没有冻结范围和关联 ID,保留缺失提示。心跳仍按原保留期限清理,监督结果独立保存。取消和人工修改时间序列审计未实现;`no_change` 的后续接入见下节。
|
||||
|
||||
### 其他边界
|
||||
|
||||
- 新批次来源读取按 messageId 返回已保存片段及版本;旧历史缺少消息 ID 时保留会话/时间定位。本地知识引用通过现有 `KnowledgeService` 解析文档/分块,失效时显示不可用;外部引用只使用已保存 locator,不调用远端全库。
|
||||
- 继续讨论和知识库写入使用现有本地会话队列与知识库实体接口,并要求先预览、再由用户确认提交。侧栏已支持结果直达图谱,尚无可编辑的上下文预览。
|
||||
- 关系移除保留撤销状态及原始来源;界面没有提供恢复入口。
|
||||
- 候选集最多 100 个实体,复用依赖模型显式选择;不会自动合并历史重复实体或跨 scope 合并。结果级历史内容已保存,事件滑块仍不重建逐事件的实体演变。Experiment 和完整图谱回放尚未实现;批次暂停/继续已接通。
|
||||
|
||||
## 2026-09-23 自动增量验证
|
||||
|
||||
对应 FR-S4、FR-S10、US-S27。自动心跳报告与下游监督均已接入来源版本和处理位置;无变化跳过模型并保存 `no_change`。消息修订、任务状态、知识引用变化分别参与增量判定,已确认记忆只作背景。实现、预算和删除语义见[自动增量收集](./technical-design.md#自动增量收集)。
|
||||
|
||||
- schema 46 增加最小来源进度与消息版本,扩展已有心跳状态 CHECK。来源片段进度与对应结果原子保存,模型或事务失败不推进。未送入模型的来源及正文余段保留待处理资格。
|
||||
- `incremental-review.test.ts` 使用真实 SQLite 和生产服务,覆盖重开数据库、重复零调用、消息修订后复用实体 ID、105 个长来源分批读完、模型及结果事务失败、任务状态、范围排序、记忆背景、知识引用、报告保存失败和 schema 45 有数据升级。
|
||||
- 真实模型使用已有加密默认文本配置,经 `RuntimeSettingsStore` 和 Electron `safeStorage` 读取隔离副本,生产 `createDefaultModelRuntime`、`HeartbeatService`、`SupervisorService` 和 SQLite 执行。`tests/incremental-review-live.electron.test.ts` 于本日通过:首次自动运行 2 次 HTTP 请求,重复自动运行 0 次,修改消息后的监督回顾 1 次,再次监督检查 0 次,总计 **3 次真实文本请求**。工具与附件均为 0。
|
||||
- 真实输出通过结构、来源及实体引用校验,修改后的 Atlas 实体至少复用一个原持久化 ID;无变化检查前后图谱相同。测试检查原设置字节未变、外键与数据库完整性。隔离数据库、配置快照和加密 Local State 随测试目录删除,未改用户计划或数据库。
|
||||
- 实时测试命令:设置 `GB_REVIEW_LIVE_SETTINGS` 为已有运行时配置路径后,执行 `npx vitest run tests/incremental-review-live.electron.test.ts`。无该环境变量时跳过;每次测试最多 3 个真实请求,不记录凭据。
|
||||
- 最终聚焦验证:10 个服务、SQLite、Preload 和 Renderer 测试文件共 76 项通过;`npx vitest run src/main/ipc.test.ts -t 'supervision|heartbeat|application enable'` 另有 9 项通过,124 项按过滤条件跳过。`npm run typecheck`(Node、Agent、Web)和本次改动的 19 个 TypeScript 文件 scoped ESLint 均通过。
|
||||
|
||||
真实测试覆盖来源较短的单次自动回顾、重复检查及修改后的实体复用。长正文和大量来源预算由确定性 SQLite 回归验证;没有执行全量测试或真实用户数据库升级。事件驱动、清空或重建图谱和语义近似去重不在本次实现范围。
|
||||
- 知识正文条目与监督实体仍是两个模型,当前不会把监督实体当作知识正文条目。
|
||||
@@ -0,0 +1,348 @@
|
||||
# 监督回顾分块调度设计
|
||||
|
||||
日期:2026-09-24。状态:生产分块、持久化续跑、设置及活动入口已接线;27 条消息的完整会话通过真实模型验证,较早的 129 条消息实验仍未完成。当前实现以第 0 节为准,第 1 节保留改造前基线,第 2 至 11 节保留目标设计,第 12 节为早期隔离原型证据。
|
||||
|
||||
本文针对全局七天 `supervision:run` 出现 `AbortError` 的反馈,设计完整分页、证据分块、模型任务调度和逐层汇总。用户已明确要求预算耗尽或失败后保留成功批次及位置,重启后可继续,不重复计算成功叶子。生产接线复用 `SupervisorService`、现有数据库及 `review_checkpoints`;不另建定时调度器。schema 47 的实现与目标差异见第 0 节。
|
||||
|
||||
产品范围仍由[监督者产品设计](./supervisor-prd.md)定义;本设计对应 FR-S4、FR-S6、FR-S10 及 US-S12、US-S26、US-S27。既有规则见[逻辑设计](./logic-design.md#自动增量与手动重看),实现架构见[技术设计](./technical-design.md),已验证事实见[实施进度](./progress.md)。第 0 节以当前代码为准,其余目标须逐项核对实现状态;大范围回顾仍需独立性能验证。
|
||||
|
||||
## 0. 生产接线与剩余边界
|
||||
|
||||
Main 的手动 IPC 和心跳完成回调均使用 `createProductionSupervisorService`,该工厂连接 `SupervisorService`、`SupervisionReviewStore`、共享模型池及实际 ask Runtime。生产路径不再调用旧的 20 会话 × 20 消息 collector,也不使用旧服务分支的 100 条 / 48,000 字符裁剪。旧的有界服务注入方式仍供原有测试和实验脚本使用;心跳报告本身继续使用原报告预算。
|
||||
|
||||
### 已接入的调度与存储
|
||||
|
||||
schema 47 增加运行配置、来源清单、成功批次、导航节点四张表和一个实时来源视图。迁移只创建新结构,保留此前迁移、结果及用户数据。来源清单通过 `(project_id, conversation_id, sequence, source)` 稳定键分页,SQL 事务冻结范围内的来源身份、版本、长度及自动起点;清单不保存正文。项目和会话由来源页共同枚举,不另设全量 JavaScript 目录数组。正文随后按主键和有界 `substr` 读取。
|
||||
|
||||
派发按项目及项目内会话轮转,每个会话同一轮至多一个批次,会话内按 sequence/source 排序。批次跨数据库页累计;长消息未装入部分保留待处理。每片最多 8,000 UTF-16 单元、每批最多 50 片,均为封块条件,不是范围上限。位置采用零基、左闭右开的 Unicode 码点;消息 locator 保存 messageId、conversationId、projectId、role、sequence、source、revision、start、end、length。知识引用另保留原 locator 及所属消息,任务使用当前状态和区间内创建/完成时间。
|
||||
|
||||
成功叶子的输出、原文片段和连续 offset 在同一事务提交。失败或软预算到期保留此前批次。继续操作使用同一 run ID、时间范围和分块配置;重启后可继续。手动运行不读写自动 checkpoint。自动运行先恢复同 scope 的未完成 run;仅在全部来源连续覆盖、导航完成、最终发布事务成功时写入 `stage=supervisor` 的 checkpoint。空范围无需模型。
|
||||
|
||||
叶子输出仍使用现有事件、实体、变化和关系契约,并校验引用与候选身份。会话、项目、scope 使用二叉递归合并导航摘要,单子透传,总共 N 个叶子至多 N-1 次合并。已成功导航节点按孩子 ID 保存,发布失败后可复用。叶子、导航及最终结果的生成文本不设字段字符上限,事实数组不设独立数量上限;导航节点仍不得生成替代叶子的事件或实体。发布事务逐页读取叶子,保存既有历史/图谱结构,保持历史定位、来源及用户确认状态;完整叶子同时通过独立批次接口分页读取。
|
||||
|
||||
单次 Runtime 响应继续在接收过程中限制为 100,000 字节,防止响应缓冲持续增长。超过边界时停止接收,明确提示本批次未保存、已保存批次可继续使用;不裁剪后冒充成功结果。该边界与摘要字符数不同,不增加摘要长度设置。监督旧摘要读取、导航输入、保存和恢复均保留全文。身份格式、来源归属、字段类型与导航空事实数组的校验继续生效;原有数据库字段及已保存结果无需迁移。
|
||||
|
||||
导航使用独立的严格输出 schema,要求 `summary`、`changeDigest`、`openItems`;四个事实数组允许省略或为空,非空数组、错误类型和额外字段仍拒绝。验证通过后才在内部补空数组,以复用现有导航存储形状。叶子继续要求四个数组齐全,不将缺字段当作成功;来源、事件实体、变化实体和关系端点均校验。继续时还会核对已保存叶子的引用,导航不覆盖其独立事实。
|
||||
|
||||
`supervision:pause` 将父 signal 传入排队及在途模型;取消后的输出不提交。`supervision:resume` 恢复已保存 run;`supervision:batches` 默认每页 10 批、最多 20 批,返回完整叶子及来源。活动返回已保存批次、码点数、剩余来源、当前阶段、排队或执行中的模型任务数及已保存配置。软预算到期会等待已派发批次各自结束,随后显示 `paused`;完成判定不依赖摘要措辞。
|
||||
|
||||
当前阶段以 `phase` 写入既有 `supervision_review_runs.state_json`,取值为 `collecting / extracting / summarizing / saving`;无新表或迁移。它记录服务最后进入的阶段,失败和重启后保留;完成仍由 `supervision_runs.status` 的发布事务决定。活动的 `navigationNodes` 从已保存导航表计数,`inFlight` 仍来自进程内计数。旧运行没有 phase 时不回填推测值。清单和最终发布为同步事务,短阶段不保证被轮询看到,清单事务失败也不会留下部分阶段记录。批次接口只有成功记录,没有逐个失败/在途任务或项目、会话聚合状态;界面仅按分页返回的真实归属组织详情。
|
||||
|
||||
### 当前设置合同
|
||||
|
||||
| 字段 | 默认 | 整数范围 | 生效规则 |
|
||||
| --- | --- | --- | --- |
|
||||
| `supervisionReview.pageSize` | 50 | 1..200 | 每次来源元数据页条数;不改变最终集合或批次边界 |
|
||||
| `supervisionReview.batchCharacters` | 8000 | 1000..16000 | 每批新正文 UTF-16 单元,不含提示、候选及 JSON |
|
||||
| `supervisionReview.batchMessages` | 20 | 1..50 | 每批不同消息数;任务等无消息 ID 的来源单独计数 |
|
||||
| `supervisionReview.executionSeconds` | 300 | 30..3600 | 每次执行软预算,继续后重新计时;到期停止新增工作 |
|
||||
| `supervisorOrganizeTimeoutSeconds` | 240 | 30..600 | 创建回顾时冻结,节点获槽并解析 Runtime 后计时 |
|
||||
| `heartbeatReportTimeoutSeconds` | 240 | 30..600 | 维持报告发起时冻结及既有租约规则 |
|
||||
| `supervisorModelConcurrency` | 1 | 1..4 | 回顾保存接纳并发;共享池上限仍随应用设置实时变化,实际执行取较小值 |
|
||||
|
||||
应用仍默认关闭。新参数由共享 Zod 合同、ApplicationSettingsStore、App 设置回调和 Renderer 表单保存;没有放置尚未生效的高级字段。
|
||||
|
||||
### 未完成与实测限制
|
||||
|
||||
- 真实验证覆盖一个 27 条消息的完整会话;较早的 129 条消息实验在第二次输出 JSON 无效后停止。两实验合计 5 次付费请求,没有重复付费或回退。范围及缓存复核说明见[进度](./progress.md#2026-09-24-完整会话生产验证与收尾)。
|
||||
- 来源被修改或删除时保留旧批次并明确失败,标记需要新建回顾,活动页停止提供无效的继续按钮;下一次自动检查可创建新 run,不会永久重复该失败。尚未实现仅使受影响叶子和父节点失效后重算。清单记录冻结版本,不保存全库正文快照。
|
||||
- 分块配置、超时、接纳并发及算法版本已保存。实际默认 Runtime、模型及候选背景仍在执行时解析;未实现跨重启模型/提示词版本固定,也未实现共享池排空后切换冻结并发的目标规则。
|
||||
- 元数据清单和最终发布仍是同步 SQLite 事务,缺少大库延迟验证及可中断来源读取。批次详情已分页,既有历史图谱查询仍返回整份结果;大图谱的响应大小尚未改造。
|
||||
- 没有 token/费用预算、提供商 RPM/TPM、Retry-After 冷却、执行硬期限、独立清理期限或逐层可配置超时。默认正文预算不保证任意模型上下文容量;超长输出、非法 JSON 和引用错误会失败,保留已成功批次。
|
||||
- 片段以码点边界切分,尚未增加句界优先和重叠背景。事实保留指保留模型已产出的结构化记录,不证明语义无遗漏;生产契约校验引用 token,不要求原型的逐字引文和 reviewedTokens 回执。提供过的 locator.source 仅在本节点唯一对应一个片段时可作为引用别名;歧义及未知来源拒绝。
|
||||
|
||||
## 1. 改造前代码事实
|
||||
|
||||
下列旧草案行号仅作定位参考,工作区代码更新可能使其偏移;以所列函数和下方本次核对的控制清单为准。
|
||||
|
||||
| 环节 | 当前行为与代码依据 | 对调度设计的影响 |
|
||||
| --- | --- | --- |
|
||||
| 手动入口 | [ipc.ts:7917](../../../src/main/ipc.ts#L7917) 校验应用启用和维护状态,等待 `SupervisorService.run()` 返回 | 当前没有提前返回 runId 的开始接口,也没有监督取消接口 |
|
||||
| 运行串行 | [supervisor-service.ts:124](../../../src/main/assistant/supervisor-service.ts#L124) 用一个 `pending` Promise 串行执行整个服务的请求,出队后才创建运行 | 当前不是按项目并发;排队时间不在摘要定时器内,也尚无排队活动记录 |
|
||||
| 单次摘要 | [supervisor-service.ts:135](../../../src/main/assistant/supervisor-service.ts#L135) 最多取 100 条证据,每条最多 8,000 个 JS 字符,总正文最多 48,000,再调用一次 `summarize` | 没有分块队列或递归汇总;48,000 不包括实体候选、系统指令、旧摘要和 JSON 开销 |
|
||||
| 模型超时 | [ipc.ts](../../../src/main/ipc.ts) 的监督摘要器读取 `supervisorOrganizeTimeoutSeconds`,默认 240 秒,超时带明确原因 abort;流结束检查 signal 和 `done`,finally 释放会话 | 新控制已实现,完整清单见下节;不能据此反推原始 AbortError |
|
||||
| 底层模型请求 | [model-runtime.ts:206](../../../src/main/agent/model-runtime.ts#L206) 默认请求超时十分钟;[createRequestSignal](../../../src/main/agent/model-runtime.ts#L584) 传播上层 signal 并设置底层超时;[retryModelStream](../../../src/main/agent/model-runtime.ts#L1891) 对部分网络失败重试 | 上层默认四分钟可以先到,当前已可配置;调度层不能再盲目叠加网络重试。注入 Runtime 的实际类型和配置仍须运行时核实 |
|
||||
| 手动采集 | [ipc.ts:7120](../../../src/main/ipc.ts#L7120) 传入精确时间范围;[buildHeartbeatInput](../../../src/main/assistant/assistant-database.ts#L8168) 在时间和范围过滤后最多取 20 个会话、每会话最后 20 条消息、100 个任务、100 条记忆 | SQL 层未入选的证据在服务分块前已经遗漏,单改 `summarize` 无法补齐 |
|
||||
| 自动采集 | [collectIncrementalReview](../../../src/main/assistant/assistant-database.ts#L8333) 按阶段、scope、来源版本和位置过滤,再按发生时间和来源 ID 排序;最多 100 个来源,各取后续 2,000 个 Unicode 字符 | 已有片段定位与进度能力;查询需要扩展运行内位置,避免提交前反复读到同一片段 |
|
||||
| 自动预算 | [ipc.ts:7115](../../../src/main/ipc.ts#L7115) 监督新证据预算 44,000 个 JS 字符;[heartbeat-service.ts:227](../../../src/main/assistant/heartbeat-service.ts#L227) 报告预算 12,000;[reviewBackground](../../../src/main/assistant/assistant-database.ts#L8419) 最多四条各 500 字符记忆,旧摘要最多 2,000 字符 | 自动路径与手动路径的证据集合、预算和游标语义不同 |
|
||||
| 输出及身份 | [supervisor-service.ts:71](../../../src/main/assistant/supervisor-service.ts#L71) 检查 100KB 输出与来源引用;[同文件:167](../../../src/main/assistant/supervisor-service.ts#L167) 校验候选身份;[共享输出契约](../../../src/shared/supervision-contracts.ts#L62) 限制每对象 20 个来源、40 个事件、40 个实体、80 个变化和 80 个关系 | 多个合法子输出拼接后不一定合法;汇总必须同时约束字节、数量与引用 |
|
||||
| 保存 | [saveSupervisionResult](../../../src/main/assistant/assistant-database.ts#L8531) 一个事务保存运行状态、结果、来源正文片段、图谱快照及进度,来源重新分配 UUID | 可复用一次结果事务;当前 `coverage_json` 只有证据条数,不能表达完整覆盖 |
|
||||
| 自动入口 | [heartbeat-service.ts:220](../../../src/main/assistant/heartbeat-service.ts#L220) 先处理报告,再经 [ipc.ts:1914](../../../src/main/ipc.ts#L1914) 调同一监督服务;报告 `no_change` 仍检查监督阶段 | 报告和监督是独立消费阶段,报告成功不能替监督推进游标 |
|
||||
| 活动 | [活动契约](../../../src/shared/supervision-contracts.ts#L27) 没有 `partial`、`cancelled` 或子任务计数;[数据库活动投影](../../../src/main/assistant/assistant-database.ts#L8501) 按 heartbeat run ID 合并;[SupervisorActivity](../../../src/renderer/src/SupervisorActivity.tsx#L21) 仅在激活时轮询 | 展示新进度需要明确扩展契约,不能只添加状态文案 |
|
||||
|
||||
初稿阶段没有检查真实运行日志或用户数据库。后续只读探测记录了两次约 240 秒的失败,但没有复现该次请求或取得明确 abort 原因,尚不能确定是四分钟定时器或证据规模导致超时。诊断记录应包含运行 ID、阶段、耗时、输入规模、错误类型和 abort 原因;不记录证据正文、模型凭据或完整提示词。
|
||||
|
||||
当前监督输出先累积字符串、再检查 100KB;它不像心跳摘要器那样在流中检查字节数。报告和监督均将池 signal 与局部超时 signal 合并;清除本地数据会取消池内等待和在途工作,IPC 释放时 dispose 池。该生命周期取消不等于已有用户取消入口或分块 run 父级取消。以上都是静态实现差异,不是本次真实日志结论。
|
||||
|
||||
### 是否截断丢数据
|
||||
|
||||
手动回顾会遗漏本次分析输入:SQL 的数量限制,以及服务的 `.slice(0, 100)`、单条和总字符裁剪均会排除内容;任务和记忆排在会话证据之后,还会竞争余量。未入选内容没有自动续读机制。原始消息并未因此从数据库删除,但结果不能代表七天全部记录,裁剪后的来源也不能冒充完整消息。
|
||||
|
||||
自动回顾的未读片段仍保留待处理资格。[supervisor-service.ts:174](../../../src/main/assistant/supervisor-service.ts#L174) 只将完整进入摘要输入的片段 checkpoint 交给保存事务,模型或保存失败不推进。这里的“完整”指采集片段,并非整条消息。来源若在后续执行前离开滚动窗口,就不会被自动补读,因此“不会误标已处理”不等于“未来必定覆盖”。
|
||||
|
||||
目标是不再静默裁剪,把单次上限改成分页、分块和暂缓派发的依据,直至冻结范围内全部项目、会话、消息及其他入选来源处理完毕。模型提取仍可能遗漏原文含义,但已产出的叶子事实、事件、冲突及来源必须完整保留、可分页读取。摘要只用于导航,不能替代叶子结果;“原文已处理”也不等于模型识别了全部事实。
|
||||
|
||||
### 当前超时与调度控制清单(已实现)
|
||||
|
||||
以下是当前代码事实,不是后文拟议的多层调度预算。设置暴露两个阶段超时和一个监督并发数。
|
||||
|
||||
| 控制 | 实际值、读取时点与范围 | 代码依据与边界 |
|
||||
| --- | --- | --- |
|
||||
| 心跳报告模型阶段 | `heartbeatReportTimeoutSeconds`,整数 30..600 秒,默认 240;`runNow` / `processDue` 在等待池之前读取并冻结,获得槽位后才领取数据库任务 | [共享设置](../../../src/shared/application-settings-contracts.ts)、[设置存储](../../../src/main/application-settings-store.ts)、[HeartbeatService](../../../src/main/assistant/heartbeat-service.ts);手动及自动均适用,排队期间修改设置不改变该次报告值;到期领取每次最多一条 |
|
||||
| 监督整理模型阶段 | `supervisorOrganizeTimeoutSeconds`,整数 30..600 秒,默认 240;出队、采集完成后,在摘要器开始时读取 | [IPC 摘要器](../../../src/main/ipc.ts);后续监督阶段可读到新值,正在执行阶段不变;不覆盖排队、采集、Runtime 解析、保存或 finally 清理 |
|
||||
| 监督模型并发 | `supervisorModelConcurrency`,整数 1..4,默认 1;报告与监督整理共用一个 Main 内 FIFO 池 | [SupervisionModelPool](../../../src/main/assistant/supervision-model-pool.ts)、[IPC 接线](../../../src/main/ipc.ts);普通聊天不入池。升限可派发等待项,降限不打断在途项,等占用降到新上限以下再派发;监督服务外层 run 仍串行 |
|
||||
| 报告计时与失败 | 获得槽位、领取、采集和 Runtime 解析完成后,在实际请求开始前续租并启动计时;timeout 原因优先于下层 abort,流结束检查 signal、`done` 及空输出;流中限制 100KB | [IPC 报告摘要器](../../../src/main/ipc.ts);失败不保存报告及其 checkpoint,finally 清 timer、释放会话;报告槽位覆盖领取与采集,但排队和这些步骤不消耗模型时限 |
|
||||
| 监督计时与失败 | 摘要器读取设置后等待池,获得槽位并解析 Runtime 后启动计时;保留明确 timeout 原因及非 timeout 的提供商错误,流结束检查 signal 和 `done` | [IPC 监督摘要器](../../../src/main/ipc.ts)、[SupervisorService](../../../src/main/assistant/supervisor-service.ts);排队不消耗模型时限,失败不保存结果及 checkpoint;100KB 仍在服务收齐后校验,无监督用户取消入口 |
|
||||
| 直连模型传输 | 产品默认固定 600 秒的独立请求超时,本页两个阶段超时设置不修改它;内部 `requestTimeoutMs` 可由 Runtime 构造配置 | [model-runtime.ts](../../../src/main/agent/model-runtime.ts) 的 `createRequestSignal` / `fetchWithTimeout`;传播上层 signal,不是整次回顾预算;其他 Runtime/提供商可能更早失败 |
|
||||
| 直连传输重试 | 最多 3 次重试,间隔 500 / 1,000 / 2,000ms,仅符合条件的网络错误且尚未有阻止重试的流事件时重试 | 同文件 `retryModelStream`;等待也受上层 signal 约束,不重新获得报告/监督阶段时限,不是用户设置或调度叶子重试 |
|
||||
| 报告领取租约 | `max(300, reportTimeoutSeconds + 60)` 秒,实际范围 300..660 秒;模型请求实际开始前按同一冻结值刷新 | [HeartbeatService](../../../src/main/assistant/heartbeat-service.ts) / [renewHeartbeatLease](../../../src/main/assistant/assistant-database.ts);刷新须匹配运行 ID、owner、attempt、claimed 状态且尚未过期,否则拒绝启动。300 是下限;数据库默认仍为 300 秒,租约不是取消定时器,不覆盖下游监督 |
|
||||
| 阶段交接 | 报告保存成功或 `no_change` 后释放报告槽位,再进入下游监督;finally 的重复释放幂等 | [HeartbeatService](../../../src/main/assistant/heartbeat-service.ts);默认并发 1 时不会持有报告槽位等待监督取得同一个槽位 |
|
||||
| 心跳失败重领 | 最多 3 次尝试,失败退避 60 秒、300 秒;过期租约按领取条件重领 | [AssistantDatabase](../../../src/main/assistant/assistant-database.ts) 的 `claimDueHeartbeats` / `failHeartbeatRun`;与 HTTP 重试及未来成功批次续跑不同 |
|
||||
| 后台 tick | 30 秒一次,启动时也检查;`heartbeatTickRunning` 防重入 | [ipc.ts](../../../src/main/ipc.ts) 的 `runDueHeartbeats`;是到期检查周期,不是阶段超时或分钟级计划设置 |
|
||||
| 计划迟到 | 到期超过 2 小时记为 skipped 并计算下次时间 | [claimDueHeartbeats](../../../src/main/assistant/assistant-database.ts);这是漏跑处理窗口,不是模型超时 |
|
||||
| 活动轮询 | 有运行中记录时读取结束 2 秒后刷新,否则 10 秒;串行,离页停止 | [UI 设计](./ui-design.md#活动);只读展示,不终止任务、不触发模型 |
|
||||
| 提供商限流 | 尚无跨聊天与监督的提供商全局并发、RPM 或 TPM 调度限制,也无监督层 Retry-After 冷却 | 当前池只限制监督工作;普通聊天仍可能与之竞争同一上游额度。已核对直连文本 [model-runtime.ts](../../../src/main/agent/model-runtime.ts) 的 HTTP 失败分支和 `retryModelStream`:网络错误重试不等于按 429 或 Retry-After 退避;其他 Runtime 的内部策略需分别核实 |
|
||||
|
||||
阶段超时以各自读取设置时的值为准;并发数更新则影响池的后续派发。两个时限不包含数据采集和保存,不能相加视为整次 run 的最大耗时;清理也可能使实际返回晚于计时值。当前 FIFO 无排队超时或持久化等待记录,也没有按项目公平调度。池的生命周期取消不证明完整分块调度及所有子任务清理已实现。原始错误没有关联日志证据,仍不能确定是否来自旧四分钟定时器、传输、提供商或其他 Runtime。
|
||||
|
||||
## 2. 执行单位与范围
|
||||
|
||||
一次 run 对应一个冻结的 scope、时间区间和最终结果。项目、会话和消息块只是 run 内部的分组,不能各自创建故事线或把全局请求改写成多个项目 scope,否则既有实体身份将被分散。Global 在启动时分页解析并保存符合条件的项目集合及目录读取边界;后续新增项目留给下一次运行。正文按读取时版本处理,不持有跨模型调用的数据库事务,也不建立全库来源快照;成功批次仍须保存事实及其对应原文片段和定位。
|
||||
|
||||
手动新回顾重新读取所选区间,使用该 run 的持久化位置,不读取或写入自动 checkpoint;继续操作恢复同一逻辑 run。自动模式从 `stage=supervisor` 的已提交位置开始。批次输出及其恢复位置一起持久化,自动消费 checkpoint 只在最终完整结果提交时推进。两种位置分开,避免其他自动运行跳过尚未发布的事实;同 scope 有未完成自动 run 时先恢复它,不重复创建相同工作。
|
||||
|
||||
采集器按项目轮转、项目内按会话轮转;会话内遵循消息 sequence,同序时用消息 ID,任务和知识引用有各自的来源顺序。每轮先给各组一个块,再给仍有余量的组第二个块。分页在范围与时间过滤后执行,使用稳定排序键,不再使用“最新 20 个会话 × 最后 20 条消息”的预筛选。项目、会话目录也须分页,不能先加载全库正文再切片。页内只读元数据及即将派发的正文。
|
||||
|
||||
自动查询的排序与现有实现不同,需定向验证旧 checkpoint 仍能复用。项目目录、会话目录、消息页和其他来源页均保存恢复位置、稳定排序边界及来源版本;内存只缓存有界在飞批次。预算、失败或退出不删除已提交批次。续跑使用原冻结区间,不让滚动窗口移动丢掉该 run 尚未读取的尾部;来源删除或版本不符必须显式处理,不能当作已覆盖。只有目录和全部来源页耗尽、无空洞且汇总提交成功,才完成该 run。
|
||||
|
||||
任务按其实际项目归组;无项目任务仅在 Global 的独立组内处理。知识引用按所属消息分组,保留项目、会话、引用位置及本地或外部 locator。当前手动 collector 未输出这些完整分组字段,自动知识引用 locator 也需补所属消息元数据;这是采集接口的拟议改动。
|
||||
|
||||
## 3. 消息分块与背景
|
||||
|
||||
优先将一个会话中连续的短消息合成一个叶子任务;项目或会话改变就结束该块。单条长消息按段落或句界切分,找不到适当边界时按字符位置切分,后半段必须留在队列中。结构化任务、引用 JSON 保留完整字段及对象身份。超长说明按字段分段,游标仍映射到采集器原始 body 的位置;若暂未实现这种映射,则整条装入一个符合预算的块,装不下时明确失败,避免以重新序列化后的下标写入旧 checkpoint。
|
||||
|
||||
每个原文片段在运行内用 `source + revision + start + end` 标识,另保存 `projectId`、`conversationId`、`messageId`、角色、发生时间和原始 locator。消息来源的业务 `sourceId` 继续是会话 ID,不能用它代替消息身份。范围统一采用零基、左闭右开的 Unicode 码点位置,与 SQLite `substr` 的位置规则转换;JS 字符预算按 UTF-16 code unit 计算。中文、emoji、代理对和组合字符需有测试,不能用 JS `slice` 的下标直接推进 SQLite 位置。
|
||||
|
||||
允许加入前一片尾部或前一轮对话作为理解背景。背景注明原来源及位置,与本块的新证据分开传递;重叠区域计入模型输入预算,但不计新增字符、不推进 checkpoint、不独立产生事件。若结论只引用背景而没有本轮新证据,不把它作为新增变化输出。范围外的旧上下文也须标为背景,不能据此创建区间内事件。
|
||||
|
||||
已有摘要、同 scope 实体说明和已确认记忆同样属于背景。记忆是当前内容,任务状态也是当前状态,不推断历史快照。只有这些背景改变时,自动运行仍是 `no_change`。长消息完整分析后的累计覆盖是新证据区间的并集,不是包含重叠后的输入长度之和。
|
||||
|
||||
## 4. 设置配置清单
|
||||
|
||||
本节是监督者算法配置的权威清单,对应 FR-S2、FR-S4、FR-S6、FR-S10。目标是在“监督者 > 设置”开放有实际调优用途的参数,普通项常显,高级项按需展开;交互见 [UI 设计](./ui-design.md#算法配置设置目标)。除明确标为已实现的三个字段外,均为规划,不代表已有设置合同或控件。未测参数的产品默认值与数值范围均为待定,下表只给设计校验规则;原型参数单独列出,不作为推荐默认值。
|
||||
|
||||
### 普通配置
|
||||
|
||||
| 设置 | 单位、默认值与状态 | 用途与约束 |
|
||||
| --- | --- | --- |
|
||||
| 消息每页条数 | 条/数据库页;待实现,默认值与范围待定 | 正整数;只控制一次数据库 fetch 的消息数,不是总接纳消息上限、每次模型调用消息数或活动列表页大小 |
|
||||
| 监督模型并发数 | `supervisorModelConcurrency`;已实现,整数 1..4,默认 1 | 报告和监督共用;未来叶子、会话、项目及全局汇总也入同一池,不将项目并发与叶子并发相乘;外层 run 仍串行 |
|
||||
| 心跳报告模型超时 | `heartbeatReportTimeoutSeconds`;已实现,整数 30..600 秒,默认 240 | 报告模型阶段;当前读取时点及租约规则见第 1 节 |
|
||||
| 监督整理模型超时 | `supervisorOrganizeTimeoutSeconds`;已实现,整数 30..600 秒,默认 240 | 当前单次整理;未来作为未单独覆盖的节点超时,仍须按层测量,不声称现值适合所有节点 |
|
||||
| 单次执行软预算 | 秒/次执行;待实现,默认值与范围待定 | 到期停止读页、接纳和派发;已在飞节点在各自期限及硬期限内结束,校验后保存批次,再暂停。继续同一 run 可重新获得一次执行预算 |
|
||||
|
||||
消息页必须按稳定键持续读取至冻结范围耗尽,可跨多次执行继续。增大或减小页大小只改变数据库读取批量,不改变来源资格、最终待覆盖消息集合或语义分块边界。块可跨数据库页,但不能跨本设计规定的会话/项目边界;一条消息也可拆成多个片段。页面耗尽、批次封块和执行预算耗尽是不同事件,不能用页数或单轮接纳上限代替完整覆盖判定。
|
||||
|
||||
### 高级配置
|
||||
|
||||
下列分组均待实现。计数、字符、token、字节和时间分别校验,不使用一个“批量大小”兼任多个含义;数值上限需通过内存与真实模型测量后写入共享合同。
|
||||
|
||||
| 分组 | 拟开放参数及单位 | 设计校验、依赖与到限行为 |
|
||||
| --- | --- | --- |
|
||||
| 来源分页 | 项目目录每页项目数、会话目录每页会话数、任务/引用等来源每页记录数 | 各为正整数,与消息每页条数独立;只改变 fetch 大小,不限制范围总量。正文读取还受内存接纳预算限制,不能一次装入多条无界长消息 |
|
||||
| 叶子分块 | 每批不同消息数、每批片段数、每批新增正文字符数(UTF-16 单元) | 各为正整数;任一到限即封块,未装入内容继续排队。拆分消息按消息 ID 去重计数,片段另计;字符切分必须保留完整 Unicode 码点 |
|
||||
| 上下文与重叠 | 相邻片段重叠字符数、旧摘要字符预算、记忆条数及单条字符预算 | 非负整数,重叠可为零;均计入完整输入预算,不计新增正文及覆盖。输入预算不足时优先缩减可选背景,不能通过缩减新证据后丢弃余段来满足预算 |
|
||||
| 模型输入 | 每节点完整输入 token 上限、模型上下文容量、无 tokenizer 时的保守估算系数 | 输入包含系统指令、元数据、JSON、实体候选、背景与重叠。上下文容量优先读取实际模型配置,手工值不得超过已知能力;估算必须标明,不能冒充实测 token |
|
||||
| 模型输出 | 叶子与汇总各自的输出预留 token、输出字节预算 | 输入上限加输出预留不得超过模型上下文;预留还受提供商输出上限约束。用户预算不得放宽生产输出合同;超限拆小任务或显式失败,不截断 JSON、事实或引用 |
|
||||
| 摘要卡 | 叶子/中间导航卡的目标 token、字节预算 | 卡片只用于导航,完整事实独立保存;须能在父节点输入预算内放入至少两个孩子及必要元数据,否则拒绝该组合 |
|
||||
| 实体候选 | 每组候选数、候选描述字符/token 预算 | 数量与正文均有界,UUID 和名称保留;合法孩子身份继承规则不变,缩小候选预算不授权按同名合并 |
|
||||
| 汇总结构 | 每次合并孩子数(扇入)、最大汇总深度 | 扇入至少两个,单子透传;层级必须严格收缩,触及深度限制显式失败,保留叶子。产品默认及上限待测 |
|
||||
| 内存接纳 | 待执行块数、每轮新增字符/token、采集与在飞内容合计字节预算 | 约束内存和本轮接纳量,包含元数据、背景、摘要卡;到限暂缓读取/接纳并持久化成功批次。不得当作整次 run 的消息总额 |
|
||||
| 来源与排队 | 单页来源读取超时、队列等待超时(秒) | 分开计时;读取超时不能变成空页,排队超时保留恢复记录。同步 SQLite 查询须验证真实中断能力;等待不消耗模型时限 |
|
||||
| 节点时限 | 叶子、会话汇总、项目汇总、跨项目汇总模型超时(秒,可继承监督整理值) | 包含 Runtime 内部重试,各自获槽并解析 Runtime 后开始;显式覆盖需校验实际 Runtime 能力,不能自动沿用现有字段的数值范围 |
|
||||
| 阶段预算 | 单项目处理、全局汇总与最终发布前的阶段预算(秒) | 与单节点超时分开;到限停止新增工作并保存可恢复状态,缺失项目不能用空摘要补齐;发布事务保持原子性 |
|
||||
| 执行硬期限与清理 | 本次执行硬期限、独立清理期限(秒) | 硬期限不得早于软预算;到期向在飞节点传播取消,拒绝迟到提交,保留此前批次。清理单独计时和报告,不承诺实际返回时间等于硬期限 |
|
||||
| 调用与用量 | 每次执行及逻辑 run 累计模型调用数、token 预算;费用预算仅在价格和用量可可靠换算时开放 | 派发前预留,完成后核算;逻辑调用与 HTTP 尝试分别计数,重试也计入用量。累计预算续跑不归零,到限暂停;费用不可计算时显示不可用,不猜价格 |
|
||||
| 失败与续跑 | 自动续跑开关、最大自动尝试次数、重试退避起始/上限(秒) | 仅失败或未提交节点可重试;成功批次复用。次数有界、退避上限不小于起始值;初版不新增节点网络重试,复用 Runtime 策略,后续根据失败证据再开放对应字段 |
|
||||
| 上游额度 | 同额度组并发数、RPM(请求/分钟)、TPM(token/分钟)、429 冷却等待预算(秒) | 额度组按实际提供商账号/连接/模型共享规则确定;生效并发取监督池与额度组限制的较小值。遵守 Retry-After,等待预算不足则暂停,不能提前重发;冷却不占模型槽或报告租约 |
|
||||
|
||||
上游额度设置必须标明实际纳入的请求路径。仅监督接线时标为“监督请求额度”,不能称为提供商全局限额;覆盖普通聊天及相关 Runtime 后才能称为应用内共享额度,其他进程或客户端的消耗仍不可控。跨连接共享同一账号额度时需归入同组,不能按连接各放一份完整额度;未知共享关系不猜测。未支持的 Runtime 不显示可保存却不生效的控件。
|
||||
|
||||
### 配置冻结与续跑
|
||||
|
||||
目标设计中,保存设置仅影响新建 run;创建时保存解析后的有效配置及算法/提示词/输出合同版本,整个 run(含排队、节点和跨重启继续)使用同一份配置。共享模型池不能为不同 run 各创建独立上限:不同并发配置的执行须等待当前报告及监督工作排空,再采用该执行冻结的值;恢复旧 run 同样使用其原值,不修改已保存的新设置。这是待实现的生效规则调整;第 1 节记录的当前阶段读取与并发即时影响后续派发行为保持事实描述,不提前改称已冻结整个 run。
|
||||
|
||||
默认“继续”沿用原配置,不能因应用设置已保存新值而重切剩余片段。性能字段(分页条数、并发、等待/执行时限、速率与退避)不改变已保存事实的有效性,但本设计仍只在新 run 使用新值,不隐式修改恢复中的配置。语义字段(消息/片段/字符分块、背景重叠、模型与提示词、候选选择、输入输出预算、汇总结构)会影响产物;需要采用新值时新建 run,不将旧叶子按新规则冒充同一版本结果。
|
||||
|
||||
恢复时复查来源 revision、算法/提示词/输出合同版本及依赖。来源变更使该来源叶子及依赖汇总失效,其他有效批次保留;仅汇总规则失效可保留可验证的叶子、重做受影响汇总,叶子语义或合同不兼容则相应叶子及上层都需重做。软件升级后无法按原版本继续时明确说明并提供重新回顾,禁止静默套用新版本。这里只要求最小有效配置与依赖记录,不新增通用历史版本执行引擎。
|
||||
|
||||
累计调用/token 预算、成功批次和恢复位置须随 run 保存;普通恢复不能重置累计额度,也不能自动删除未完成 run 的唯一输出。后续若增加显式调整预算的续跑操作,需单独定义其生效范围与记录;当前清单不将保存设置视为该操作。
|
||||
|
||||
### 固定正确性条件
|
||||
|
||||
以下不提供关闭开关或可降低的阈值:冻结范围内全部目录与来源页耗尽、同 revision 连续无空洞覆盖、来源及精确位置校验、叶子事实与 provenance 保留、输出结构/身份校验、取消后的迟到提交拦截,以及最终发布与自动 checkpoint 的原子提交。覆盖率不得配置成低于完整覆盖的“成功阈值”;任何预算都不能授权静默截断或将未读内容标为完成。批次持久化与恢复所需记录不是可选性能开关。
|
||||
|
||||
### 原型参数依据
|
||||
|
||||
静态核对 [review-algorithm.mjs](../../../scripts/review-algorithm.mjs) 和 [review-deepseek.mjs](../../../scripts/review-deepseek.mjs):`pages` 默认 17 条/页,`chunks` 默认 8,000 UTF-16 单元/叶子且跨页累计;没有单批消息数/片段数上限、句界切分或重叠参数。`extract` 默认宽度 2、调用上限 `Infinity`,DeepSeek 入口仅接受并发 1..2,首次执行最多两次叶子调用,实验总 HTTP 上限 12(包含评估),单请求 180 秒,输出 16,384 token、200,000 字节校验上限。这些是脚本值和实验边界,未验证为产品默认或合法范围;尤其不能用原型 200,000 字节替换生产 100KB 合同,也不能采用无限调用作为产品默认。
|
||||
|
||||
没有可用 tokenizer 时用保守字符估算,并将计数明确标为估算值。上线前按模型、中文长文本和大量实体测量输入 token、首字延迟、总耗时、输出大小及调用数,再调整参数。证据数量大但每条很短时,也要计入元数据和引用 token 开销。
|
||||
|
||||
一次执行接纳量不限制整个逻辑 run 的范围。内存片段、元数据和摘要卡总量应有上界;达到上界就持久化已完成批次并暂缓派发,不能删来源。现有全局 100 条/48,000 字符裁剪须改为每节点校验及分页结果。存储不足明确暂停/失败,保留已保存内容及未完成状态,不通过删除叶子事实满足容量限制。
|
||||
|
||||
### 未来超时与清理清单(均未实现)
|
||||
|
||||
以下是调度设计项,不是现有设置字段或已接通的分块取消能力。没有测量依据的数值保持未定。
|
||||
|
||||
| 层次 | 拟议范围和到期行为 | 参数状态 |
|
||||
| --- | --- | --- |
|
||||
| 叶子模型 | 单块执行含底层重试;超时停止该节点,保留其他成功批次,恢复只重试未成功部分 | 未定 |
|
||||
| 会话汇总 | 有界卡片合并;失败保留所有叶子事实及引用 | 未定 |
|
||||
| 项目汇总 | 单项目各合并节点及项目阶段预算;不得用失败项目的缺省摘要替代完整结果 | 未定 |
|
||||
| 跨项目汇总 | 全局合并节点及最终发布预算;缺任一项目时不能标完成 | 未定 |
|
||||
| 来源读取 | 项目/会话/消息分页、任务及引用读取;读取超时不是空页或无变化 | 未定;同步 SQLite 查询不能仅靠 JS timer 中断,实现时需验证 |
|
||||
| 排队等待 | 区分等待与执行时间;到期移出待执行队列,保留恢复记录,不创建假完成结果 | 未定 |
|
||||
| 批次接纳预算 | 块数、字符、token、内存到限后暂停接纳;成功输出及连续位置先落库 | 未定 |
|
||||
| 单次执行与整次 run | 软预算停止派发并保存进度,硬期限取消在飞调用;逻辑 run 可跨执行继续,累计调用/token 预算不重置 | 默认与范围待测;费用预算以可可靠换算为开放前提,不能以预算耗尽视为完成 |
|
||||
| 提供商速率与冷却 | 按上方额度组与实际接线范围限制并发、RPM、TPM;冷却不占执行槽,不领取报告租约,预算不足时保留待恢复工作 | 未实现;先核实各 Runtime 已有重试与错误信息,避免重复退避;未覆盖聊天时只称监督请求额度 |
|
||||
| 用户取消与进程关闭 | 父 signal 到全部节点,停止读页/派发/发布,保留已提交批次,忽略迟到完成 | 待接类型化 IPC 和状态;关闭应用开关仍按既有规则,非取消 |
|
||||
| 清理期限 | 清 timer、监听器和 controller 注册,等待在飞调用终止,释放临时会话,核验本次拥有的远程进程已退出 | 独立期限未定;不得误停共享或其他运行资源,清理失败单独记录 |
|
||||
|
||||
有效节点期限不得超过本次执行剩余硬期限;软预算只停止新增派发,不立即取消已在飞调用。暂停、错误和用户取消分别记录原因;自动恢复与重试按配置有界执行,不能形成无限付费循环。上述清单的设置交互等实现后再开放,目前算法控制 UI 只显示报告/监督两个超时及共享监督并发数。
|
||||
|
||||
## 5. 队列及汇总算法
|
||||
|
||||
```text
|
||||
run(request):
|
||||
create or resume durable run with frozen scope, time range, runtime and effective configuration
|
||||
wait for existing supervisor run queue and compatible shared pool configuration
|
||||
load successful batches and page/source positions; read next bounded page
|
||||
if automatic, all eligible pages exhausted and no new evidence: save no_change; return
|
||||
admit bounded leaf tasks, round-robin by project and conversation
|
||||
run admitted leaves in a shared worker pool
|
||||
validate and persist each successful batch with facts, provenance and resume positions
|
||||
if budget expires, a task fails or cancellation arrives: stop; retain batches; mark incomplete
|
||||
continue paging all project/conversation/message/source groups until exhausted
|
||||
reduce within conversation, then project, then requested scope
|
||||
pass through singleton groups; reduce oversized groups in bounded rounds
|
||||
validate final output, identities, original references and coverage
|
||||
persist successful reduction nodes without replacing leaf facts
|
||||
if coverage is exhaustive and gap-free: publish result and automatic checkpoints atomically
|
||||
return completed only after publication; otherwise return resumable incomplete status
|
||||
```
|
||||
|
||||
采集与叶子执行可交错,但入队和在飞任务有界,不使用对全部来源的 `Promise.all`。每批完成后持久化,再接纳下一批;时间不足就暂停。首次派发前用有界探测判断是否直通;穷尽范围后确实只有一个叶子时直接生成最终输出,通过校验后只调用一次模型。因预算只接纳一个叶子时仍是未完成 run。空范围及自动 `no_change` 必须在全部相关目录/来源页检查完毕后判定,不调用模型、不把预算未读误作无变化。
|
||||
|
||||
叶子有多个时输出受限摘要卡及结构化事实,Main 保存卡片 token 到原文片段的映射。会话只有一个卡片时透传,项目只有一个会话摘要时透传,全局只有一个项目摘要时透传。不得为了层级外观额外调用模型。边界必须标清项目和会话,跨项目汇总仍使用原请求 scope 的候选实体集。
|
||||
|
||||
每次汇总只读取符合输入预算的一组有界卡片、少量背景和所需实体候选,不重新附上所有原文、所有中间摘要或全部候选描述。若一个大组有 N 个卡片,划成至少两个孩子的组,单个余项透传;下一轮卡片数必须严格小于 N。以卡片数量作终止度量,同时限制每张输出卡大小和最大深度。输入预算连两张卡都放不下时,重新缩小叶子卡输出目标或明确失败;禁止用同一内容反复“再总结一次”形成不收敛循环。
|
||||
|
||||
24 个叶子使用二叉合并时至多产生 23 个合并节点;分组层级不额外生成单子节点调用。最终合并节点直接生成最终结果,不再调用独立的润色模型。小任务直通和此调用上界都应有确定性测试。上述次数按摘要器逻辑调用计,底层 HTTP 重试应单独统计。
|
||||
|
||||
单节点 JSON 继续遵守事件、实体、关系和引用上限,整个 run 的事实结果使用分页读取,不能把最终 40 个事件等单输出上限套在叶子事实总集上。归纳层可以合并导航条目,但必须保留被合并的叶子 ID、事实及冲突;不能以父摘要覆盖孩子。一个对象展开后超过 20 个来源时,以分页关联读取完整引用或显式拒绝该节点,不能截断。完整叶子事实及 provenance 的存储和查询合同属于本设计的必要实现项,不再作为可选后续工作。
|
||||
|
||||
## 6. 来源与实体合并
|
||||
|
||||
来源归属由 Main 管理。模型只返回本节点收到的引用 token;中间卡片 token 由 Main 展开为原始来源 token,校验引用属于本 run、对应版本与范围,并存在于已保存片段集合。每个成功批次持久化叶子事实、原始引用映射、版本、范围和有界原文片段,恢复时不依赖内存映射。中间摘要只作导航;父摘要未列出的事实仍能从叶子结果分页访问。覆盖依据成功片段,不以最终摘要引用数推算,不复制完整用户库。
|
||||
|
||||
重叠输入的同一原文区间只登记一次;不同版本或不同片段保持可区分。不同 run 的来源 UUID 继续重新分配,底层 `source`、revision、位置和业务 `sourceId` 用于追溯,不能拿结果内 UUID 当增量身份。引用去重与实体合并是两件事。
|
||||
|
||||
实体采用两类明确身份:已有实体用允许候选中的 `persistedId`,本轮新实体先用带叶子命名空间的局部 ID。多个叶子引用同一合法 `persistedId` 时,Main 合成一个身份并改写事件和关系端点,再汇总描述及冲突,最终输出中的 persistedId 保持唯一。父节点可继承孩子已校验的身份,Main 同时校验它属于本轮原始候选集;候选描述的筛选不应使已确认合法的孩子身份丢失。
|
||||
|
||||
新实体只在有界汇总中显式给出“这些局部 ID 指同一概念”的映射后合并,并校验 ID 均来自孩子输出;同名、相同裸模型 ID 或相似描述都不是自动合并规则。保留未能判断的独立实体,接受候选集有界带来的漏复用风险,不新增语义检索服务。合并后从实体到事件、变化、关系及来源的引用必须全部重写,去掉合并导致的重复关系,并校验自关系是否仍有意义。
|
||||
|
||||
实体候选始终来自原请求的同 scope 故事线。不得将项目分组变成跨 scope 身份复用,也不批量修复历史重复实体。保存事务再次检查候选归属和撤销状态,沿用 [assistant-database.ts:8564](../../../src/main/assistant/assistant-database.ts#L8564) 的保护;已确认、已修订字段及已撤销关系不被自动结果覆盖。候选在执行中被撤销时整次保存失败,游标保持原值,下一次重新读取候选。
|
||||
|
||||
## 7. 超时、取消和部分失败
|
||||
|
||||
一个 run 持有父 controller,节点持有独立的超时 controller;父取消传到全部在飞节点。取消后停止采集、派发和结果提交,清除定时器并释放临时 Runtime 会话。节点超时只触发本轮失败,不取消其他运行或共享 Runtime。节点采用独立 requestId/conversationId;冻结默认 Runtime 选择,不因证据来自某个项目就切换到该项目远程执行环境。
|
||||
|
||||
当前监督通过无 projectId 的 [resolveRequestRuntime](../../../src/main/ipc.ts#L1492) 获取默认 Runtime。此设计只改变桌面侧编排,不要求新增 Agent 调度协议。后续实现若改动共享 Runtime 的取消或释放逻辑,必须检查 `gbagent` 是否共用该实现,并按仓库要求验证实际受影响的远程路径,不能据桌面测试推断远端行为。
|
||||
|
||||
摘要器应在流式输出期间检查字节上限,并要求完成事件;超时、用户取消、关闭进程、提供商失败、非法 JSON 和引用错误保留可区分的原因。调度层初版不重试节点网络错误,复用 Runtime 已有重试;模型格式错误也不无限“修 JSON”。先用小块降低失败成本,再根据实际失败数据决定是否值得增加一次有界纠正调用。
|
||||
|
||||
取消需要 Main 注册与执行生命周期连接,并扩展类型化 Preload/IPC;只让 Renderer 忽略响应不构成取消。取消先于最终发布事务开始则拒绝发布,保留此前批次;仅最终完整结果已经同步提交时,取消才返回“已完成”。单个批次已提交不能使 run 完成。关闭监督应用继续遵循现有规则:停止新领取,允许已开始的运行结束,不等同于用户取消。
|
||||
|
||||
| 情况 | 本轮结果与进度 | 用户可见结果 |
|
||||
| --- | --- | --- |
|
||||
| 叶子或汇总失败 | 保留已持久化成功批次和恢复位置,不发布完整结果、不推进自动消费 checkpoint | run 失败但可恢复;仅重做失败/未提交节点,旧完整结果仍可读 |
|
||||
| 用户取消或执行硬期限到达 | 停止在飞调用和发布,保留已提交批次 | 显示取消或预算暂停及可恢复范围,不标 `completed`;具体新状态需扩展合同 |
|
||||
| 执行软预算到达 | 停止读页、接纳和派发,在飞节点在节点及硬期限内结束并保存成功批次 | 未完成时显示预算暂停,可继续;若已满足全部完成条件则可正常发布 |
|
||||
| 批次或发布事务失败 | 该事务回滚;此前成功批次不回滚 | 从最后持久化位置恢复;发布失败不重算已有合法叶子 |
|
||||
| 达到采集预算,已接纳任务全部成功 | 保存有限覆盖批次及位置,仍有未读内容 | 显示“未完成,可继续”,不以 `completed` 加提示代替真实状态 |
|
||||
| 有来源读取失败或运行中被删除 | 初版按本轮失败处理,不将读取失败当作没有新证据 | 显示来源不可用的数量和范围,后续重新查询 |
|
||||
| 自动无新来源 | 保存 `no_change`,无新结果或图谱事件 | 保留旧结果,模型调用数为零 |
|
||||
|
||||
成功批次持久化是用户已授权的续跑要求。最小记录包含 run 冻结范围、目录/页位置、来源 revision 与连续 offset、已校验叶子输出及引用、必要的汇总依赖和成功输出。输出与对应位置同事务提交,以 run 和来源区间等稳定身份防止重复保存;不持久化每次进度动画或完整提示词。崩溃前未提交的在飞批次允许重算,已提交成功批次必须复用。表结构及保留/清理实现仍待设计落地,不能声称现有 schema 46 已支持此能力。
|
||||
|
||||
## 8. 游标与覆盖提交
|
||||
|
||||
同一来源片段可能乱序完成。恢复位置只能是同一 revision 从已提交 offset 开始、成功持久化区间连续覆盖的最远位置,不能取最大 end 跨过空洞;空洞后的成功批次单独保留,恢复时跳过其已完成工作。背景、仅读取内容和失败片段不推进。自动消费 checkpoint 在完整发布事务中推进,现有 `MAX` 合并位置的调用方仍须满足连续覆盖条件。
|
||||
|
||||
同一来源读到不同 revision 时不拼接版本;标明该来源及依赖汇总需重新处理,保留其他未受影响的成功批次。若最后读取之后来源修改,提交的仍是确实分析过的旧 revision,下一次查询因版本不符处理新版本。已保存旧事实及片段保留版本标识,不冒充当前内容;不还原未保存的历史正文,也不引入全库快照。
|
||||
|
||||
建议扩展 `coverage_json`:是否读完冻结范围、已处理新字符和片段数、覆盖项目/会话数、是否仍有来源、停止原因及恢复引用。精确片段继续用 locator,恢复记录关联批次输出,不仅保存一个总计数。总数未知时显示“仍有未读内容”,不用动态发现数充当固定分母。结果读取合同需显式暴露 coverage;当前数据库查询了 coverage,但共享结果 schema 未返回它。
|
||||
|
||||
手动和自动均须跨重启继续同一未完成逻辑 run。Main 根据持久化 run ID 读取冻结范围、成功输出及位置,复查来源版本,恢复剩余页和未成功汇总;Renderer 不能自报已处理 offset。手动继续不污染自动位置;自动恢复先于创建同 scope 新 run。恢复后的单次执行重新获得执行预算,但不丢失既有进度。
|
||||
|
||||
持久化续跑及手动“继续”入口是本设计的验收要求,不能以建议缩小范围替代。恢复记录的保留期限、主动放弃后的清理规则和具体 schema 尚未确定;不得在 run 未完成时自动删除唯一成功输出或继续所需位置。显式放弃可以终止后续工作,但不能标为完整覆盖。
|
||||
|
||||
## 9. 心跳接线与重复调用
|
||||
|
||||
手动 `supervision:run` 和心跳完成回调继续进入同一个服务及同一运行内队列。外层按计划唤醒仍归 `HeartbeatService`,不能让各项目、会话和叶子再调用 `heartbeat.runNow`、创建计划或再次进入 `supervision:run`。队列等待中的自动请求到执行时重新查询 checkpoint,从而看到前一个 run 的已提交结果。
|
||||
|
||||
初版保留既有报告语义:每次心跳最多执行一次原报告阶段,再执行一次分块监督;报告含记忆和任务建议,不能直接用监督 summary 替代而悄悄删除这些产物。报告阶段无新证据时零次模型调用,仍允许监督补读或重试;报告已经提交而监督失败,下次报告仍按自己的 checkpoint 判定,无变化则不再付费。设置中的“立即运行”目前会主动重做报告,此行为不与自动无变化混淆。
|
||||
|
||||
因此本设计消除的是分块引入的重复报告调用,不声称将现有两类模型输出合为一次调用。一个叶子的自动首次运行在两阶段都有新输入时仍为“报告 1 次 + 监督 1 次”;多叶时为“报告至多 1 次 + 叶子数 + 必要汇总数”,另计底层重试。报告只读报告阶段的有界输入,不能宣称它覆盖监督处理的全部片段。
|
||||
|
||||
若后续决定让心跳只触发监督、报告从同一结构化结果投影,必须先定义记忆建议和任务建议的共同输出契约、各自成功语义及旧报告展示,另行修改 FR-S4 和 US-S27;本轮不把该产品调整藏在调度重构里。也不使用多阶段事务将两个结果强行绑定。
|
||||
|
||||
报告先获共享槽位再领取租约,并在实际模型请求开始前刷新;报告完成或无变化提交后释放槽位,再执行下游监督。领取与续租条件见当前控制清单。未来整次执行预算不能套到报告租约,也不能在报告完成前等待整棵监督树。现有 tick 防重入不替代共享池上限;监督过长会延迟到期检查,拟通过有界执行和持久化续跑控制,不另建后台调度服务。
|
||||
|
||||
## 10. 活动与结果展示
|
||||
|
||||
沿用[现有活动设计](./ui-design.md)及[全局界面规范](../../../UI-DESIGN.md),活动仍是一条手动逻辑 run 或合并后的心跳 run。拟显示采集中、整理会话、汇总项目、生成回顾、保存中,以及已发现块数、成功块数、在飞数和新字符数。持久化成功批次提供可恢复进度,实时在飞数只存内存。发现未结束时显示“已处理 N 块,继续读取”,不用不可靠百分比。摘要、项目导航可展开分页叶子事实及完整来源。
|
||||
|
||||
计数在 `supervision:activity` 读取时与持久化运行信息合并,保持每页 50 条及串行轮询,不持久化每次计数更新。当前出队前没有 run 记录;未来排队/恢复操作需能定位逻辑 run。重启后从成功批次重建已完成计数,不恢复虚构在飞状态;待恢复记录不能被现有未完成记录处理逻辑误判为全部作废。
|
||||
|
||||
结束后以持久化状态为准:只有冻结范围穷尽、片段无空洞、必要汇总成功并发布结果,才能显示 `completed`。预算暂停或失败保留部分产物,显示“未完成,可继续”、实际覆盖和原因,不显示完成徽标。需要扩展活动/结果状态、coverage、恢复入口及中英文文案和 UI 测试;不能从摘要措辞猜状态。旧结果 coverage 缺失时显示“覆盖详情不可用”,不推断已读完。
|
||||
|
||||
取消按钮仅在 Main 接线可用时加入。拟增加暂停/未完成/可恢复及取消的明确状态语义,最终枚举和 schema 尚未实现;禁止用现有 `completed` 冒充部分成功。活动读取不调用模型,离页停止轮询,迟到响应不改写选择。旧完整结果与本次部分产物、失败原因分开展示,继续入口恢复原 run,不暗中重做全区间。
|
||||
|
||||
## 11. 验证方案与本次检查
|
||||
|
||||
实现时先用隔离 SQLite、生产采集与保存函数、可控摘要器和时钟验证调度规则,不修改用户数据库。以下生产端到端验收仍待执行;隔离原型已覆盖的子项及其限制见第 12 节和实施进度:
|
||||
|
||||
| 场景 | 必须验证的结果 |
|
||||
| --- | --- |
|
||||
| 全局七天、多项目、超过一页项目/会话、每会话超过 20 消息 | 过滤在分页前;逐页耗尽项目、会话、消息及其他来源,无静默裁剪;每个片段有来源定位 |
|
||||
| 一条超长消息及中文、emoji | 多片段覆盖连续、无意外缺口;Unicode 位置与 JS/token 预算分开;重叠不增加进度和事件 |
|
||||
| 小任务、自动无变化 | 小任务只有一次监督模型调用;背景变化单独触发时零次;不调用空的会话/项目汇总层 |
|
||||
| 24 叶子测试样本、多级汇总、超大候选及引用 | 报告及所有节点共用配置的监督池上限;每个节点满足预算;节点数严格收缩;二叉合并调用至多 23;引用展开超限显式处理,样本数不作为产品预算 |
|
||||
| 某叶失败、根汇总失败、保存失败及进程重启 | 已提交成功批次仍可读取;恢复不重新调用成功叶子;只重做未提交/失效依赖;完整发布与自动 checkpoint 原子提交 |
|
||||
| 预算结束、乱序完成及同源多个片段 | 批次输出及连续恢复位置原子提交;空洞后成功输出保留但不提前推进;同一冻结 run 续读尾部,绝不标 completed |
|
||||
| 叶子事实超过最终输出上限 | 所有已产出叶子事实和 provenance 分页可读;摘要遗漏、合并或缩短不删事实、冲突或来源 |
|
||||
| 来源修改、删除及窗口滑出 | 版本不混用;下一次处理新版本;窗口外未读不假装已完成,也不擅自扩大范围 |
|
||||
| 两叶同一 persistedId、同名新实体、关系端点变化 | 显式身份可复用;局部 ID 命名空间隔离;同名不自动合并;人工修订和撤销状态受保护 |
|
||||
| 节点超时、run 超时、取消及迟到完成 | signal 到达实际 Runtime,停止派发与提交,释放临时会话;原因可区分,其他请求不受影响 |
|
||||
| 报告与叶子/汇总竞争槽位、运行中调低并发、上游 429 | 共用监督池且不重入占槽;排队不消耗模型时限、不提前领取报告;未来冷却遵守提供商信息且不重复叠加 Runtime 重试;普通聊天与监督共享额度的行为按实际接线验证 |
|
||||
| 手动与自动先后入队、报告成功而监督失败 | 后续自动请求重读已提交位置;手动不改自动位置;每次心跳报告最多一次,监督重试不重复已成功且无变化的报告输入 |
|
||||
| 活动及继续入口 | 合并活动仍关联同一逻辑 run;部分覆盖不伪装完整;重启后从持久化输出重建进度;继续失败明确原因,手动恢复不改自动位置 |
|
||||
| 配置与分页 | 改变消息/目录页大小不改变最终入选集合和语义块;分别触发消息数、片段数、字符与 token 上限,余段仍被覆盖;跨字段无效组合不能保存 |
|
||||
| 配置生效与恢复 | 保存和恢复默认值只影响新 run;进行中与重启恢复沿用冻结配置;语义或来源版本失效只重做相应依赖,累计额度不被续跑重置;软预算与硬期限行为分开验证 |
|
||||
|
||||
实现后的定向测试应覆盖 `supervisor-service`、`incremental-review`、`supervision-activity`、相关 IPC 和 Renderer 场景,并检查所有参数上界及调用计数。真实模型性能验证另行执行,使用脱敏固定长文本,限制请求次数,记录不同输入规模的耗时和 token,不以模型替身测试宣称超时已解决。涉及 Runtime 生命周期变化时追加对应真实 Runtime 路径验证。
|
||||
|
||||
初稿只做静态核对。后续只读用户数据探测及 DeepSeek 隔离原型的测量、复现命令、范围和限制统一记录在[实施进度](./progress.md)。这些证据不证明生产分块调度已实现;原始 AbortError 仍缺乏明确 abort 原因的日志证据。
|
||||
|
||||
## 12. 隔离原型的设计结论
|
||||
|
||||
`scripts/review-algorithm.mjs` 实现了可执行的消息分页、码点分块、逐批 SQLite 保存、按已保存片段恢复、完整覆盖判断及叶子导航校验。`scripts/review-deepseek.mjs` 用配置中的 DeepSeek 完成整条目标会话验证;具体计数和耗时只在[实施进度](./progress.md#2026-09-24-deepseek-全目标会话隔离原型)维护。原型按已授权的续跑要求使用最小批次表,没有修改产品数据库。
|
||||
|
||||
来源覆盖、事实保留和短摘要须分别验收。覆盖以完整冻结消息集合及成功批次的区间并集判断;事实以精确来源引用和独立语义检查判断;导航必须索引全部叶子,生成前后核验叶子内容未变。真实实验中导航摘要确实省略了仍保留在叶子中的细节,因此产品查询和 UI 必须能访问叶子,不能只存最终 summary。模型的 reviewedTokens 回执仅参与结构校验,不能证明事实召回。
|
||||
|
||||
重复引文是已出现的定位歧义。原型保留同一已提交片段内全部精确匹配区间,标记 ambiguous 和候选数;这些位置都能重建原引文,但不冒充已确定的唯一发生位置,也不复制事实。生产接口需要携带该歧义或要求更长引文,禁止无提示地选第一次出现。来源中不存在的引文仍拒收。
|
||||
|
||||
成功叶子与覆盖区间一起提交,另一批校验失败不回滚已提交结果;重开时只处理缺失批次。测试脚本额外保存原始提供商响应,以便校验器修正后本地复核,不因此增加付费请求。该实验缓存不是新增的产品响应日志设计。无变化路径必须遍历所选批次并核验完整覆盖后返回零调用,不能因本轮预算耗尽就返回 no_change。
|
||||
|
||||
本次每叶 8,000 UTF-16 单元、并发 2、输出预留 16,384 token 只是在该模型和单会话样本上验证过的实验参数,不直接定为产品默认值。原型加载完整冻结来源,尚未满足生产有界内存采集;没有句界切分和上下文重叠,也未实现项目轮转、多层有界汇总、生产发布事务、自动 checkpoint 或继续 UI。导航关联保持空数组,跨项目身份和关系合并未作真实验证。第 11 节中的生产端到端场景仍需分别落实。
|
||||
@@ -0,0 +1,60 @@
|
||||
# 监督者应用产品设计
|
||||
|
||||
日期:2026-09-23。状态:工作回顾、图谱、活动和设置已接入;其余目标能力以[实施进度](./progress.md)为准。
|
||||
|
||||
本文定义监督者应用的定位、功能边界与知识库关系。[会话监督 PRD](./prd.md)继续负责对执行过程的观察、证据与介入边界;本文将该能力纳入更完整的工作回顾应用。
|
||||
|
||||
## 产品定位
|
||||
|
||||
监督者是持续关注指定工作范围的智能 Agent。用户可以主动要求回顾,也可以通过智能心跳定期唤醒它。它根据可见记录判断是否需要反馈、整理阶段或更新知识关联,没有值得反馈的变化时保持安静。
|
||||
|
||||
| 名称 | 职责 |
|
||||
| --- | --- |
|
||||
| 监督者 | 应用名称,负责观察、回顾和解释工作进展 |
|
||||
| 智能心跳 | 按计划唤醒监督者的机制,保留计划与运行管理 |
|
||||
| 监督反馈 | 会话右侧的阶段回顾、问题提示与证据入口 |
|
||||
| 故事线图谱(Story Graph) | 呈现事件、知识实体及其随时间发展的关系 |
|
||||
|
||||
FR-S1:将现有智能心跳入口整合、升级为监督者,不增加另一个平级重复应用。当前通过应用中心管理;未来应用市场提供同一应用的发现与启用入口。市场建设不应成为监督者落地的前置条件。
|
||||
|
||||
应用默认关闭,用户显式启用后才接受新的回顾工作;关闭时保留计划和历史。默认值、已保存选择、自动运行条件及在途运行边界由[应用启停与执行](./logic-design.md#应用启停与执行)统一定义。
|
||||
|
||||
FR-S2:应用包含“工作回顾 / 故事线图谱 / 自动监督 / 活动记录 / 设置”五个主视图。工作回顾提供统一监督结果阅读、历史选择和手动新回顾,结果显示自身范围与日期;自动监督集中展示计划状态、范围、周期及下次执行时间,以及运行概览、指标、趋势、自动监督报告、待处理记忆与行动建议和心跳审计,创建和编辑使用同一份 Modal 表单;活动记录展示统一实际执行、进度、详情及结果入口,支持按计划 ID 筛选;设置只管理超时、并发、分页、分批及执行预算,不嵌套次级菜单。计划保存失败保留草稿,关闭未保存修改须确认;编辑已暂停计划保持暂停,创建须显式保存并启用。原有运行记录及应用、存储标识保留。
|
||||
|
||||
当前设置还支持分别调整报告与监督整理的模型超时,以及两者共用的监督并发数。并发设置不限制普通聊天,也不保证避免上游限流;排队不消耗模型阶段时限,降低并发不打断在途请求。取值及生效规则由[当前控制清单](./review-scheduling-design.md#当前超时与调度控制清单已实现)定义。
|
||||
|
||||
FR-S10:活动同时覆盖手动监督回顾、定时心跳及用户立即运行的心跳。每条记录显示触发方式、范围、开始与结束时间、运行状态、失败原因和已有结果入口。同一次心跳的报告和下游监督回顾合为一条活动,报告保存成功不能掩盖下游仍在运行、暂停或失败。活动读取不调用模型,关闭应用后不查询活动。新批次运行支持暂停及继续,保留成功批次;人工修改的时间序列审计尚未实现,历史缺失不得猜测补齐。
|
||||
|
||||
FR-S2:分页只控制数据库读取批量,不能限制最终处理范围。当前已开放来源页大小、每批正文/消息上限、执行软预算;这些配置在新建回顾时保存,继续沿用。当前字段和未来高级参数的边界见[生产接线](./review-scheduling-design.md#0-生产接线与剩余边界),交互见 [UI 设计](./ui-design.md#算法配置设置目标)。完整覆盖、来源校验与事实保留不作为可关闭选项。
|
||||
|
||||
## 范围与触发
|
||||
|
||||
FR-S3:首次启用时选择项目、会话及可用来源,生成前显示范围和时间区间。应用启用不等于默认观察全部内容。已有心跳的 Global、单项目和多项目范围语义由[智能心跳 PRD](../smart-heartbeat/prd.md)定义,统一应用时保留。
|
||||
|
||||
FR-S4:支持手动回顾,并复用已有定时检查机制。自动报告和监督回顾分别只处理所选范围、回顾窗口内新增、修订或尚未处理的来源片段;没有待处理输入时跳过模型及结果生成。失败输入保留重试资格,预算遗漏不得记为已处理。手动回顾重新整理所选历史区间。具体规则见[自动增量与手动重看](./logic-design.md#自动增量与手动重看)。事件触发和阶段自动判定仍待设计。
|
||||
|
||||
FR-S5:会话右侧在跟随模式下展示当前会话的监督反馈;固定到 Conversation、Task 或 Experiment 时保持所选目标并显示其名称,切换聊天不改变监督目标。跨会话总结在完整应用中展示。反馈包含结论、来源和“在图谱中查看”定位入口。用户主动选择“发到当前会话”才加入聊天消息,同一结果不重复触发侧栏和全局通知。
|
||||
|
||||
FR-S6:监督者可提出整理与修改建议;已有会话监督的观察边界继续生效。自动更新图谱不能静默覆盖用户修改的描述、已确认的关系或已有知识库正文。取消整理后保留上次结果;失败说明受影响范围并允许重试,部分结果不得显示为完整回顾。
|
||||
|
||||
FR-S4、FR-S6、FR-S10 的验收要求:完整分页处理冻结范围内的入选来源;叶子事实和来源完整保留,摘要用于导航。预算耗尽或失败后保存成功批次与位置,重启后继续,不重复计算仍有效的成功批次;未读完或汇总未成功不能标完成。该续跑要求已进入生产接线,当前实现、实测范围与剩余差异见[回顾分块调度](./review-scheduling-design.md#0-生产接线与剩余边界)。
|
||||
|
||||
## 与知识库的关系
|
||||
|
||||
FR-S7:知识库管理资料、索引、检索和可复用知识;监督者管理工作事件、阶段总结与演变关系。故事线图谱共用现有来源,引用知识库对象,不复制一套资料正文,也不取代已有知识库图谱。
|
||||
|
||||
一个知识实体可以关联多份资料,一份资料也可支撑多个实体。图谱的关系与阶段记录由监督者维护,原始内容由所属会话、笔记或知识库维护。新增功能的概念实体与现有图谱实体是否共用标识,需结合现有模型制定技术方案。
|
||||
|
||||
FR-S8:本地知识库提供双向定位:资料详情可进入相关故事线图谱,图谱详情可返回资料及引用位置。已有本地条目显示“打开知识库条目”;新归纳提供“新建条目”或“补充已有条目”,先预览内容和引用再保存。外部引用沿用现有能力,打开已保存的有界片段及可用定位信息,不承诺重新打开远端原文;缺少位置时明确提示。外部知识库保持只读;不得因图谱整合而增加对 Dify、FastGPT、RAGFlow 的写入或批量同步。
|
||||
|
||||
FR-S9:来源事实、自动归纳、待核对关联和用户确认内容分别标注。移除关联不删除原文,纠正操作可撤销。来源失效时显示不可用状态;回放历史时使用当时已有的归纳与依据,无法恢复旧版原文时明确提示,不能以当前版本冒充历史内容。
|
||||
|
||||
## 落地范围
|
||||
|
||||
触发、整理与结果共享规则,以及整体架构和图谱对象关系见[逻辑设计](./logic-design.md)。
|
||||
|
||||
首个完整流程为:启用监督者、选择范围、生成回顾、进入图谱、核对来源、携带选定上下文继续讨论。知识库双向定位与内容补充随后接入同一流程。图谱关系辅助检索需单独验证效果,不作为首版默认检索策略。
|
||||
|
||||
当前工作回顾和图谱读取生产结果,活动展示实际执行与无变化检查、暂停和继续入口,自动监督设置复用已有计划。完整图谱回放及应用市场尚未实现,验收证据见[实施进度](./progress.md)。
|
||||
|
||||
具体 UI 与验收见[UI 设计](./ui-design.md)和[User Stories](./user-stories.md)。图谱对象与操作由[故事线图谱设计](../story-graph/prd.md)定义。已实现的实体身份和增量处理方式见[技术设计](./technical-design.md),来源旧版本能力仍受原应用限制。
|
||||
@@ -0,0 +1,299 @@
|
||||
# 监督者技术设计
|
||||
|
||||
## 文档信息
|
||||
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 状态 | 生产分页、持久化批次、暂停/继续及设置已接线;27 条消息完整会话通过真实模型验证,较大样本仍未完成;完整历史回放未实现 |
|
||||
| 日期 | 2026-09-23 |
|
||||
| 产品设计 | [监督者应用产品设计](./supervisor-prd.md) |
|
||||
| 行为规则 | [监督者逻辑设计](./logic-design.md) |
|
||||
| 界面设计 | [监督者 UI 设计](./ui-design.md) |
|
||||
| 实施进度 | SQLite schema 47;当前算法、设置和验收边界见[生产接线](./review-scheduling-design.md#0-生产接线与剩余边界) |
|
||||
|
||||
本文回答如何在现有 GoodBuddy 桌面端中实现监督者。它不改变产品范围,也不把模拟 Demo 当作生产数据模型。
|
||||
|
||||
生产入口由 `supervision-production.ts` 组装服务、SQLite、共享池和 ask Runtime。`supervision-review-store.ts` 保存冻结来源清单,按有界页和片段供 `SupervisorService` 派发;叶子及连续位置先提交,导航和完整结果之后发布。消息已有知识引用保留本地或外部 locator,已确认记忆通过独立背景输入提供。不会检索外部全库。详细存储、调度及当前限制统一见[分块调度第 0 节](./review-scheduling-design.md#0-生产接线与剩余边界)。
|
||||
|
||||
模型实体 `id` 只在单次输出内有效。Main 从同一 scope 的故事线提供最多 100 个未撤销实体候选,生产提示只暴露 `candidateRef`(如 `known_1`)、名称和说明。模型显式选择候选后,Main 转换为严格 UUID 类型的内部 `persistedId`。服务校验候选集成员和重复映射,保存事务再次按实体主键校验故事线归属及撤销状态。没有有效候选引用时分配新 UUID,不按名称或裸模型 ID 合并。来源仍按每次结果分配 UUID,保留原始 `source_id` 和 locator。
|
||||
|
||||
新实体省略 `candidateRef`,不要求模型生成数据库 ID。未知非空 `candidateRef`、冲突身份和候选集之外的 UUID 均拒绝。旧输出中的 `persistedId` 仅在 UUID 合法且属于候选集时复用;空值、占位符和格式错误值不能证明已有身份,保留为独立新实体。局部 ID 或同名不会触发合并。
|
||||
|
||||
真实旧库存在 `goodbuddy` 等文本实体主键。候选读取为这些键生成稳定 UUID 别名,并在 Main 内保留 `storageId` 到原键的映射;模型看不到这两个字段。别名使用 scope 和原键的 SHA-256 摘要构造 UUID v8,保存时按精确映射查询原实体,保留历史图谱引用和人工确认字段,不迁移或重写原主键。新实体仍由 Main 分配随机 UUID。此兼容仅处理已经存在的旧库键,模型返回同样的文本不能直接复用实体。实测见[真实失败运行修复](./progress.md#2026-09-24-真实失败运行身份修复)。
|
||||
|
||||
schema 42 为实体、实体变化和关系增加 `source_reference_ids_json`,保存映射后的来源 ID。升级只补列,旧记录、ID、确认状态及 locator 保留;此前未保存的引用使用空数组,已被覆盖或忽略的历史内容无法据此恢复。
|
||||
|
||||
schema 43 在结果上增加 `graph_snapshot_json`,保存当次实体名称、说明、确认状态、关系理由及本次来源引用。后续 run 只更新 `automatic` 的当前实体与关系,已确认、修订或移除状态受保护。历史结果读取自身内容;人工操作带 `resultId` 时先校验成员归属,在事务内更新当前对象及所选结果,其他结果不变。移除关系记录 `revoked`,防止后续相同端点和类型的自动关系恢复它。
|
||||
|
||||
升级从现有事件实体关联、实体变化和来源引用恢复结果成员,不猜测名称匹配;旧数据缺失的归属或已经覆盖的内容无法还原,原对象仍保留。新结果即使实体没有事件或来源,也会保存完整成员。结果内容与运行、事件、来源在同一事务提交。
|
||||
|
||||
`overview({ target? })` 返回稳定的 `id`、`storyLineId` 及来源 ID。指定 Conversation/Task 时,Main 读取目标实际项目,查询同时要求目标来源匹配、结果 scope 为 global 或包含该项目;LIMIT 在过滤之后。卡片展示结果真实范围,global 结果不会伪装成单会话摘要。`graph({ resultId, storyLineId? })` 校验二者归属,只返回该结果的事件、对象和来源;显式 ID 无效时不回退到最新结果。继续讨论按结果主键读取,并验证来源属于该结果。
|
||||
|
||||
工作回顾保存所选结果,切换或刷新使用请求序号忽略迟到图谱。侧栏按目标重建卡片,清空来源和预览,并忽略旧请求;工作栏 tasks 实例的 `targetRef` 可保存 conversation/task 固定目标,取消固定后恢复跟随当前选择。该绑定只控制监督卡片,不改变任务列表的项目范围。继续讨论预览显示实际目标名称和 ID,发送回调显式携带该会话 ID,由 Main 使用该会话的 Runtime 与项目。
|
||||
|
||||
侧栏通过 `onOpenSupervisionGraph(resultId)` 交给 App 导航;App 遵守现有离页检查,向 HeartbeatCenter / SupervisorWorkspace 传递类型化 `graphNavigation`,复用 `heartbeat` 路由及 keepalive 页面。每次点击创建新的导航请求,打开 graph 页签并更新历史选择共用的 resultId。overview 未包含该 ID 时仍直接请求其 graph;旧 overview、graph、来源和操作响应通过同一请求序号失效,不覆盖新结果。没有新增 window 事件、页面或持久化字段。
|
||||
|
||||
继续讨论入队时,无附件请求省略 `serializedContexts`;请求未指定 Runtime 时使用会话保存的选择。知识库预览与提交均检查目标库存在且为本地可写库,更新实体还检查其实际所属库。侧栏确认框展示 Main 返回的实体字段、目标库和来源正文,取消或写入失败后可以重新预览。
|
||||
|
||||
监督来源清单将 UI 时间区间归一化为 UTC,先筛选再按稳定键分页。项目必须存在且 active;消息按闭区间筛选,任务按区间内创建或完成时间筛选。手动和自动监督共用该路径,自动清单另读取 supervisor checkpoint;手动心跳报告保留原有 collector。
|
||||
|
||||
已确认记忆作为当前背景,不参与新增正文覆盖。最多四条、每条 500 字符,时间来自真实更新时间;模型不能把背景引用为本批新增证据。旧结果继续保留原来源类型和 locator。监督正文上限现用于分批,余段继续处理;心跳报告的有界输入语义没有改变。
|
||||
|
||||
## 模型阶段超时与调度
|
||||
|
||||
监督整理超时由 run 创建时冻结;共享模型池仍采用实时设置上限。`supervision:pause` 取消该 run 的排队和在途工作,`supervision:resume` 继续已保存批次;监督整理输出在流中按 `supervisionReview.responseKiB` 检查响应容量,默认 1024 KiB,可在设置页调整为 100 至 16384 KiB。该容量每次请求读取当前设置,继续旧运行也采用新值;超限保留成功批次并提示调高后继续。生成摘要及事实内容不设独立短字符限制,监督旧摘要读取保留全文,具体边界见[生产接线](./review-scheduling-design.md#0-生产接线与剩余边界)。以下报告领取和租约规则继续适用。
|
||||
|
||||
手动和自动监督均在桌面 Main 调用生产工厂,Runtime 解析请求只有 `workMode: ask`,不携带 SSH 项目或执行空间。摘要校验、合并和持久化在 Main 完成;本次取消内容长度限制不改变 `gbagent`、远端 Runtime 启动或桌面到 Agent 协议。
|
||||
|
||||
`ApplicationSettingsStore` 持久化报告和整理模型超时,均为 30..600 整数秒,默认 240。报告排队前冻结,监督创建 run 时冻结;计时从获槽及 Runtime 解析后开始,覆盖模型执行及其内部重试。流结束校验 signal 和完成事件,finally 释放临时会话,心跳报告保持原有 100KB 上限,监督整理使用上文的可调响应容量。采集、排队、保存和清理不计入模型超时。
|
||||
|
||||
`supervisorModelConcurrency` 为 1 至 4 的整数,默认 1。报告和监督整理共用 Main 内 `SupervisionModelPool` FIFO 池,普通聊天不入池;降低上限不终止在途请求。报告槽位从领取前持有至报告完成或无变化提交后,在下游监督开始前释放,避免并发 1 时相互等待。监督服务外层运行仍串行;该池不是完整分块调度器,也不提供跨聊天与监督的提供商全局并发、RPM、TPM 或 Retry-After 控制。
|
||||
|
||||
报告租约为 `max(300, reportTimeoutSeconds + 60)` 秒,300 是下限;每次到期领取最多一条。Runtime 解析后、模型请求开始前按冻结超时刷新租约,数据库要求运行 ID、owner、attempt、claimed 状态和未过期条件均匹配,否则拒绝启动模型。直连模型独立的 600 秒传输超时、网络重试、心跳重领及轮询不属于用户阶段时限。设置 UI 提供两个超时、共享并发及分页/分批/软预算字段。
|
||||
|
||||
schema 47 的四张批次相关表使用外键随监督运行清理,未完成运行不新增自动过期删除规则。启动时原有 running 记录会标 failed,但已保存批次和配置保留,活动页仍可继续。发布逐页读取叶子并复用现有结果/来源/图谱写入,在同一事务更新完成状态和自动 checkpoint。
|
||||
|
||||
当前设置及冻结范围见[当前设置合同](./review-scheduling-design.md#当前设置合同)。未实现的高级参数继续保留在目标清单,不显示为可保存控件;没有兼容原型数据库的读取器。
|
||||
|
||||
## 自动增量收集
|
||||
|
||||
本节的 100 来源/2,000 码点批次查询现用于原心跳报告及旧服务测试;生产监督使用 schema 47 清单及批次位置,自动 checkpoint 只在完整发布时提交。已有 schema 46 指纹算法和 checkpoint 仍复用,不重置用户已处理位置。
|
||||
|
||||
对应 FR-S4、FR-S10 和 US-S27,行为定义见[自动增量与手动重看](./logic-design.md#自动增量与手动重看)。schema 46 在现有数据库增加 `review_checkpoints`,主键为阶段、规范化 scope 和来源标识,保存版本指纹、已处理位置和来源长度。来源标识区分消息 ID、任务 ID 和消息内知识引用位置,不使用消息时间戳作为身份。
|
||||
|
||||
消息正文写入时由 SQLite trigger 更新轻量 `review_revision`;范围、标题、角色和时间共同参与版本指纹。任务标题、状态、创建和完成时间,以及知识引用内容或定位变化分别使对应来源待处理。工具执行元数据更新不会把未修改的消息正文再次标为新增。删除来源时清理其进度。原始消息不复制到进度表;自动查询先排除已处理版本,再读取待处理片段,不重新载入每个会话的历史正文。
|
||||
|
||||
原心跳增量查询按来源时间和 ID 排序,每轮最多 100 个来源、每个最多 2,000 码点,正文预算 12,000 UTF-16 单元。该报告路径的余段留待后续检查,窗口外来源不自动补读。生产监督另用冻结清单和持久化批次,未读尾部不会因继续时的滚动窗口变化而消失;旧监督分支的 44,000/48,000 字符上限已退出生产接线。
|
||||
|
||||
只有实际送入模型、通过结果校验且成功落库的完整片段推进位置。心跳报告及其进度、监督结果及其进度分别在各自 SQLite 事务提交。监督服务串行执行,后续自动请求在前次保存后重新收集。报告成功而监督失败时,下一次自动检查跳过已成功的报告输入,但重试监督输入;无变化不创建报告、来源或图谱事件。
|
||||
|
||||
已有同 scope 故事线的 scope 表示和实体 ID 保留;比较项目集合时忽略排序。模型接收最近摘要和同 scope 最多 100 个未撤销实体的候选引用、名称、说明,显式选择候选才复用,身份转换按上文执行。不同范围不合并,已有重复实体不自动修复。知识引用继续以独立来源保存 locator;当前记忆和旧摘要不作为新事件。
|
||||
|
||||
迁移在事务中扩展心跳计划及运行表的状态 CHECK,保留列、索引、ID、计划、报告与引用;消息版本列使用默认值,无需遍历或重写旧正文。进度初始为空,首次自动运行从配置窗口开始。报告保留期限覆盖 `no_change` 运行,但不删除来源进度;删除报告不触发重新消费。
|
||||
|
||||
## 活动读取与执行持久化
|
||||
|
||||
对应 FR-S10、US-S26。schema 45 在既有 `supervision_runs` 增加可空 `heartbeat_run_id` 和唯一索引,在 `heartbeat_runs` 增加执行范围、下游投影状态、错误及结束时间字段。迁移保留原记录,不补造旧关联。监督服务通过 store 的 start/fail 接线记录执行,保存结果事务更新同一 run;不增加调度器。心跳在执行前记录冻结范围和投影进度,报告事务仍保持原有含义,下游失败单独保存。手动心跳在生产路径使用实际结束时间,活动等待下游结束后才显示整次执行的结束时间。
|
||||
|
||||
`supervision:activity` 沿用可信 sender 与 Zod 输入校验,接受 `limit`(1 至 100,默认 50)、`offset`(0 至 100000)及可选 `configId`。应用未启用时返回空列表且不访问活动表。未筛选时 SQL 合并心跳及未关联的监督记录;筛选时在 LIMIT/OFFSET 前按 `heartbeat_runs.config_id` 精确匹配,排除独立手动回顾,不依赖计划名称或范围。关联查询使用运行 ID;返回状态、阶段、范围、时间、错误、摘要和 resultId,不调用模型。保留规则和历史缺失见[活动记录](./logic-design.md#活动记录)。
|
||||
|
||||
Preload 暴露类型化 `supervision.activity()`。`overview({ resultId })` 支持按 ID 读取历史回顾,供活动跳转使用。Renderer 只在活动页签和 heartbeat 路由同时激活时串行定时读取,每次有界分页;effect 清理撤销定时器并使旧响应失效。App 关闭应用时卸载页面;读取没有执行副作用。该改动位于桌面 Main/Renderer,未修改 Agent 或远程 Runtime 协议。
|
||||
|
||||
活动进度现在包含既有运行 JSON 中的持久 `phase` 及导航表计数 `navigationNodes`。`SupervisionReviewStages` 将实际阶段与监督运行状态组合呈现;`SupervisionBatchDetails` 按既有 10 批分页结果组织项目、会话和批次,新增批数变化时刷新已打开页。导航和叶子使用不同的严格输出 schema,继续仍复用成功叶子与导航节点。字段语义、旧记录缺失和同步阶段限制由[生产调度合同](./review-scheduling-design.md#已接入的调度与存储)统一定义;未增加任务调度器、数据库表或远程协议。
|
||||
|
||||
## 1. 总体方案
|
||||
|
||||
监督者作为 Main 进程中的一个领域服务,复用现有智能心跳的计划、运行领取、租约、重试和有界模型调用。图谱是监督者整理结果的查询视图,知识库继续拥有资料正文、索引和检索。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
UI["Renderer\n监督者页面 / 图谱 / 侧栏"]
|
||||
IPC["Preload + IPC\n类型化输入输出"]
|
||||
SVC["SupervisorService\n范围、运行、结果、用户确认"]
|
||||
HB["HeartbeatService\n计划、领取、租约、重试"]
|
||||
EVIDENCE["EvidenceCollector\n按范围读取有界证据"]
|
||||
MODEL["BoundedSummarizer\n只读 JSON 输出"]
|
||||
STORE["AssistantDatabase\nSQLite 事务与查询"]
|
||||
KB["KnowledgeService\n本地资料、引用、检索"]
|
||||
CONV["会话 / 笔记 / 任务"]
|
||||
|
||||
UI --> IPC --> SVC
|
||||
HB --> SVC
|
||||
SVC --> EVIDENCE
|
||||
EVIDENCE --> CONV
|
||||
EVIDENCE --> KB
|
||||
SVC --> MODEL
|
||||
SVC --> STORE
|
||||
SVC --> UI
|
||||
SVC -->|确认后补充本地条目| KB
|
||||
```
|
||||
|
||||
职责边界如下:
|
||||
|
||||
| 模块 | 负责 | 不负责 |
|
||||
| --- | --- | --- |
|
||||
| `HeartbeatService` | 计划、到期运行、租约、重试、已有报告兼容 | 图谱实体合并、用户确认、知识库写入编排 |
|
||||
| `SupervisorService` | 触发统一、证据收集、整理结果、图谱查询和修订 | 自己实现调度器、复制来源正文 |
|
||||
| `EvidenceCollector` | 按冻结范围读取会话、笔记、任务、知识库引用 | 越过范围读取全部数据、调用工具 |
|
||||
| `SupervisorSummarizer` | 以有界输入调用文本模型并解析结构化输出 | 执行工具、直接修改知识库 |
|
||||
| `AssistantDatabase` | 事务、外键、分页、状态和来源定位持久化 | 生成模型内容 |
|
||||
| `KnowledgeService` | 资料和本地知识条目的维护、检索及引用 | 管理监督者事件和关系 |
|
||||
| Renderer | 展示和发起用户操作 | 访问 SQLite、决定权限或拼接 SQL |
|
||||
|
||||
## 2. 运行路径
|
||||
|
||||
应用启停遵守[逻辑规则](./logic-design.md#应用启停与执行)。`ApplicationSettingsStore` 对新设置及缺失的 `heartbeatEnabled` 使用 `false`,保留显式布尔值。Main 在心跳 tick 调用 `processDue()` 前、`heartbeatsRunNow` 与 `supervisionRun` IPC 入口、心跳完成后的监督回顾回调中读取已保存设置,只有值为 `true` 才继续。此检查不取消已领取运行,也不改变普通任务调度链路。Renderer 由 App 将同一启用状态传入 `RightAssistantSidebar`,控制监督面板挂载。
|
||||
|
||||
`HeartbeatCenter` 的五个顶层页签直接选择内容,无内部页签。overview 只由 `SupervisorWorkspace` 读取 `supervision.overview` 的统一结果,历史选择和图谱共享 resultId;新请求控件不改变所选结果的 scope/timeRange。`HeartbeatSections` 仅在 plans 输出内容,包含 `HeartbeatSettings`、范围与刷新、运行概览、指标、趋势、自动监督报告、记忆与行动建议及审计;activity 只渲染 `SupervisorActivity`,settings 只渲染两个算法参数表单。运行中的手动请求保留既有结果,活动入口清空计划筛选并聚焦 activity。计划列表放在当前状态下,复用唯一创建按钮及未配置说明。计划表单在原组件内通过 body Portal 显示,复用 `custom-task-dialog` 和 `activateModalFocus` / `trapTabFocus`,没有复制表单或嵌套 form。关闭确认、提交锁定及失败草稿由本地状态管理;保存继续调用 App 原有心跳 CRUD 回调。
|
||||
|
||||
计划“执行记录”将 `activityPlanId` 交给活动查询,并切换、聚焦 activity 页签。更换计划时清空旧列表、重置 offset 并清理旧轮询,保留筛选控件的键盘焦点;该筛选不传入自动监督概览,返回 plans 时仍显示全部计划的统计和心跳审计。报告展开和追加条数保存在同一组件内,切换顶层页签仍保留;图谱继续由 `SupervisorWorkspace` 持有结果选择,活动结果跳转保留精确 resultId。此次仅调整桌面 UI 和只读活动查询,不修改 schema、Agent 或远程 Runtime。
|
||||
|
||||
计划 CRUD 沿用 App → Preload → Main 接线。`heartbeatRecurrenceSchema` 与 `computeNextHeartbeatRun` 仅支持 daily/weekly;本轮不增加分钟间隔合同或调度分支。编辑草稿保存已有 `timezone` 和 `enabled`,新建仅在显式提交时使用本机时区创建启用计划。UI 命名统一为自动监督,内部 ID、翻译键、数据库及 IPC 名称保留。
|
||||
|
||||
手动回顾和心跳回顾必须经过同一个 `SupervisorService.run()`,只在触发来源上区分。这样可以保证两者使用相同的范围校验、输入预算、输出契约、结果状态和来源处理。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant T as 手动/智能心跳
|
||||
participant S as SupervisorService
|
||||
participant D as AssistantDatabase
|
||||
participant C as EvidenceCollector
|
||||
participant M as 文本模型
|
||||
participant R as Renderer
|
||||
|
||||
T->>S: run(scope, timeRange, trigger)
|
||||
S->>D: 创建 run,冻结范围与时间
|
||||
S->>C: collect(frozenScope)
|
||||
C-->>S: 有界证据 + source references
|
||||
S->>M: 结构化整理请求,禁止工具
|
||||
M-->>S: validated result
|
||||
S->>D: 事务写入 result、events、entities、relations、references
|
||||
D-->>S: completed
|
||||
S-->>R: 状态和定位信息
|
||||
```
|
||||
|
||||
运行状态与内容确认状态分开保存:
|
||||
|
||||
- 当前监督运行状态:`running`、`paused`、`completed`、`failed`、`no_change`。心跳及合并活动的映射见[活动记录](./logic-design.md#活动记录)。
|
||||
- 内容状态:来源事实、自动归纳、待核对、用户确认、用户修订、已移除关系。
|
||||
- 运行成功只表示模型结果已按契约保存,不表示所有实体和关系已经被用户确认。
|
||||
- 失败保留上次完整结果和本次已保存批次;暂停不生成完整结果,继续入口恢复同一 run。
|
||||
|
||||
## 3. 数据模型
|
||||
|
||||
首版使用现有 `AssistantDatabase` 和同一 SQLite 数据库。新增表保持领域对象独立,但不复制会话、笔记或知识库正文。
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
supervision_runs ||--o{ supervision_results : produces
|
||||
supervision_results ||--o{ story_events : contains
|
||||
story_lines ||--o{ story_events : organizes
|
||||
story_lines }o--o{ knowledge_entities : references
|
||||
story_events }o--o{ knowledge_entities : changes
|
||||
knowledge_entities ||--o{ knowledge_relations : source
|
||||
knowledge_entities ||--o{ knowledge_relations : target
|
||||
supervision_results ||--o{ source_references : cites
|
||||
story_events ||--o{ source_references : cites
|
||||
knowledge_entities ||--o{ source_references : cites
|
||||
knowledge_relations ||--o{ source_references : cites
|
||||
```
|
||||
|
||||
建议的最小持久化对象:
|
||||
|
||||
| 对象 | 关键字段 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `supervision_runs` | `id`, `trigger`, `scope_json`, `time_range_json`, `status`, `error`, `started_at`, `completed_at` | 一次手动或心跳整理;保存冻结范围,不从当前配置回推历史 |
|
||||
| `supervision_results` | `id`, `run_id`, `summary`, `change_digest`, `coverage_json`, `graph_snapshot_json` | 工作回顾、监督反馈和图谱共同读取的结果版本 |
|
||||
| `story_lines` | `id`, `scope_json`, `title`, `created_at`, `updated_at` | 用户选择的工作故事线,不等同于 Project |
|
||||
| `story_events` | `id`, `story_line_id`, `result_id`, `occurred_at`, `title`, `description`, `event_type`, `confidence` | 时间轴上的事件;同日聚合只在查询或 UI 层处理 |
|
||||
| `knowledge_entities` | `id`, `canonical_label`, `description`, `state`, `confirmation_state`, `updated_at` | 可被多条故事线引用的持续认识,不与知识库文档一一对应 |
|
||||
| `entity_changes` | `event_id`, `entity_id`, `change_type`, `description`, `confirmation_state` | 表达提出、补充、验证和修订,避免覆盖实体正文 |
|
||||
| `knowledge_relations` | `id`, `from_entity_id`, `to_entity_id`, `relation_type`, `reason`, `confirmation_state` | 关系独立拥有状态和来源;移除关系不删除来源 |
|
||||
| `source_references` | `id`, `owner_type`, `owner_id`, `source_type`, `source_id`, `locator_json`, `availability` | 只保存来源标识和有界定位,不复制正文 |
|
||||
|
||||
外键和索引要求:
|
||||
|
||||
- 新增监督者对象使用 UUID;已有文本实体主键按上文兼容映射保留。实体、来源和关系通过稳定 ID 关联,不能只按名称合并。
|
||||
- `supervision_runs` 按 `created_at`、`scope` 查询;事件按 `story_line_id, occurred_at` 查询。
|
||||
- 关系对 `(from_entity_id, to_entity_id, relation_type)` 建唯一约束,允许同一实体对存在不同关系类型。
|
||||
- 删除故事线只删除其组织关系,不删除共享实体、来源或知识库资料;删除知识库资料后来源标为不可用。
|
||||
- 整理结果、实体变化、关系变化和来源引用在同一 SQLite 事务中提交,提交失败全部回滚。
|
||||
- 只保存整理结果自身的对象内容与有界来源,不建立原会话、记忆或知识库的完整历史快照和独立向量索引。
|
||||
|
||||
叶子模型输出使用严格共享契约,包含 `events`、`entities`、`entityChanges`、`relations`、`summary`、`changeDigest`、`openItems`,事实项保留 `sourceReferenceIds`。Main 校验数量、字符、枚举、ID 所属范围和引用存在性,拒绝未知字段后才保存。导航输出另用[导航合同](./review-scheduling-design.md#已接入的调度与存储),允许省略空事实数组;该规则不适用于叶子。
|
||||
|
||||
## 4. Main、IPC 与 Renderer
|
||||
|
||||
新增能力沿用现有边界:Renderer 只传业务输入,Main 重新校验项目、故事线、时间范围、来源和权限。
|
||||
|
||||
建议的 IPC 分组:
|
||||
|
||||
| 通道 | 用途 |
|
||||
| --- | --- |
|
||||
| `supervision:overview` | 工作回顾、运行状态、未解决事项和最近结果 |
|
||||
| `supervision:run` | 手动开始一次整理,完成后返回包含 runId 的结果 |
|
||||
| `supervision:activity` | 有界读取真实执行和阶段状态 |
|
||||
| `supervision:graph` | 按故事线、时间区间和回放时刻分页读取图谱 |
|
||||
| `supervision:object` | 读取事件、实体、关系及来源详情 |
|
||||
| `supervision:confirm` | 确认、修订或移除自动关系和实体变化 |
|
||||
| `supervision:continue-context` | 生成继续讨论前的上下文预览 |
|
||||
| `supervision:knowledge-preview` | 预览新建或补充本地知识条目 |
|
||||
| `supervision:knowledge-commit` | 用户确认后调用知识库服务写入 |
|
||||
|
||||
IPC 返回图谱查询结果时使用分页和有界字段;详情中的来源正文复用现有会话、笔记和知识库读取路径。不要把完整图谱一次性发送给 Renderer,也不要把模型原始输出暴露为可执行内容。
|
||||
|
||||
侧栏反馈通过已有应用状态刷新机制读取结果。首版可以在手动运行完成后主动刷新,心跳完成后发布一个不含私人正文的变更通知,Renderer 再按权限读取摘要;不新增系统通知正文通道。
|
||||
|
||||
## 5. 安全与一致性
|
||||
|
||||
- 监督者模型调用固定为 `ask` 语义,输入来自 `EvidenceCollector` 的有界快照,工具授权始终拒绝。
|
||||
- `Global`、Project、多项目和固定 Conversation/Task/Experiment 目标在 Main 冻结;模型不能生成或改变范围。
|
||||
- 每个来源引用必须属于本次冻结范围,跨范围引用、伪造项目 ID 和不存在的来源 ID 直接使结果失败。
|
||||
- 心跳运行沿用现有 `heartbeat_runs` 的领取、租约和重试机制;新增监督运行按心跳运行 ID 关联,活动据此合并阶段。没有新增取消协议。
|
||||
- 同一配置同时触发手动和定时运行时,沿用现有幂等键和运行领取规则;已完成结果不能重复写入。
|
||||
- 用户确认或修订后的实体、关系不被后续整理静默覆盖;新证据以补充或冲突待核对状态写入。
|
||||
- 外部知识库只读。图谱可以引用保存到本地的外部片段和定位信息,不调用远端写入、不承诺打开远端原文。
|
||||
- 来源正文仍由原应用拥有。监督者删除关系、故事线或运行记录时不得删除会话、笔记、任务或知识库资料。
|
||||
|
||||
## 6. 实施顺序
|
||||
|
||||
### 阶段 A:先把现有心跳变成统一运行入口
|
||||
|
||||
- 抽出 `HeartbeatSummarizer` 的有界输入和结构化输出校验,使 `SupervisorService` 可以复用。
|
||||
- 增加手动运行的统一服务入口,但先只生成现有回顾结果,不改变心跳页面行为。
|
||||
- 为运行状态、范围冻结、失败保留旧结果补充 Main 单元测试。
|
||||
|
||||
### 阶段 B:接入证据引用和阶段回顾
|
||||
|
||||
- 实现 `EvidenceCollector`,接入会话、任务、消息已有知识引用和记忆;魔法笔记仍未接入。
|
||||
- 建立 `supervision_runs`、`supervision_results` 和 `supervision_sources`,保存来源 locator,并通过 `KnowledgeService` 解析本地文档/分块。
|
||||
- 在现有智能心跳页面增加监督者工作回顾入口;不先改应用名称和导航 ID。
|
||||
|
||||
### 阶段 C:实现最小故事线图谱
|
||||
|
||||
- 建立事件、实体、实体变化和关系表及查询服务。
|
||||
- 先完成平铺环绕时间轴、节点详情、来源展开、回放和列表模式;圆形视图继续作为后续布局。
|
||||
- 同一实体通过稳定 ID 连接多个事件,禁止按每次事件复制实体。
|
||||
- 用真实整理结果替换 Demo 的模拟数据,并保留 Demo 作为视觉回归 fixture。
|
||||
|
||||
### 阶段 D:接入用户确认和知识库双向定位
|
||||
|
||||
- 增加关系确认、修订、撤销和实体拆分的最小交互。
|
||||
- 从图谱返回本地会话资料及引用位置;知识库文档定位依赖来源中存在文档/分块元数据。
|
||||
- 新建或补充本地条目必须经过预览和确认;外部知识库保持只读。
|
||||
|
||||
### 阶段 E:侧栏反馈与增量整理
|
||||
|
||||
- 将结果摘要投影到现有右侧助手栏,并沿用当前会话生命周期。
|
||||
- 心跳只负责触发,监督者根据新证据决定有无变化和是否反馈。
|
||||
- 在运行去重、用户修订保护和来源失效提示稳定后,再评估更复杂的跨目标固定策略、事件唤醒和自动阶段识别。
|
||||
|
||||
## 7. 验证策略
|
||||
|
||||
首版每层验证一条可追踪链路:
|
||||
|
||||
1. `EvidenceCollector` 测试范围裁剪、字符预算、来源定位和不可用来源。
|
||||
2. `SupervisorService` 测试模型输出解析、引用完整性、重复运行、取消、失败和部分结果。
|
||||
3. `AssistantDatabase` 测试迁移、事务回滚、外键、分页、关系移除不删来源。
|
||||
4. IPC 测试不可信输入、越权 Project/Source ID、确认状态和知识库写入前置预览。
|
||||
5. Renderer 测试从反馈进入图谱、点击实体高亮多个时间点、回放不显示未来结果,以及窄屏列表访问。
|
||||
6. 使用一组固定脱敏 fixture 验证“监督反馈 → 实体 → 多个事件 → 来源 → 继续讨论”的完整路径。
|
||||
|
||||
首个生产验收不要求模型自动发现所有关系。必须先证明来源可追溯、失败可解释、用户修订不会丢失,以及图谱与工作回顾读取同一结果。
|
||||
|
||||
## 8. 暂不实现的内容
|
||||
|
||||
- 独立的图数据库、向量图谱或新的知识库正文存储。
|
||||
- 让监督者拥有通用 Agent 工具执行能力。
|
||||
- 自动把所有聊天消息变成事件或节点。
|
||||
- 无依据的实体自动合并、旧观点自动失效和静默覆盖用户确认。
|
||||
- 应用市场建设作为监督者开发前置依赖。
|
||||
- 在没有增量和去重证据前实现事件触发、自动阶段识别和全量后台重算。
|
||||
|
||||
## 当前剩余差距
|
||||
|
||||
- 实体与关系 revoke 保存状态;界面没有 undo 或恢复入口。
|
||||
- 跨 run identity 依赖同 scope 候选 UUID 的显式引用,不自动合并历史重复实体;模型漏选候选时会创建新身份。
|
||||
- 结果图谱使用真实事件实体连线,侧栏已按固定目标过滤并支持结果直达图谱;逐事件状态回放和 Experiment 尚未实现。
|
||||
- 用户暂停已接到排队/在途 signal,并保留已保存批次;源版本变更后的局部重算、完整模型配置冻结及其他限制见分块调度第 0 节。
|
||||
- 监督知识实体与知识库正文条目仍是不同对象,知识正文只能通过现有条目定位和预览接口处理。
|
||||
@@ -0,0 +1,104 @@
|
||||
# 监督者 UI 设计
|
||||
|
||||
日期:2026-09-16。状态:目标设计,遵循根目录 [UI-DESIGN.md](../../../UI-DESIGN.md)。需求对应[监督者应用产品设计](./supervisor-prd.md) FR-S1 至 FR-S9。
|
||||
|
||||
## 应用入口
|
||||
|
||||
应用中心中的智能心跳入口升级为“监督者”,沿用既有启用、常驻与排序能力。未来应用市场使用同一应用身份。应用已启用时,停用单条自动心跳计划仍可手动回顾;关闭应用后,已打开页面显示关闭状态并禁用工作区操作。默认值及运行边界见[应用启停与执行](./logic-design.md#应用启停与执行)。
|
||||
|
||||
当前应用页只使用一套共享 PageTabs:“工作回顾 / 故事线图谱 / 自动监督 / 活动记录 / 设置”(英文活动标签为 Activity records)。自动监督独立管理计划;设置只显示模型超时与并发、回顾算法,不含计划、报告、审计或内部页签,也不显示整页全局徽标或独立刷新行。计划范围仍在表单和每条计划内显示。整个页面只有一个一级标题;监督者 bridge 不可用时显示明确错误,设置仍可访问,不回退到旧心跳概览。共享页签保留方向键、Home/End、游标焦点和单一激活面板,窄窗口横向滚动。
|
||||
|
||||
工作回顾只读取统一监督结果。顶部为紧凑的“新回顾”范围、时间、发起与刷新控件,不新增 Modal;简短帮助说明选择仅作用于新请求。历史结果选择与图谱入口位于正文之前,即使只有一条结果也显示选择器。新回顾工具栏、状态、历史选择和结果面板铺满监督者页面壳层的可用内容宽度,与页签左右对齐,不另设阅读宽度上限;结果内部按面板内边距排版,不再单独居中窄正文。正文使用“监督结果”标题,常显生成日期、真实范围和覆盖区间,历史结果不标为最新。工作总结按原有空行分段并保留换行;本次变化和未解决事项直接使用已有字段,不补写结论或截断正文。390px 下新请求字段与操作各占两列,结果元数据可换行。
|
||||
|
||||
新回顾运行中保留已显示结果及其日期、范围,禁用重复提交,并显示简短状态和活动记录入口;该入口清除计划筛选并聚焦活动页签。工作回顾不读取自动监督报告,也不显示自动报告空态、记忆与行动建议或第二行全局范围与刷新。无监督结果时显示手动回顾入口;自动报告是否为空不影响此判断。
|
||||
|
||||
## 活动
|
||||
|
||||
文案遵循根目录 [监督者文案规则](../../../UI-DESIGN.md#134-智能心跳):不把内部路线图、交付状态或实现缺项展示给用户。活动说明只描述手动回顾、自动监督执行及自动刷新;真实错误、失败和暂停状态仍展示,并保留恢复操作。事实详情提示展开批次并对照原文核对模型内容,不承诺完整语义识别;图谱说明实体状态以所选回顾结果为准。
|
||||
|
||||
对应 FR-S10、US-S26。活动按开始时间倒序,每页 50 条,提供较新/更早记录和刷新。列表保留触发方式、执行范围、开始/结束时间、实际状态、阶段状态、错误及已保存摘要。没有结果时不显示结果按钮;有监督结果时可分别进入该结果的工作回顾和故事线图谱,焦点移到目标页签。超过最近 20 条的结果仍按 ID 读取,不能回退最新结果。
|
||||
|
||||
活动页只展示统一监督活动、进度、详情及结果入口,不在列表下方重复显示自动监督概览或心跳审计。计划“执行记录”打开此页并按精确 ID 筛选,筛选器显示所选计划并提供清除;监督查询在分页前过滤。同名计划仍按 ID 区分;没有匹配记录时显示筛选无结果,不回退全部数据。自动报告正文和建议处理位于自动监督。
|
||||
|
||||
自动监督页依次展示范围与“刷新自动监督”、当前状态及计划列表、运行指标、报告趋势、待确认记忆与行动建议、“自动监督报告”历史、心跳报告阶段审计。刷新统一更新此页数据。报告与运行历史每次追加 20 条,报告可展开全部摘要和洞察,切换页签保留展开与分页位置;不再单独复制最近报告。建议保留确认、忽略、带入对话、标记完成及长内容展开。活动的计划筛选不改变自动监督的统计范围。当前状态下复用计划列表的唯一创建按钮,无计划时只显示列表的未配置说明,不叠加第二个计划空态或创建入口;点击直接打开现有 Modal。报告为空时明确写“暂无自动监督报告”,与工作回顾的结果状态区分。
|
||||
|
||||
加载、空记录和读取失败分开显示;读取失败保留已显示记录并提供就地重试。活动页面激活时,存在运行中记录则在上次读取结束 2 秒后刷新,否则 10 秒后刷新;每次最多一个请求。切换顶层页签、离开监督者或关闭应用后停止定时器,迟到响应不更新界面。错误后等待用户重试,不持续重发失败查询。
|
||||
|
||||
活动列表与工具栏铺满监督者页面壳层的可用内容宽度,与页签左右对齐,不另设仪表盘宽度上限。范围、计划筛选、清除筛选和刷新紧邻排列,空间不足时换行。列表使用共享表面、边框、状态颜色和按钮。每条运行首先显示类型、状态、开始时间和范围;“收集来源 / 整理批次 / 合并摘要 / 发布回顾”紧随其后,宽窗口使用按内容排布的紧凑状态行,窄于 720px 时两列,不为每个阶段铺设独立色块卡片。阶段读取已保存位置和真实运行状态,剩余来源为零不表示发布成功。旧运行没有阶段字段时显示“未记录”,不猜测失败位置;已提交来源清单可确认收集结束。无变化运行不显示新结果跳转,后续无需执行的阶段单独标注。
|
||||
|
||||
阶段下显示已保存批数、已读字符数、剩余来源和已保存导航合并数,运行中另显示排队或在途数。字符进度按码点统计,界面不展示 Unicode 术语。恢复操作先于详情:运行中可暂停,失败或暂停后沿用同一运行继续;已记录合并、发布阶段时分别显示“继续合并摘要”“重试发布”。成功叶子保留。来源失效时显示新建回顾提示,不提供无效继续。原始错误默认折叠,展开后的完整文本最多占 240px 高度,可滚动、选择复制和键盘聚焦;错误与恢复操作位于批次树之前。结束时间、覆盖区间、报告状态和已有摘要收进“运行详情与配置”,已保存配置在其中再次折叠。
|
||||
|
||||
“已保留事实与来源”展开后才查询既有批次接口,每页 10 批,按本页持久记录组成项目、会话、批次层级。项目使用可用名称,会话优先使用保存来源的标题;只有已保存批次标记“已保存”,不创建未落库的待执行、在途或失败叶子,也不把本页数量当作项目总量。批次摘要收起时最多两行,展开后保留完整摘要、事件、实体、变化、关系及原文片段。来源显示消息 ID、码点范围及版本;长原文局部滚动。原生 details/summary 保留键盘展开和可见焦点,轮询不主动展开用户收起的会话。加载、空批次和读取失败分开显示,失败可重试;换页失败不显示上一页批次,切换 run 后旧响应失效。暂停状态为“未完成,可继续”,手动回顾软预算暂停后在工作回顾页提示去活动继续,不显示完成通知。
|
||||
|
||||
## 自动监督设置
|
||||
|
||||
2026-09-23 当前已实现“模型超时与并发”表单:心跳报告与监督者整理分别设置,均为 30 至 600 的整数秒,默认 240;监督模型并发数为 1 至 4 的整数,默认 1。保存真实字段 `heartbeatReportTimeoutSeconds`、`supervisorOrganizeTimeoutSeconds`、`supervisorModelConcurrency`;无效输入不能提交,设置加载/保存中或锁定时禁用,失败保留草稿并提供重试。超时以阶段读取设置的时点为准,不改变已冻结的值;并发数影响后续派发,降低上限不打断在途请求。
|
||||
|
||||
并发说明明确报告和监督整理共用上限,排队不计入模型超时;普通聊天不受此设置限制,但仍与监督竞争上游额度。表单不宣称提供商全局并发、RPM、TPM 或 Retry-After 限流已实现。
|
||||
|
||||
模型超时不包含数据收集与保存,直连模型另有独立 600 秒传输超时。“回顾算法”表单提供“每次读取来源条数”“每批文本量”“每批消息数”和“单次整理时长”,显示默认值及合法范围,使用“保存回顾算法”独立提交。文本量内部仍按 UTF-16 单元计算,界面不要求用户理解编码单位。保存经 App 的现有设置回调进入共享设置存储,加载/保存/锁定时禁用,失败保留草稿。取值以[当前设置合同](./review-scheduling-design.md#当前设置合同)为准。帮助说明执行时间达到软预算后,正在处理的批次完成并保存,再暂停,可从活动记录继续;已保存配置用“同时处理任务数”解释并发,说明实际并发还受当前监督设置限制。
|
||||
|
||||
对应 US-S22 至 US-S24。“自动监督”先显示计划列表,点击创建或编辑才打开 Modal;复用原表单,不保留页面内副本。表单展示名称、项目范围、每日或每周频率、指定时间、回顾窗口(1 至 720 小时)和历史保留(1 至 365 天);每周显示星期选择。时区常显,新建使用本机时区,编辑保留计划原时区。列表上方帮助说明选择每日或每周时间、保存并启用后自动运行。
|
||||
|
||||
无计划时显示“尚未配置自动监督,目前仅支持手动回顾”;全部暂停时另行说明。加载或首次读取失败不显示未配置状态。打开页面、Modal 或填写字段均不创建计划,只有点击“保存并启用计划”才提交创建。已有计划显示范围、周期、时区、回顾窗口、保留期限和启停状态,保留编辑、暂停、恢复、立即运行、执行记录和确认删除;暂停时不显示待执行的下次时间,编辑保存不改变暂停状态。
|
||||
|
||||
Modal 按[共享表单 Modal 规则](../../../UI-DESIGN.md#615-表单-modal)复用 header、body、footer:标题左侧、带本地化名称和工具提示的 X 右侧;底部取消次按钮、保存主按钮整体右对齐。标题和操作区保持可见,仅表单内容滚动。表单使用独立字段容器,不嵌套设置表单;窄窗口表单单列,保留共享窗口安全边距。
|
||||
|
||||
沿用整窗 Portal、共享遮罩、背景 inert、焦点循环与关闭后焦点恢复;初始焦点进入名称。X、Escape、取消或点击遮罩关闭时,有未保存修改先确认;确认初始焦点为继续编辑,Escape 或 X 只返回编辑,点击遮罩不放弃修改。保存期间禁止关闭和重复提交,失败在表单内显示并保留草稿。
|
||||
|
||||
界面及可访问名称统一使用“自动监督 / 回顾”,保留内部 heartbeat 标识及已有计划名称、报告正文。表单继续使用共享控件和容器响应式布局,建议及审计无需先进入即可配置计划。
|
||||
|
||||
### 算法配置设置目标
|
||||
|
||||
以下高级分组为目标交互;已接通的四项算法控件以上节为准。其余参数引用[设置配置清单](./review-scheduling-design.md#4-设置配置清单),不在 UI 文档另设数值,不增加一级页面。
|
||||
|
||||
普通配置常显消息每页条数、监督并发、报告/整理模型超时和单次执行软预算。高级配置按“来源与分块”“上下文与输出”“汇总与内存”“超时与续跑”“上游额度”分组。消息页字段的常显帮助为:“每次从数据库读取的消息数;会持续读完所选范围,不限制回顾总消息数,也不决定每次发给模型的消息数。”模型批次消息数、片段数和新增字符数分别标注,避免统称“批量大小”。
|
||||
|
||||
字段显示单位、已确定的默认值和合法范围;补充帮助说明调大后的内存、等待或模型用量影响。允许继承的节点超时显示“继承监督整理值”及解析后的秒数;依赖模型容量的输入/输出预算显示所用模型、容量及 token 是否为估算。跨字段校验覆盖输入与输出预留之和、扇入可容纳性、软预算与硬期限顺序、退避起始与上限;错误附在字段并关联可访问说明,提交时聚焦首个错误,Main 仍作权威校验。
|
||||
|
||||
规划中的“保存配置”持久化后只用于新建运行,常显“已开始和待继续的回顾沿用启动时配置”。“恢复本组默认值”只修改该组草稿,经保存才生效,不开始回顾、不修改已有计划;取消编辑恢复已保存值。运行详情展示本次实际配置,继续操作沿用它;想采用新算法配置时使用“重新回顾”,说明会重新计算。当前已实现字段仍按上文的阶段读取和共享池生效方式工作,整次 run 冻结接线完成后再统一调整提示。
|
||||
|
||||
初次加载、读取失败、编辑、保存中、保存失败和已保存分别呈现;读取失败不填假默认值,保存失败保留草稿并允许重试。保存成功使用应用通知,字段错误就地显示,不重复通知。窄窗口按单列排列,折叠标题可键盘操作,单位和关键生效说明不藏进悬浮帮助。
|
||||
|
||||
只在真实 Main/存储/运行路径接通后显示相应可编辑字段;尚未实现的配置留在设计文档,不放置永久禁用的假控件。已接通但因加载、保存或依赖缺失而临时不可编辑时说明实际原因。上游额度明确显示适用连接/模型和是否包含普通聊天;未实现跨路径共享时不得标为提供商全局限制。覆盖完整性、来源校验及事实保留不提供开关。
|
||||
|
||||
## 工作回顾
|
||||
|
||||
当前页面提供全局或单项目范围,以及 1、7、30 天时间区间选择。没有自动计划也能手动回顾;成功结果显示自身冻结的范围、时间和未解决事项,可选择历史结果或进入图谱。来源筛选由本次回顾范围和时间限制;图谱详情只列出所选事件的关联来源,实体或关系列出相关事件的来源。独立的来源类型选择器尚未实现。
|
||||
|
||||
选择历史结果后,图谱读取该结果的成员和内容,标题显示其回顾区间和生成时间。切换时清空旧图谱和详情并显示加载状态,迟到请求不得替换新选择。确认、修订和移除反馈更新当前所选结果,后续自动回顾不改写旧结果。
|
||||
|
||||
手动“回顾当前进展”生成中禁用重复提交。该操作新建回顾,活动页“继续”恢复原 run;两者不得混淆。失败保留上次结果、本次范围和成功批次。手动操作不推进自动位置,删除报告也不重置自动位置。
|
||||
|
||||
## 会话侧栏
|
||||
|
||||
监督反馈标题右侧使用共享紧凑图标按钮固定/取消固定目标与刷新回顾;固定状态通过
|
||||
`aria-pressed` 和按钮高亮表达。目标正文显示会话或任务名称,名称缺失时使用“未命名会话”
|
||||
或“未命名任务”,原始 ID 仅作为悬停补充。无目标时禁用固定和刷新,读取中禁用刷新;
|
||||
窄侧栏中的长标题换行,按钮保留可见键盘焦点。
|
||||
|
||||
仅在应用已启用时挂载监督面板及固定目标控件;关闭或设置尚未加载时不挂载,也不发起面板查询。任务列表仍按原规则显示。
|
||||
|
||||
任务中心从上到下依次显示项目概览(含当前会话统计)、监督反馈、项目任务。
|
||||
|
||||
右侧通用助手工作栏中的监督卡片展示“监督反馈”,沿用现有[工作栏设计](../assistant-workbar/prd.md)的布局和执行边界。当前支持 Conversation/Task:有选中任务时跟随任务,否则跟随会话;“固定监督目标”保存当前目标,切换聊天后保持,取消固定恢复跟随。卡片显示目标、结果真实范围、回顾区间和摘要,可刷新并查看匹配来源;无目标或无匹配结果显示空态。Experiment 尚未接入。
|
||||
|
||||
有匹配结果时提供“在图谱中查看”,打开现有监督者页面的图谱页签并读取该结果;无匹配结果不显示入口。按钮保留原生键盘操作,进入后焦点落在图谱页签,继续使用共享 PageTabs 的方向键导航。固定目标不因跳转改变;再次点击相同或不同结果均重新定位,包括页面缓存后再次进入。结果不可用时显示错误并重试原 ID,不展示默认最新结果。
|
||||
|
||||
当前“继续讨论”预览实际发送会话名称、ID 和上下文,确认后发送到该会话;固定任务使用其关联会话,无法确定发送会话时禁用。切换目标使旧来源请求和预览失效。编辑预览中的引用仍属后续工作。
|
||||
|
||||
## 故事线图谱
|
||||
|
||||
使用 `supervisor` 页面壳层,最大内容宽度为 `1440px`。当前生产图谱左侧为紧凑的事件、实体和关系列表,中间为 SVG 画布,右侧为详情与来源。桌面列宽为 `216px minmax(0, 1fr) 280px`,画布获得主要空间。外圈事件按发生时间排序后等距逆时针排列,不表示实际时间间隔;起止之间保留缺口和方向箭头。内部显示真实实体名称,实线仅连接实际事件与实体,虚线仅连接实际实体关系。
|
||||
|
||||
实体及其影响连线复用现有 `--graph-node-*-border` 令牌;颜色用于辅助辨认实体,不代表故事分类或确认状态。选中项加粗并突出连线,其余节点降低图形强调度但保留可读文字。详情分为名称与说明、图谱关联、确认操作和来源,来源正文按需读取。图例常显,读图说明可展开。
|
||||
|
||||
画布每批最多显示 8 个事件、6 个实体;同时间事件分开排布。列表保留全部返回记录和完整名称,选择实体会定位到真实关联事件所在批次,选择关系会优先显示两端实体。滑块明确命名“事件浏览”,只切换当前事件,不还原实体历史状态,也不宣称完整历史回放。当前接口返回单条故事线,不复制 Demo 中的三条模拟故事。后续视图规划见[图谱设计](../story-graph/prd.md)。
|
||||
|
||||
按实际内容容器宽度响应:不超过 `1190px` 时变为 `184px` 列表加画布,详情移到下方;不超过 `860px` 时变为单列,画布优先,列表限制高度并独立滚动。SVG 最小宽度为 `660px`,窄屏只在画布内部横向滚动,页面本身不横向溢出。范围、时间和操作可换行。节点、关系与事件均可从列表选择,画布节点支持 Enter、Space 和可见焦点。加载、无数据、读取失败和服务不可用各自呈现,错误提供可行的恢复说明或重试入口。
|
||||
|
||||
隔离视觉验证使用 `tests/support/supervisor-layout-regression.tsx` 渲染生产 React 组件;所有输入标为模拟 fixture,不读取用户数据库或调用模型。`tests/supervisor-layout.electron.test.ts` 启动 Vite 与 Electron,通过 `executeJavaScript` 测量布局、`capturePage` 截图,覆盖 1440、1024、390px、浅深主题、长名称、密集事件和异常状态。可通过 `GOODBUDDY_SUPERVISOR_ARTIFACTS` 保留截图;交互预览入口为 `node tests/support/supervisor-preview.mjs`。
|
||||
|
||||
设置 `GOODBUDDY_SUPERVISOR_PLAN_MODAL=1` 可只验证五页签 fixture 中的计划 Modal:创建和编辑各覆盖浅深主题、1440×1100、390×1100、390×480,检查关闭按钮、底部按钮对齐、正文滚动、鼠标命中和原生键盘操作。截图及测量写入同一产物目录。
|
||||
|
||||
设置 `GOODBUDDY_SUPERVISOR_RECAP=1` 可验证本次工作回顾分区:1440×1100 和 390×1100 下覆盖监督结果与自动报告均有内容、仅有监督结果、两者均空,另检查历史结果运行中仍可阅读以及活动入口。该分支检查每页一处刷新、自动报告和建议的归属、正文段落与宽度,并截取自动页建议、展开报告和审计区块。
|
||||
@@ -0,0 +1,570 @@
|
||||
# 监督者 User Stories
|
||||
|
||||
日期:2026-09-22。本文定义监督者从应用入口到故事线图谱的用户场景、界面行为和验收条件。
|
||||
|
||||
当前代码已经交付智能心跳页面和运行链路;监督者工作回顾、故事线图谱、会话侧栏反馈和知识库双向定位仍是目标设计。因此每条故事标注了当前状态:
|
||||
|
||||
- **已实现**:当前生产界面或 Main/Preload 链路已有可验证行为。
|
||||
- **待实现**:产品目标,不能用现有智能心跳页面或 Story Graph Demo 代替验收。
|
||||
- **沿用**:已有能力在监督者统一入口中必须继续保留。
|
||||
|
||||
## 用户角色
|
||||
|
||||
| 角色 | 目标 |
|
||||
| --- | --- |
|
||||
| 工作使用者 | 回到工作现场,理解进展、决定和未解决事项,并继续工作 |
|
||||
| 知识整理者 | 核对来源,修正自动归纳,把可靠认识补充到知识库 |
|
||||
| 监督者配置者 | 决定观察范围、运行时间和反馈方式 |
|
||||
| 受限访问者 | 只能看到已授权范围内的会话、任务、笔记和知识库引用 |
|
||||
|
||||
## 入口与范围
|
||||
|
||||
### US-S01 启用后知道监督者观察什么
|
||||
|
||||
**状态:待实现。**
|
||||
|
||||
作为工作使用者,我希望在第一次使用监督者时明确它将观察哪些内容,避免启用应用后默认读取全部工作。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 用户第一次打开监督者 | 选择 Global 或一个、多个 Project,并选择可用来源和时间区间 | 页面在生成前显示完整范围、来源和时间区间 |
|
||||
| 用户没有选择来源 | 点击生成回顾 | 不开始整理,显示选择来源的入口和原因 |
|
||||
| 当前项目已打开 | 打开范围选择 | 当前项目可以作为建议值,但不能覆盖已保存范围 |
|
||||
| 用户切换当前项目 | 返回监督者 | 已保存的范围不改变 |
|
||||
|
||||
**界面行为**
|
||||
|
||||
- 首次状态显示“选择关注范围”,不显示看似已有的空回顾。
|
||||
- 范围摘要持续显示在页面标题或范围徽标中。
|
||||
- Global、指定项目和来源选择使用持久标签;不能只用颜色表示范围。
|
||||
- 生成按钮在范围未完成时禁用,并把错误关联到对应字段。
|
||||
|
||||
### US-S14 从应用中心进入唯一的监督者
|
||||
|
||||
**状态:应用入口与启停已接入,完整验证范围见[实施进度](./progress.md)。**
|
||||
|
||||
作为用户,我希望监督者只有一个稳定入口,不需要在智能心跳、任务中心和设置中心之间寻找重复配置。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 用户打开应用中心 | 启用应用后选择智能心跳入口 | 进入监督者统一页面,未来显示为监督者名称 |
|
||||
| 用户已有智能心跳计划 | 应用入口升级 | 计划、运行记录、报告、建议和启用偏好仍可访问 |
|
||||
| 用户打开任务中心或设置中心 | 查找心跳配置 | 不出现第二套计划 CRUD;需要时只提供进入监督者的链接 |
|
||||
| 新安装或设置缺失应用启用值 | 启动应用 | 监督者关闭;已有显式开启或关闭选择保留 |
|
||||
| 应用已启用但没有已保存且启用的计划 | 等待自动调度 | 不自动创建计划或运行回顾 |
|
||||
| 已有心跳运行被领取并正在执行 | 关闭应用 | 该运行可完成并保存;不再接受新运行或追加自动监督回顾,侧栏不挂载或查询监督面板;普通任务定时调度不变 |
|
||||
|
||||
以上启停验收遵守[应用启停与执行](./logic-design.md#应用启停与执行)。
|
||||
|
||||
**界面行为**
|
||||
|
||||
- 应用身份、排序和常驻行为保持一致,不能生成第二个平级应用。
|
||||
- 迁移期间如果仍显示“智能心跳”,页面应明确说明它是监督者的自动唤醒能力。
|
||||
- 旧数据为空时进入有意义的首次配置状态,而不是报错。
|
||||
|
||||
### US-S21 选择一个或多个项目作为观察范围
|
||||
|
||||
**状态:已实现。**
|
||||
|
||||
作为监督者配置者,我希望心跳计划明确属于 Global 或选定项目。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 用户编辑心跳计划 | 选择 Global | 计划显示 Global,不能同时选择项目 |
|
||||
| 用户选择指定项目 | 勾选一个或多个活动项目并保存 | 计划保存为一份多项目计划,不为每个项目复制 Run 或报告 |
|
||||
| 用户没有勾选项目 | 尝试保存指定项目计划 | 保存按钮不可用或显示字段错误 |
|
||||
| 项目已归档或删除 | 查看或保存旧计划 | 显示不可用项目;不能默默扩大或改写范围 |
|
||||
|
||||
**界面行为**
|
||||
|
||||
- 使用分段控件选择 Global/指定项目,项目使用 Checkbox。
|
||||
- 计划卡片显示 Global 或完整项目摘要;项目过多时显示数量并保留可访问的完整名称。
|
||||
- 当前项目切换不会重新解释计划卡片的范围。
|
||||
|
||||
### US-S22 配置自动监督
|
||||
|
||||
**状态:已实现。**
|
||||
|
||||
作为监督者配置者,我希望配置回顾时间、周期、回顾窗口和保留期限。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 用户打开自动监督 | 查看计划列表并点击创建或编辑 | Modal 显示名称、项目范围、每日/每周频率、指定时间、时区、回顾窗口和保留期限;页面说明选择每日或每周时间、保存并启用后自动运行 |
|
||||
| 应用已启用但没有计划 | 打开自动监督或填写 Modal 字段 | 显示“尚未配置自动监督,目前仅支持手动回顾”,不自动保存、创建计划或发起运行 |
|
||||
| 用户修改计划草稿 | 保存失败或关闭 Modal | 失败保留输入并可重试;Escape、取消和遮罩关闭均先确认放弃,继续编辑保留草稿 |
|
||||
| 已有计划处于暂停状态 | 编辑并保存 | 更新计划字段,保持暂停;只有显式恢复才启用 |
|
||||
| 两条计划名称相同 | 点击其中一条的执行记录 | 打开统一活动记录并按所选计划的精确 ID 筛选;可清除筛选,不影响自动监督页的概览和审计 |
|
||||
| 用户完成配置 | 点击保存并启用计划 | 保存明确选择的频率、时间、范围和期限,参与既有调度 |
|
||||
| 用户编辑已有计划 | 修改字段并保存 | 计划卡片显示新配置、下次运行和上次状态 |
|
||||
| 已有计划暂停且时区与本机不同 | 编辑并保存 | 保留已暂停状态及原时区,不隐式恢复或改变执行时间 |
|
||||
| 用户开始编辑 | 点击取消编辑 | 草稿丢弃,恢复编辑前的计划值 |
|
||||
| 保存失败 | 提交表单 | 草稿保留,错误就地显示,用户可以修正后重试 |
|
||||
|
||||
**界面行为**
|
||||
|
||||
- 表单标签始终可见,不能用 placeholder 代替。
|
||||
- 每次操作期间禁用重复提交和相互冲突的按钮。
|
||||
- 周期选择为 weekly 时才显示星期字段。
|
||||
- 新建使用本机时区,编辑保留原计划时区,表单与计划摘要常显时区;范围和状态不能只依赖颜色。
|
||||
|
||||
### US-S23 暂停、恢复或立即运行计划
|
||||
|
||||
**状态:已实现。**
|
||||
|
||||
作为用户,我希望临时暂停自动回顾,也能随时手动运行一次。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 计划正在运行 | 点击暂停 | 计划停止下一次自动运行,已有历史和结果保留 |
|
||||
| 计划已暂停 | 点击恢复 | 计划恢复并显示下一次运行时间 |
|
||||
| 计划可运行 | 点击立即运行 | 创建一条标记为手动触发的 Run,并在完成后刷新结果 |
|
||||
| Run 正在执行 | 再次点击运行或暂停 | 禁止重复操作,按钮显示进行中状态 |
|
||||
|
||||
## 工作回顾和监督反馈
|
||||
|
||||
### US-S02 隔几天回来能继续上次工作
|
||||
|
||||
**状态:待实现;当前智能心跳的运行概览和最新报告是可复用基础。**
|
||||
|
||||
作为工作使用者,我希望打开监督者就知道上次形成了什么决定、哪些内容改变、还有什么没有解决。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 已有成功的阶段回顾 | 打开工作回顾 | 默认显示最近成功结果、覆盖范围和生成时间 |
|
||||
| 回顾包含决定、变化和未解决事项 | 点击其中一项 | 展开依据,并提供“在图谱中查看”入口 |
|
||||
| 没有成功回顾 | 打开工作回顾 | 显示首次生成入口,不伪装成已有总结 |
|
||||
|
||||
**界面行为**
|
||||
|
||||
- 页面顶部固定显示当前范围、来源和时间区间。
|
||||
- 工作回顾是监督者的主视图之一,与故事线图谱使用 PageTabs 切换。
|
||||
- 内容卡片显示“自动归纳”“待核对”或“用户确认”等状态。
|
||||
- 点击图谱入口时带入当前阶段、实体或事件的定位,不打开无选择的全局图谱。
|
||||
|
||||
### US-S24 手动回顾当前进展
|
||||
|
||||
**状态:待实现;当前智能心跳已有“立即运行一次心跳”。**
|
||||
|
||||
作为工作使用者,我希望主动检查当前范围,而不必等待下一次计划运行。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 范围有效且有来源 | 点击“回顾当前进展” | 页面显示整理中状态、进度说明和取消操作 |
|
||||
| 整理完成并有新变化 | 返回结果 | 工作回顾、监督反馈和图谱引用同一次结果 |
|
||||
| 没有有效新变化 | 整理完成 | 显示检查时间和“暂无新进展”,不重复推送旧内容 |
|
||||
| 用户点击取消 | 整理仍在运行 | 显示已取消,保留上一次成功结果 |
|
||||
|
||||
**界面行为**
|
||||
|
||||
- 运行按钮在整理期间禁用,取消后可以重试。
|
||||
- “无新进展”与“失败”使用不同状态和文案。
|
||||
- 结果必须显示本次冻结的范围,不能因用户后来切换项目而改变解释。
|
||||
|
||||
### US-S03 自动检查不过度打扰
|
||||
|
||||
**状态:待实现;当前智能心跳已实现计划运行、静默之外的私有结果保存和运行审计。**
|
||||
|
||||
作为用户,我希望定时检查有变化时才打扰我,没有新信息时保持安静。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 计划到期 | 智能心跳唤醒监督者 | 监督者只读取冻结范围内的有界内容 |
|
||||
| 没有有效新变化 | 运行完成 | 保存检查记录,不发送重复侧栏反馈或全局通知 |
|
||||
| 有新的阶段认识 | 运行完成 | 更新工作回顾和图谱,并按反馈设置显示监督反馈 |
|
||||
| 运行失败 | 运行结束 | 显示失败状态和重试入口,不把失败当作成功回顾 |
|
||||
|
||||
**界面行为**
|
||||
|
||||
- 心跳负责唤醒,监督者负责判断是否需要反馈。
|
||||
- 自动结果不在系统通知中暴露私人正文;用户在应用内查看详情。
|
||||
- 同一结果不能同时作为重复的侧栏卡片和全局通知推送。
|
||||
|
||||
### US-S25 查看和处理监督反馈
|
||||
|
||||
**状态:Conversation/Task 过滤、固定/跟随、来源、确认发送和侧栏结果直达图谱已接入;Experiment 仍待实现。验证见[进度](./progress.md)。**
|
||||
|
||||
作为工作使用者,我希望在当前会话旁边看到与当前工作有关的简短反馈,并能进入完整回顾。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 监督栏处于跟随模式 | 用户切换会话或任务 | 面板只显示包含该目标来源且范围匹配的结果,同时标出结果真实范围;无结果显示空态 |
|
||||
| 旧目标请求仍在读取 | 用户切换目标 | 旧摘要、来源和预览不出现在新目标下 |
|
||||
| 监督栏固定到 Conversation、Task 或 Experiment | 用户切换聊天 | 面板继续显示固定目标,并显示目标名称 |
|
||||
| 反馈卡片有来源 | 点击“查看依据” | 展开来源摘要和可用定位信息 |
|
||||
| 用户点击“在图谱中查看” | 进入图谱 | 图谱定位到反馈对应阶段或实体 |
|
||||
| 用户点击“发到当前会话” | 确认发送 | 才将反馈作为消息加入会话;默认不自动发言 |
|
||||
|
||||
**界面行为**
|
||||
|
||||
- 卡片显示目标、阶段、结论、未解决事项和来源。
|
||||
- 跨会话总结明确显示覆盖范围,不能伪装成当前会话观察。
|
||||
- 固定/跟随模式沿用通用助手工作栏的选择和返回行为。
|
||||
|
||||
## 故事线图谱
|
||||
|
||||
### US-S04 从时间事件查看发生了什么
|
||||
|
||||
**状态:待实现;当前 Demo 仅有模拟数据。**
|
||||
|
||||
作为工作使用者,我希望知道某个时间点发生了什么,以及它影响了哪些认识。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 图谱显示多个时间事件 | 点击外圈时间点 | 相关知识实体和实际连线高亮,其余内容降低显著度 |
|
||||
| 同一天事件过密 | 点击聚合点 | 显示事件数量和事件列表,可选择具体事件 |
|
||||
| 用户选择具体事件 | 查看右侧详情 | 显示事件摘要、所属故事、发生时间、影响实体和来源入口 |
|
||||
| 来源可用 | 点击来源 | 打开所属会话、笔记或知识库条目的对应位置 |
|
||||
|
||||
**界面行为**
|
||||
|
||||
- 外圈时间轴从起点到终点逆时针排列,持续显示方向和当前时间范围。
|
||||
- 外圈保留缺口,不能让用户误以为时间循环。
|
||||
- 时间到实体的连线表示事件影响实体;它与实体之间的知识关系使用不同文字说明。
|
||||
|
||||
### US-S05 追溯同一个知识实体的变化
|
||||
|
||||
**状态:待实现。**
|
||||
|
||||
作为知识整理者,我希望看到一个认识如何被提出、补充、验证或修订。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 一个知识实体关联多个时间事件 | 点击内部实体 | 多个时间点和连线高亮,右侧先显示当前理解 |
|
||||
| 事件类型分别为提出、补充、验证或修订 | 展开演变 | 每次变化显示类型、时间、摘要和依据 |
|
||||
| 新方案只是增加选择 | 查看演变 | 旧方案不被自动标记为失效 |
|
||||
| 正在回放过去时刻 | 查看实体 | 只显示截至该时刻已形成的理解,不泄漏未来结论 |
|
||||
|
||||
**界面行为**
|
||||
|
||||
- 一个实体只显示一个稳定节点,不能把每次变化复制成同名节点。
|
||||
- 用户能从当前理解切换到演变列表和来源列表。
|
||||
- 用户确认过的描述和关系显示确认状态,自动新增内容显示待核对状态。
|
||||
|
||||
### US-S06 查看知识关系为什么成立
|
||||
|
||||
**状态:待实现。**
|
||||
|
||||
作为知识整理者,我希望知道两个实体为什么被连接,而不是只看到一条线。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 图谱存在实体间关系 | 点击关系线 | 右侧显示关系类型、关联理由、确认状态和两端依据 |
|
||||
| 关系由系统推测 | 查看关系详情 | 明确标记“待核对” |
|
||||
| 用户确认关系 | 点击确认 | 关系进入用户确认状态,并保留确认操作记录 |
|
||||
| 用户认为关系错误 | 修改类型或移除 | 图谱更新,原始来源不删除,移除可撤销 |
|
||||
|
||||
**界面行为**
|
||||
|
||||
- 关系类型必须有文字名称,颜色只能辅助区分。
|
||||
- 时间到实体的关系和跨故事实体关系在详情中分别说明。
|
||||
- 后续自动整理只能提出补充或修订建议,不能静默覆盖用户确认内容。
|
||||
|
||||
### US-S10 纠正错误关系而不删除来源
|
||||
|
||||
**状态:待实现。**
|
||||
|
||||
作为知识整理者,我希望修正系统错误合并的概念或错误关系,同时保留原始记录和撤销能力。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 自动归纳把两个概念错误连接 | 在关系详情中选择修改或移除 | 图谱立即反映修正,并显示待保存或已保存状态 |
|
||||
| 用户移除一条关系 | 查看来源或知识库 | 原始来源和知识库正文仍然存在 |
|
||||
| 用户完成修正 | 后续自动整理 | 系统可以提出新的证据或差异,但不能静默恢复被移除关系 |
|
||||
| 用户点击撤销 | 修正仍可撤销 | 关系恢复到修正前状态,来源不发生变化 |
|
||||
|
||||
**界面行为**
|
||||
|
||||
- 破坏性关系操作使用明确确认,并说明只改变图谱组织。
|
||||
- 用户修改、系统归纳和来源事实显示不同状态。
|
||||
- 修正失败时保留当前详情和未提交意图,不能显示为已保存。
|
||||
|
||||
### US-S07 按时间段查看阶段总结
|
||||
|
||||
**状态:待实现。**
|
||||
|
||||
作为工作使用者,我希望复盘一段过程,而不是逐条翻阅原始记录。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 图谱有多个事件 | 拖选起止时间或使用时间范围控件 | 页面显示选中范围和阶段总结入口 |
|
||||
| 用户打开阶段总结 | 查看结果 | 总结只覆盖该范围,分别列出决定、变化和未解决事项 |
|
||||
| 用户拖动回放控制 | 回到过去时刻 | 图谱只显示当时已经发生的事件和已经形成的认识 |
|
||||
| 用户切换圆形/平铺 | 保留当前范围 | 回放时刻、选择和范围继续保留 |
|
||||
|
||||
**界面行为**
|
||||
|
||||
- 平铺环绕视图是默认图谱视图;圆形视图用于观察故事聚合。
|
||||
- 回放状态必须持续显示“截至某时刻”,避免误读当前结论。
|
||||
- 时间轴密集时先聚合数量,展开后才能选择单个事件。
|
||||
|
||||
### US-S15 在列表、窄屏和键盘中完成同样操作
|
||||
|
||||
**状态:待实现。**
|
||||
|
||||
作为无法方便操作图形的用户,我希望通过文字列表和键盘完成相同的查看和追溯任务。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 用户使用窄屏 | 打开故事线图谱 | 故事列表收为选择入口,详情进入可返回的独立区域 |
|
||||
| 用户不使用鼠标 | 用 Tab、方向键和 Enter 选择对象 | 能选择时间事件、实体和关系,并查看相同详情 |
|
||||
| 用户缩放或平移图谱 | 返回详情或列表 | 当前选择不丢失 |
|
||||
| 图形无法加载 | 使用文字列表 | 仍能浏览对象、关系、来源和回到知识库的入口 |
|
||||
|
||||
**界面行为**
|
||||
|
||||
- 焦点始终可见,弹出详情关闭后返回触发它的控件。
|
||||
- 文字列表和图形共享同一选择状态,不维护两份内容。
|
||||
- 缩放、平移和颜色不能成为获取信息的唯一方式。
|
||||
|
||||
## 来源与知识库
|
||||
|
||||
### US-S08 回到来源核对依据
|
||||
|
||||
**状态:待实现,沿用现有知识库的本地条目和外部片段边界。**
|
||||
|
||||
作为知识整理者,我希望从总结或图谱回到原始依据。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 引用的是本地知识库条目 | 点击来源 | 打开原条目及引用位置,并可返回图谱 |
|
||||
| 引用的是会话或魔法笔记 | 点击来源 | 打开所属内容,并显示可用的消息、条目或位置 |
|
||||
| 引用的是外部知识库片段 | 点击来源 | 打开已保存的有界片段和可用定位信息 |
|
||||
| 原来源已失效或没有位置 | 点击来源 | 明确显示不可用或缺少定位,不伪造远端原文跳转 |
|
||||
|
||||
### US-S09 把新认识整理进本地知识库
|
||||
|
||||
**状态:待实现。**
|
||||
|
||||
作为知识整理者,我希望把可靠的新认识放入现有知识库,而不是建立第二份正文存储。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 候选归纳尚无对应条目 | 选择“新建条目” | 显示正文预览、来源和目标位置,确认后才保存 |
|
||||
| 已有本地条目可以补充 | 选择“补充已有条目” | 显示新增内容与原内容的差异,确认后修改原条目 |
|
||||
| 用户查看的是外部只读资料 | 查找写入操作 | 不显示写回或远端同步按钮 |
|
||||
| 同一内容已被保存 | 再次整理 | 提示已有条目,避免重复创建 |
|
||||
|
||||
**界面行为**
|
||||
|
||||
- 图谱保存实体、关系和来源定位,不复制知识库正文。
|
||||
- 保存失败保留编辑预览,不能把候选显示为已写入。
|
||||
- 现有资料使用“打开知识库条目”,已有条目不显示“存入知识库”。
|
||||
|
||||
## 建议、任务与继续工作
|
||||
|
||||
### US-S16 审核心跳产生的记忆建议
|
||||
|
||||
**状态:已实现,沿用智能心跳页面。**
|
||||
|
||||
作为知识整理者,我希望逐条确认或忽略记忆候选。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 存在待确认记忆 | 打开待处理建议 | 显示类型、内容、置信度和显著性 |
|
||||
| 内容较长 | 点击展开 | 显示完整内容,再次点击可以收起 |
|
||||
| 用户确认 | 点击确认记忆 | 状态变为已确认,计数和指标更新 |
|
||||
| 用户不接受 | 点击忽略 | 候选不再作为待处理项,不改写来源 |
|
||||
|
||||
### US-S17 处理后续任务建议
|
||||
|
||||
**状态:已实现,沿用智能心跳页面。**
|
||||
|
||||
作为工作使用者,我希望把回顾产生的行动建议带入对话或标记完成。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 存在未完成的后续任务 | 点击“带入对话处理” | 将任务交给当前会话处理入口,不自动执行工具 |
|
||||
| 用户完成任务 | 点击标记完成 | 任务状态更新,行动转化指标刷新 |
|
||||
| 用户不再需要任务 | 点击忽略建议 | 任务标记为取消或不再待处理 |
|
||||
| 任务已经完成或取消 | 再次打开建议 | 不显示需要处理的操作按钮 |
|
||||
|
||||
### US-S11 带着上下文继续讨论
|
||||
|
||||
**状态:待实现。**
|
||||
|
||||
作为工作使用者,我希望围绕已有知识继续讨论,而不用重新交代背景。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 用户选中知识实体或阶段总结 | 点击“继续讨论” | 显示将携带的摘要、时间范围和来源列表 |
|
||||
| 用户移除部分上下文并确认 | 进入会话 | 会话只携带剩余上下文,并明确来自监督者 |
|
||||
| 新讨论产生内容 | 下一次整理 | 新内容可以关联到原实体或提出待核对的新关系 |
|
||||
| 用户没有确认上下文 | 关闭预览 | 不创建会话消息,也不改变图谱确认状态 |
|
||||
|
||||
## 历史、失败与权限
|
||||
|
||||
### US-S18 查看报告和运行审计
|
||||
|
||||
**状态:已实现。**
|
||||
|
||||
作为监督者配置者,我希望区分报告内容和产生报告的运行记录。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 已有报告 | 打开报告与记录 | 按时间显示摘要、亮点和建议数量 |
|
||||
| 用户点击展开报告 | 展开一项 | 显示完整摘要和亮点,可再次收起 |
|
||||
| 用户查看运行记录 | 打开审计列表 | 显示手动/计划触发、时间、状态、尝试次数和错误 |
|
||||
| 记录超过首屏数量 | 点击加载更多 | 追加历史,不替换当前已展开内容 |
|
||||
|
||||
**界面行为**
|
||||
|
||||
- 报告和 Run 审计是两个可区分的区域。
|
||||
- `claimed`、完成、失败和跳过使用文字和图标共同表达。
|
||||
- 失败信息直接显示在对应 Run 下,不能用空报告代替。
|
||||
|
||||
### US-S19 加载、刷新和首次空状态可区分
|
||||
|
||||
**状态:已实现;监督者页面必须沿用。**
|
||||
|
||||
作为用户,我希望知道页面是在加载、真的没有数据,还是读取失败。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 页面首次读取尚未完成 | 打开页面 | 显示加载状态和忙碌语义,不显示创建按钮造成误导 |
|
||||
| 首次读取失败 | 页面返回错误 | 显示错误原因和重试按钮 |
|
||||
| 已有数据显示但刷新失败 | 点击刷新后失败 | 保留旧数据,同时显示刷新失败提示和重试入口 |
|
||||
| 读取成功但没有数据 | 完成加载 | 显示首次配置或生成回顾的空状态 |
|
||||
|
||||
### US-S12 新一次失败不覆盖旧结果
|
||||
|
||||
**状态:待实现;当前智能心跳已有失败 Run 保留历史结果的基础行为。**
|
||||
|
||||
作为工作使用者,我希望一次模型、来源或网络失败不会抹掉上一次可用回顾。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 已有成功回顾 | 新一次整理失败 | 旧回顾继续可读,新运行显示失败原因和覆盖范围 |
|
||||
| 新一次整理被取消 | 用户查看工作回顾 | 显示已取消,不把部分内容标成完整回顾 |
|
||||
| 只有部分来源可用 | 整理完成 | 显示已覆盖和遗漏范围,结果标记为部分完成 |
|
||||
| 用户点击重试 | 重新运行 | 创建新的运行记录,不把旧失败记录改写为成功 |
|
||||
|
||||
### US-S13 历史回放不冒充当前来源
|
||||
|
||||
**状态:待实现。**
|
||||
|
||||
作为知识整理者,我希望回看过去时看到当时的认识,知道原始来源是否已经变化。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 来源后来被修改 | 回放旧阶段 | 仍显示当时保存的归纳和可用历史依据 |
|
||||
| 旧版原文无法恢复 | 点击来源 | 明确提示历史版本不可用,并显示当前可用信息的时间,不称其为旧原文 |
|
||||
| 当前结论与过去不同 | 回放过去 | 不把当前结论注入过去的图谱或阶段总结 |
|
||||
|
||||
### US-S20 遵守范围和访问边界
|
||||
|
||||
**状态:已实现于智能心跳输入与工具禁用边界;监督者目标行为待实现。**
|
||||
|
||||
作为受限访问者,我希望监督者只处理授权范围内的内容。
|
||||
|
||||
**Given / When / Then**
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 计划是 Global | 运行回顾 | 只读取允许访问的有界会话、任务和 Global 已确认记忆 |
|
||||
| 计划绑定项目 | 运行回顾 | 只读取选中项目内容及允许共享的 Global 记忆 |
|
||||
| 模型尝试调用工具 | 整理期间 | 调用被拒绝,运行失败原因可审计 |
|
||||
| 模型输出项目外的记忆或任务 | 校验输出 | 结果被拒绝,不写入越权内容 |
|
||||
|
||||
**界面行为**
|
||||
|
||||
- 页面显示本次运行的冻结范围。
|
||||
- 权限不足、来源不可用和没有新内容使用不同状态。
|
||||
- 监督者的 Agent 身份不自动获得工具执行、批准或切换 Execute 的能力。
|
||||
|
||||
## 活动记录
|
||||
|
||||
### US-S26 查看实际执行活动
|
||||
|
||||
对应 FR-S10,当前已接入。
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 用户手动开始监督回顾 | 打开活动 | 显示冻结范围、开始时间和运行中状态;失败保留错误,成功提供结果入口 |
|
||||
| 定时或立即运行心跳已保存报告 | 下游监督仍在运行或失败 | 一条活动分别显示阶段状态,整体不能显示完成 |
|
||||
| 活动包含较早的成功结果 | 点击查看回顾或在图谱中查看 | 打开该 resultId,超过最近结果列表仍正确定位 |
|
||||
| 活动读取失败 | 点击重试 | 保留可用记录并重新读取,不调用模型 |
|
||||
| 活动有在途请求 | 离开页签、页面或关闭应用 | 停止后续刷新,迟到响应不更新界面;关闭后不查询活动 |
|
||||
| 旧记录缺少范围或运行关联 | 查看活动 | 明确显示缺失,不根据当前计划或相近时间推断 |
|
||||
| 六个来源批次已保存,剩余来源为零 | 导航合并失败后打开活动 | 已记录的合并阶段显示失败,发布保持未完成;继续复用成功叶子和导航节点 |
|
||||
| 已保存批次跨多个项目和会话 | 展开事实并翻页 | 按本页真实归属分层,标明本页数量;原文可读,失败叶子不伪造为已保存 |
|
||||
| 原始错误是多行校验 JSON | 展开错误详情 | 默认折叠,展开后有界滚动,完整文本可选择复制;恢复操作和阶段仍在其前面 |
|
||||
|
||||
取消和人工修改时间序列审计不属于当前已实现范围;实际状态及保留规则见[活动记录](./logic-design.md#活动记录)。
|
||||
|
||||
### US-S27 自动增量与历史重看
|
||||
|
||||
对应 FR-S4、FR-S10。
|
||||
|
||||
| Given | When | Then |
|
||||
| --- | --- | --- |
|
||||
| 自动报告和监督已成功处理窗口内来源 | 同范围再次自动检查且来源未修改 | 两阶段均不调用模型,活动显示无变化,不新增报告和图谱事件 |
|
||||
| 消息正文、知识引用或任务状态有修改 | 再次自动检查 | 只处理修改版本及未读片段,结合已有摘要和实体说明,允许复用已有实体 ID |
|
||||
| 来源多于单次预算或正文较长 | 一次结果成功保存后再次检查 | 继续处理窗口内遗漏或剩余片段,已送入且保存成功的部分不重复处理 |
|
||||
| 模型、校验或保存失败 | 再次检查相同输入 | 失败阶段仍可重试;已成功的另一个阶段不重复付费 |
|
||||
| 来源已经自动处理 | 用户手动回顾所选区间 | 重新整理有界历史,保留自动进度;删除报告不等同重看或重建 |
|
||||
| 只有已确认记忆或背景摘要变化 | 自动检查 | 背景不单独触发模型调用 |
|
||||
|
||||
## 验收优先级与依赖
|
||||
|
||||
首个可用版本应先打通以下路径:
|
||||
|
||||
> 应用入口 → 选择范围 → 手动回顾 → 工作回顾 → 选择知识实体 → 查看多个时间事件 → 打开来源 → 预览并继续讨论
|
||||
|
||||
建议实施顺序:
|
||||
|
||||
1. 顶层有工作回顾、故事线图谱、自动监督、活动记录、设置五项。自动监督保留计划 CRUD 与范围校验,创建和编辑使用同一 Modal 表单;运行概览、指标、趋势、自动监督报告、建议和心跳审计与计划列表同页,无计划时只有一处未配置说明和一个创建按钮。工作回顾只阅读统一监督结果,新请求的范围和运行状态不改变旧结果的日期、范围及正文;自动报告为空时不会在成功结果下显示“没有报告”。历史选择位于正文前,图谱打开所选结果的精确 ID。活动记录只保留统一执行、进度和详情。设置只显示算法参数,没有计划、报告、审计、第二套页签、整页范围徽标或独立刷新行。
|
||||
2. 增加监督者统一的工作回顾和共享结果模型。
|
||||
3. 接入最小故事线图谱:时间事件、知识实体、实体变化、来源和时间回放。
|
||||
4. 接入文字列表、窄屏和键盘操作,以及失败、取消和部分来源状态。
|
||||
5. 接入来源双向定位、继续讨论预览和本地知识库新建/补充。
|
||||
6. 侧栏监督反馈和自动增量已接入;事件驱动及语义层面的主动反馈判定仍待实现。
|
||||
|
||||
详细状态规则见[逻辑设计](./logic-design.md),页面结构和共享组件见[UI 设计](./ui-design.md),现有智能心跳的已实现范围见[智能心跳 PRD](../smart-heartbeat/prd.md)。
|
||||
@@ -22,9 +22,9 @@
|
||||
| --- | --- |
|
||||
| 直连模型 | Runtime provider 为 `model`、由 GoodBuddy 直接调用模型协议的文本模型 |
|
||||
| 进程执行 | GoodBuddy 在当前执行空间启动一个前台 Shell 命令并返回有界结果 |
|
||||
| 工作区搜索 | 通过安装包内置 ripgrep 搜索内容或列出文件,并返回紧凑结果 |
|
||||
| 工作区搜索 | 通过安装包内置 ripgrep 搜索内容或列出文件,接受原生参数并返回原生输出及退出码 |
|
||||
| 工作区补丁 | 通过 `*** Begin Patch` 格式新增、修改或删除工作区文本文件 |
|
||||
| 输出续读 | 通过 `output_read` 分页读取当前会话保留的进程或 Subagent 输出 |
|
||||
| 输出续读 | 通过 `output_read` 分页读取当前会话保留的搜索、进程或 Subagent 输出 |
|
||||
| 平台 Shell | Windows 上的 PowerShell,macOS/Linux 上的 Bash 或 POSIX Sh |
|
||||
| 编程 Subagent | 由直连模型临时委派、使用直连模型 Runtime 执行一个有界子任务的执行者 |
|
||||
| 父请求 | 发起进程调用或 Subagent 委派的当前直连模型请求 |
|
||||
@@ -43,7 +43,7 @@ Task、Job、Subjob、Run 和 Subagent 的对象关系以
|
||||
- OpenCode、Continue 和 DeepSeek Harness 继续使用各自原生执行与委派能力。
|
||||
- Ask 可搜索和分页读取工作区,但不允许补丁写入或进程执行;Subagent 若在 Ask 中使用,
|
||||
只能继承 Ask 的只读能力。
|
||||
- 进程和 Subagent 的长输出返回预览与续读位置,完整内容保留到所属会话或 Runtime 释放。
|
||||
- 搜索、进程和 Subagent 的长输出返回预览与续读位置,完整内容保留到所属会话或 Runtime 释放。
|
||||
Ask 和 Execute 均可续读当前会话已取得的输出,详见 [输出契约](./technical-design.md#43-输出边界)。
|
||||
- Execute 表示用户授权当前执行空间账号的完整能力,不增加第二套工具审批或权限档位。
|
||||
- 首版跨平台指桌面本机 Windows、macOS 和 Linux。托管 SSH 项目当前继续使用远端
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
3 次;用户取消和已显示的部分输出禁止重放。
|
||||
12. Ask 可调用 `workspace_rg` 和 `workspace_read_text`,但不能调用
|
||||
`workspace_apply_patch`;Execute 可调用三者。
|
||||
13. 工作区搜索固定使用随包 ripgrep 的结构化输出,不把任意 ripgrep 参数转成命令执行。
|
||||
14. FR-7 / US-A6:预览之外的进程和 Subagent 输出保留在所属会话下,分页续读不启动新命令。
|
||||
13. 搜索直接使用随包 rg 的原生参数和输出;Ask 的参数检查保持工作区内只读,Execute 使用账号权限。
|
||||
14. FR-7 / US-A6:预览之外的搜索、进程和 Subagent 输出保留在所属会话下,分页续读不启动新命令。
|
||||
会话释放先停止活动工作,再删除输出;子会话结束不删除父会话拥有的 Subagent 结果。
|
||||
|
||||
## 2. 状态维度
|
||||
@@ -58,7 +58,7 @@
|
||||
FR-4 / FR-12 / US-B0:Ask 工作区搜索和读取不依赖知识库、联网搜索或委派开关,不需要
|
||||
切到 Execute 或额外批准。MCP 服务器失败也不改变本机只读工具的可用性。
|
||||
|
||||
`output_read` 在直连模型拥有进程或 Subagent 服务时提供,Ask/Execute 均可用,不取决于
|
||||
`output_read` 在直连模型拥有搜索、进程或 Subagent 服务时提供,Ask/Execute 均可用,不取决于
|
||||
本轮 Shell 可用性、是否允许再次委派或委派深度。读取只接受当前会话的句柄;其他 Runtime
|
||||
不注册该工具,调用边界同样拒绝。
|
||||
|
||||
|
||||
@@ -193,16 +193,17 @@ Ask 禁止命令和写入。Execute 是用户对当前执行空间账号的完
|
||||
|
||||
### FR-12 高效工作区工具
|
||||
|
||||
- `workspace_rg` 同时支持内容搜索和文件列表,遵循项目忽略规则,并以
|
||||
`path:line:column:text` 紧凑格式返回匹配。
|
||||
- `workspace_rg` 接受原生 rg 参数和可选工作目录,支持内容搜索、上下文、文件列表及
|
||||
自选输出格式,保留原生输出与退出码。默认遵循项目忽略规则。
|
||||
- `workspace_read_text` 接受起始行和行数,大文件通过续读完成,不因超过旧的整文件上限而
|
||||
完全不可读;超长单行可使用返回的字节位置继续读取。
|
||||
- `workspace_apply_patch` 支持一个补丁新增、修改和删除多个 UTF-8 文件;执行前验证全部
|
||||
路径和 hunk,执行失败时准确报告已完成文件。
|
||||
- 搜索和读取在 Ask/Execute 均可用;补丁只在 Execute 可用,Main 调用边界再次检查模式。
|
||||
- `workspace_rg` 使用固定参数调用随包 ripgrep,不向模型开放 `--pre` 或任意命令参数。
|
||||
- `workspace_rg` 直接调用随包 ripgrep;Ask 限于工作区内只读搜索,Execute 使用当前账号权限。
|
||||
具体参数与模式边界见[工具契约](./technical-design.md#工具输入与执行)。
|
||||
- 工作区工具不限制文件总大小、补丁总大小、搜索表达式长度、glob 数量或模型请求的结果
|
||||
数量。单次返回按上下文容量截断或分页;读取结果提供续读位置,搜索结果提示模型缩小查询。
|
||||
数量。单次返回按上下文容量截断或分页;读取结果提供续读位置,搜索完整输出通过 `output_read` 续读。
|
||||
|
||||
## 6. 产品验收
|
||||
|
||||
|
||||
@@ -18,6 +18,31 @@ DeepSeek Harness 和托管 SSH 路径不注入这些工具。既有功能的 Win
|
||||
全量项目验证已通过;本次分页验证记录见下方 2026-09-10 条目。macOS 与 Linux 真机验证
|
||||
仍需由对应平台完成。
|
||||
|
||||
## 2026-09-25 原生 ripgrep 参数与完整输出
|
||||
|
||||
- FR-12:`workspace_rg` 使用 `{ args: string[], cwd?: string }`,直接启动随包 rg,
|
||||
保留原生 stdout、stderr 和退出码。此实现替代下方历史记录中的 JSON 解析与列号转换。
|
||||
- 搜索复用进程服务的取消、进程树清理和会话输出存储;超过旧 4 MiB 上限的结果及超长行
|
||||
可通过 `output_read` 完整续读。Ask 保留工作区只读边界,Execute 使用当前账号权限。
|
||||
- Windows 真实随包 rg、生产 Provider 与 Runtime 接线聚焦回归:6 个文件,
|
||||
`163 passed, 1 skipped`;覆盖原生参数、退出码 1/2、Ask 部分结果、长输出续读、
|
||||
会话归属与清理。`npm run typecheck`、`npm run lint` 通过。
|
||||
- 全量 `npm test`:`5091 passed, 72 skipped, 25 failed`,另有一个文件未发现测试套件,
|
||||
合计 7 个测试文件失败。失败涉及监督视图与旧 schema 夹具、监督设置 UI 断言和
|
||||
`scripts/review-algorithm.test.mjs`;本次未修改这些路径。全量开始后的最终搜索调整
|
||||
已由上述 6 文件聚焦回归复验,不能把本次整库验证记为通过。
|
||||
- 补充真实 DeepSeek 模型验收:Ask、Execute 两个场景均通过。模型通过原生参数调用
|
||||
随包 rg,再通过 `output_read` 找回 110 KiB 超长单行末尾的随机标记;断言工具完成事件
|
||||
进入 Runtime 活动。成功运行精确为 6 次上游请求(每个模式 3 次)、2 次搜索和 2 次续读。
|
||||
首次测试因夹具未配置 rg 路径失败,控制台未保留请求计数,因此不推算整轮调用总数。
|
||||
此验证覆盖真实 Runtime、Provider 和 rg,不作为完整桌面 UI 或 macOS/Linux 真机验证。
|
||||
- 最终聚焦回归:6 个文件,`169 passed, 1 skipped`;Typecheck、Lint 通过。
|
||||
最新全量运行在 600 秒执行窗口后超时,日志停留在附件布局 Electron 测试,未生成最终
|
||||
汇总,因此整库验证仍不能记为通过。
|
||||
- 已检查 `src/agent-daemon`、`src/main/remote-agent` 和远端 Runtime 打包入口:
|
||||
它们不导入本机 rg 执行器、ModelToolProvider 或本机进程服务;远端工作区不提供该搜索
|
||||
工具。因此本次不改变 gbagent 生产路径,不需要远端修复或 Host 验证。
|
||||
|
||||
## 2026-09-10 提交审查后的桌面 UI 验收
|
||||
|
||||
- Windows 隔离配置使用本轮最终开发构建,通过普通输入框发送触发完整
|
||||
|
||||
@@ -79,8 +79,10 @@ type BuiltinModelToolSummary = {
|
||||
|
||||
### 工具输入与执行
|
||||
|
||||
- `workspace_rg` 接受 `pattern`、相对目录、glob、固定字符串、忽略大小写、文件列表模式和
|
||||
最大结果数。Main 固定追加 `--no-config`,内容搜索读取 `--json`,对模型返回紧凑文本。
|
||||
- `workspace_rg` 接受 `{ args: string[], cwd?: string }`,直接启动随包 rg,不经过 Shell。
|
||||
默认参数为 `--no-config --color=never --line-number`,随后传入原生 args;cwd 默认工作区。
|
||||
保留原生 stdout、stderr 和退出码,不强制 JSON、不裁剪匹配行。完整输出复用进程服务的
|
||||
临时文件与 `output_read`;搜索不另设时限,取消和会话释放终止进程树。
|
||||
- `workspace_read_text` 接受 `path`、从 1 开始的 `offset` 和 `limit`,通过现有
|
||||
`WorkspaceAccess.readText` 字节分页组装带行号结果;`offsetBytes` 提供原始字节续读,
|
||||
覆盖跨多个页面的超长单行。
|
||||
@@ -89,7 +91,7 @@ type BuiltinModelToolSummary = {
|
||||
- Ask 只注册前两个只读工具;Execute 注册全部三个工具。旧的目录列表和整文件覆盖不再进入
|
||||
新工具清单。
|
||||
- 输入和目标文件不设固定总量上限。单次工具结果仍遵守直连模型上下文边界;读取返回续读
|
||||
位置,搜索截断时要求模型缩小路径、glob 或表达式。
|
||||
位置,搜索预览省略的内容通过 `output_read` 续读。
|
||||
|
||||
### 读取与搜索失败恢复
|
||||
|
||||
@@ -102,19 +104,16 @@ Runtime 向模型返回 `{ ok: false, recoverable: true, error, nextAction }`
|
||||
提示不得原样重复失败参数。取消、远端断线、失效工作区、缺失 rg 可执行文件及未分类的内部
|
||||
错误继续向外传播,不转为可恢复结果。
|
||||
|
||||
读取和搜索仍只接受工作区内相对路径,绝对路径即使指向工作区内文件也拒绝;原有越界及
|
||||
符号链接检查继续生效。仅当本机直连模型 Execute 的 `process_execute` 实际可用时,恢复
|
||||
提示才建议用它访问工作区外文件。此处理不改变 Windows 用户权限、UAC 或文件 ACL,
|
||||
也不提升进程权限;Ask 仍保持只读。
|
||||
`workspace_read_text` 仍只接受工作区内相对路径。搜索在 Ask 中检查 cwd、搜索路径、模式文件
|
||||
和忽略文件位于工作区内,并阻止 `--pre`、`--hostname-bin`、`--follow/-L`、`--search-zip/-z`,
|
||||
避免只读调用启动外部命令或跟随链接越界。Execute 原样接受 rg 参数及账号可访问的路径,
|
||||
不增加逐工具授权。两种模式均禁用外部 rg 配置,不提升系统权限。
|
||||
|
||||
ripgrep 结果按退出状态处理:
|
||||
|
||||
- exit 1 保持正常无匹配结果。
|
||||
- exit 2 有 stdout 且不是 regex/glob 编译错误时,保留已取得的匹配或文件列表,附加
|
||||
`incomplete search coverage` 警告及有界 stderr。零匹配只代表已搜索部分,不能据此断言
|
||||
整个目标不存在匹配;输出截断提示仍独立保留。
|
||||
- exit 2 无 stdout,或出现无效 regex/glob 编译错误时,搜索层抛错,再由 Provider 转为
|
||||
上述可恢复结果;模型可修正表达式,字面搜索可使用 `fixedStrings`。
|
||||
- exit 2 返回原生 stderr 和已有 stdout,表示错误或搜索覆盖不完整;模型可修改参数继续。
|
||||
无效正则同样返回 exit 2;字面搜索使用 `-F`。
|
||||
|
||||
该契约属于桌面 `ModelAgentRuntime → ModelToolProvider` 内置工具路径,复用浏览器标签页
|
||||
关闭和元素引用失效已有的可恢复错误机制;浏览器分别提示 `browser_navigate` 或
|
||||
@@ -203,7 +202,7 @@ type ProcessExecuteResult = {
|
||||
页面在字符边界结束;若请求大小容不下首个完整字符,最多扩展到 4 字节,保证续读前进。
|
||||
位于 UTF-8 延续字节的输入 cursor、负数、越界和未知字段均拒绝。
|
||||
|
||||
Provider 根据 `process:` 或 `subagent:` 前缀选择服务,以当前 `conversationId` 校验所有者,
|
||||
Provider 根据 `rg:`、`process:` 或 `subagent:` 前缀选择服务,以当前 `conversationId` 校验所有者,
|
||||
不接受模型另传 owner。Ask/Execute 均可用,工具总数为此预留一个槽位;不要求本轮能够启动
|
||||
命令或再次委派。分页结果直接 JSON 序列化,不进行二次文本裁剪,以免 cursor 与内容不一致。
|
||||
32 KiB 的页面即使全部需要 JSON 转义,也能保留在现有工具结果容量内。
|
||||
|
||||
@@ -1,16 +1,47 @@
|
||||
# 文档处理
|
||||
|
||||
文档处理负责聊天附件和知识库导入中的文本提取、格式转换与本地 OCR。
|
||||
文档处理负责聊天附件和知识库导入中的文本提取、本地 OCR、HTTP PaddleOCR-VL、原件与
|
||||
解析产物保存,以及正文、图片、详情预览。格式转换仍为规划能力。
|
||||
2026-09-19 已接入生产链路;验证层次、样例和未覆盖项见[实施进度](./progress.md#2026-09-19-实现与验证)。
|
||||
|
||||
## 文档导航
|
||||
|
||||
| 文档 | 权威职责 |
|
||||
| --- | --- |
|
||||
| [文档解析与本地 OCR PRD](./prd.md) | 产品范围、解析策略、OCR 模型管理和验收 |
|
||||
| [文档解析与本地 OCR PRD](./prd.md) | 产品范围、解析策略、OCR 模型管理,以及规划中的解析资源持久化、配置预览行为和验收 |
|
||||
| [聊天附件技术说明](./chat-attachments-technical-design.md) | 文件选择与粘贴的进程链路、IPC、PPTX 图片 OCR、Worker 生命周期和验证证据 |
|
||||
| [HTTP OCR User Stories](./user-stories.md) | HTTP 接入、纯图片路由与资源保存的用户验收 |
|
||||
| [HTTP OCR 逻辑设计](./logic-design.md) | 输入路由、来源选择、页码、资源归属与失败规则 |
|
||||
| [文档解析与附件 UI 设计](./ui-design.md) | 设置、共享预览、聊天与知识库选图、附件状态、响应式、无障碍及完整验收矩阵 |
|
||||
| [HTTP PaddleOCR-VL 技术设计](./http-paddleocr-vl-technical-design.md) | 计划接入点、选项契约、资产归属和实现顺序 |
|
||||
| [实施进度与验证](./progress.md) | 实测命令、调用计数、部署差异和未验证能力 |
|
||||
| [生产实现说明](./implementation.md) | 当前存储、IPC、迁移、生命周期、Runtime 校验与设计差异 |
|
||||
| [改动文件清单](./changed-paths.md) | 本轮实施涉及的完整文件路径,区分并行工作区改动 |
|
||||
|
||||
当前尚无独立 User Stories、功能逻辑、UI 和进度文档;聊天输入区交互遵循根目录
|
||||
[UI 设计系统](../../../UI-DESIGN.md)。
|
||||
User Stories、逻辑设计和进度文档覆盖 HTTP OCR 扩展、纯图片路由及资源持久化,本地 OCR
|
||||
既有实现仍由 PRD 及聊天附件技术说明维护。界面行为由 UI 设计维护并遵循根目录
|
||||
[UI 设计系统](../../../UI-DESIGN.md)。完整目标包含文档选图加入会话并发送,不以实施顺序
|
||||
缩减产品范围。具体生产实现和验证证据分别由实施说明与进度文档维护。
|
||||
|
||||
全局配置、本机推理监控、聊天附件和知识文档的入口分工,以及草稿、历史与队列的菜单规则,
|
||||
统一见 [入口与职责](./ui-design.md#11-入口与职责)和[附件操作菜单](./ui-design.md#12-附件操作菜单)。
|
||||
首次菜单核对保留在[历史记录](./progress.md#菜单核对),当前接线见生产实现说明。
|
||||
|
||||
本文档中的“内联图片”指服务在 JSON 中返回的 Base64 图片字节;“图片键”指
|
||||
Markdown 引用及 `markdown.images` 中的页内键,不是可直接写入磁盘的路径;“重组”指
|
||||
对已解析页面调用 `/restructure-pages`,不等于已经验证跨页表格合并质量。
|
||||
“会话资源”是应用 userData 下的文件资产及数据库归属引用,不是 Runtime 项目文件;
|
||||
服务返回“内联图片”也不表示产品将图片字节长期存入数据库。
|
||||
|
||||
“待添加选择”仅指预览内尚未提交的勾选;“已在草稿”指目标会话实际持有的附件引用。
|
||||
“文档插图附件”是显式复制到会话资源目录的独立图片,保留来源说明,不依赖知识库原结果
|
||||
继续存在。“提取文字”是图片附件的一种发送方式,原图仍保留,不表示原图同时发给模型。
|
||||
|
||||
“纯图片附件”指直接添加到聊天的图片,与 PDF、Office 解析提取的“文档插图”分开。
|
||||
普通图片默认直接作为模型图片输入,明确提取文字才调用配置的 OCR;不支持图片输入时的
|
||||
提示和选择、原图长期保存见 [图片路由](./prd.md#322-纯图片与文档的路由规划);验证范围见进度。
|
||||
约定的 userData 目录、既有 knowledge 原件与规划派生资源的区别统一见
|
||||
[结果与资源](./http-paddleocr-vl-technical-design.md#结果与资源)。
|
||||
|
||||
Office 文件的浏览器内人工编辑、AI 选区修改、保存和撤销不属于文档解析职责,由
|
||||
[Office 协同编辑](../office-document-editing/README.md)负责。文档处理仍负责内容提取、OCR、
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
# 本轮文档处理改动文件
|
||||
|
||||
本清单只列文档处理实施涉及的文件。工作区内并行的魔法笔记、画布及其测试改动不计入本轮;
|
||||
共享文件中的并行改动保持原样,未提交或推送。
|
||||
|
||||
## Main
|
||||
|
||||
```text
|
||||
src/main/assistant/assistant-database.ts
|
||||
src/main/context-manager.ts
|
||||
src/main/context-manager.test.ts
|
||||
src/main/conversation-attachment-storage.ts
|
||||
src/main/conversation-attachment-storage.test.ts
|
||||
src/main/document-extracted-text.ts
|
||||
src/main/document-extracted-text.test.ts
|
||||
src/main/document-parsing-service.ts
|
||||
src/main/document-parsing-service.test.ts
|
||||
src/main/document-parsing-settings-store.ts
|
||||
src/main/document-parsing-settings-store.test.ts
|
||||
src/main/document-result-storage.ts
|
||||
src/main/http-document-ocr.ts
|
||||
src/main/http-document-ocr.test.ts
|
||||
src/main/http-document-ocr.integration.test.ts
|
||||
src/main/render-ocr-pdf.ts
|
||||
src/main/index.ts
|
||||
src/main/ipc.ts
|
||||
src/main/ipc.test.ts
|
||||
src/main/knowledge/document-parser.ts
|
||||
src/main/knowledge/knowledge-database.ts
|
||||
src/main/knowledge/knowledge-database.test.ts
|
||||
src/main/knowledge/knowledge-service.ts
|
||||
src/main/knowledge/external/external-knowledge-service.test.ts
|
||||
```
|
||||
|
||||
## Shared 与 Preload
|
||||
|
||||
```text
|
||||
src/shared/attachment-limits.ts
|
||||
src/shared/assistant-contracts.ts
|
||||
src/shared/contracts.ts
|
||||
src/shared/document-parsing-contracts.ts
|
||||
src/shared/document-result-contracts.ts
|
||||
src/shared/ipc-channels.ts
|
||||
src/shared/local-inference-contracts.ts
|
||||
src/preload/index.ts
|
||||
src/preload/preload-sandbox.test.ts
|
||||
```
|
||||
|
||||
## Renderer
|
||||
|
||||
```text
|
||||
src/renderer/src/App.tsx
|
||||
src/renderer/src/App.test.tsx
|
||||
src/renderer/src/AttachmentActions.tsx
|
||||
src/renderer/src/AttachmentActions.test.tsx
|
||||
src/renderer/src/AttachmentCapabilityNotice.tsx
|
||||
src/renderer/src/AttachmentResultButton.tsx
|
||||
src/renderer/src/ChatTimeline.tsx
|
||||
src/renderer/src/ConversationInputQueue.tsx
|
||||
src/renderer/src/DocumentConversationContext.ts
|
||||
src/renderer/src/DocumentParsingSettingsSection.tsx
|
||||
src/renderer/src/DocumentParsingSettingsSection.test.tsx
|
||||
src/renderer/src/DocumentResultPreview.tsx
|
||||
src/renderer/src/DocumentResultPreview.test.tsx
|
||||
src/renderer/src/HttpOcrSettings.tsx
|
||||
src/renderer/src/KnowledgeWorkspace.tsx
|
||||
src/renderer/src/LocalInferencePage.tsx
|
||||
src/renderer/src/PendingDocumentImports.tsx
|
||||
src/renderer/src/QueuedAttachmentsDialog.tsx
|
||||
src/renderer/src/SettingsPanel.tsx
|
||||
src/renderer/src/styles.css
|
||||
src/renderer/src/i18n/locales/en-US/app.ts
|
||||
src/renderer/src/i18n/locales/zh-CN/app.ts
|
||||
```
|
||||
|
||||
## 脚本与文档
|
||||
|
||||
```text
|
||||
tests/attachment-layout.electron.test.ts
|
||||
tests/support/attachment-layout-regression.tsx
|
||||
scripts/document-parsing-production-probe.ts
|
||||
scripts/document-table-fixture.mjs
|
||||
FEATURES.md
|
||||
FEATURES.zh-CN.md
|
||||
UI-DESIGN.md
|
||||
docs/features/application-tool-navigation/ui-design.md
|
||||
docs/features/document-processing/README.md
|
||||
docs/features/document-processing/prd.md
|
||||
docs/features/document-processing/user-stories.md
|
||||
docs/features/document-processing/logic-design.md
|
||||
docs/features/document-processing/ui-design.md
|
||||
docs/features/document-processing/http-paddleocr-vl-technical-design.md
|
||||
docs/features/document-processing/chat-attachments-technical-design.md
|
||||
docs/features/document-processing/implementation.md
|
||||
docs/features/document-processing/progress.md
|
||||
docs/features/document-processing/changed-paths.md
|
||||
docs/quality/recurring-defects.md
|
||||
```
|
||||
|
||||
`scripts/paddleocr-vl-probe.mjs` 和导航 README 原有未提交内容保留,本轮未修改。
|
||||
临时 Electron 驱动、隔离构建、合成文件和原始报告未加入产品源码,证据目录索引见进度文档。
|
||||
@@ -24,6 +24,54 @@ Main。IPC 先验证可信 sender,再使用共享 `contextImportFilesSchema`
|
||||
会话草稿;发送时通过已有 `contextIds` 和 `ContextManager.enrichRequest` 生成请求上下文。
|
||||
本次变更不修改 Agent、远端协议或 Runtime 的附件消费实现。
|
||||
|
||||
## 2026-09-19 生产更新
|
||||
|
||||
HTTP 来源、文件资产、持久化草稿、显式图片 OCR、选图、历史预览和队列恢复已接入。
|
||||
当前行为以[生产实现说明](./implementation.md)为准;本文件后续“纯图片现状与规划边界”
|
||||
及“完整附件流程设计”保留 2026-09-18 的实施前分析,不能作为当前未实现清单。
|
||||
历史 Electron 记录仍按原日期保留,本轮证据见[进度](./progress.md#2026-09-19-实现与验证)。
|
||||
|
||||
## 纯图片现状与规划边界
|
||||
|
||||
当前 `ContextManager` 将本地图片、粘贴图片和消息通道图片交给 `storeImage`,通过
|
||||
`encodeBoundedJpeg` 转成 JPEG 暂存在内存;`enrichRequest` 将其放入 `images`,不调用
|
||||
`DocumentParsingService`。`serializeForQueue` 保存上下文内容,`restoreFromQueue` 可恢复
|
||||
队列图片,但这些机制未实现原始图片的独立文件保存。
|
||||
|
||||
图片能力由有效模型和 Runtime 决定。例如 `ModelRuntime` 在未启用图片输入时拒绝图片请求;
|
||||
图片工具绑定路径还可能只保留工具引用并告知聊天模型无法看图,不能由附件已添加推断模型
|
||||
已看到图片。现有路径没有统一的“提取图片文字或切换视觉模型”交互,本文不声称已经实现。
|
||||
|
||||
已确认的目标规则见 [纯图片路由需求](./prd.md#322-纯图片与文档的路由规划),原图与解析
|
||||
文档分开保存的目录及生命周期见 [资源规划](./http-paddleocr-vl-technical-design.md#结果与资源)。
|
||||
显式图片 OCR、原图保留、草稿引用保护和长期预览均待实现,以下历史验证不覆盖这些能力。
|
||||
|
||||
## 完整附件流程设计
|
||||
|
||||
本节为待实现范围,与下述既有验证记录分开。设置与预览的布局、交互及验收由
|
||||
[UI 设计](./ui-design.md)维护,发送模式、幂等、限制及删除规则见
|
||||
[选图与发送](./logic-design.md#选图与发送),进程契约和资源提交见
|
||||
[HTTP 技术设计](./http-paddleocr-vl-technical-design.md#选图与附件请求)。
|
||||
|
||||
需要在现有聊天链路中补齐:
|
||||
|
||||
- `App.tsx` 的会话草稿附件增加读取持久化资源、逐附件解析状态、预览、重试和取消。
|
||||
当前 `attachmentsByConversation` 与内存 contexts 不构成跨重启持久化。
|
||||
- 共享附件契约保存资源引用、原始名称、来源和发送方式。图片 OCR 成功只改变该草稿的
|
||||
发送方式与显示名,原图仍可打开;消息与队列保留接受时的文字/图片选择。
|
||||
- 文档预览添加选图与普通导入汇入同一草稿和请求消费路径。选图采用整批提交及来源幂等,
|
||||
不能直接套用新增文件按剩余位置截取的 UI 逻辑。父文档全文只送一次,图片分别计数。
|
||||
- 数量及容量值统一读取已有附件规则;Main 对草稿、入队和实际执行分别核验能力及有效引用,
|
||||
UI 不凭模型名称或图片工具引用推断已支持视觉。
|
||||
- 草稿、发送和队列先保存会话资产与 DB 引用,再报告接受;从知识库复制的图片属于会话,
|
||||
不依赖源库文档继续存在。移除父文档、图片、队列或会话各自只释放对应引用。
|
||||
- `ContextFileSelectionProgress` 当前只有读取/解析阶段,需增加阶段、操作 ID 和取消接线;
|
||||
首次批次失败仍回滚本批,新结果失败则保留旧附件。重启不自动恢复中断的 HTTP 推理。
|
||||
|
||||
新增图片发送路径仍需核对实际 Runtime 消费,包括远端 Agent;前文“本次变更不修改 Agent”
|
||||
描述的是已完成文件粘贴工作,不是对本节未来实现范围的保证。生产实现需按仓库规则验证
|
||||
受影响的远端路径,不能用历史 Windows 文件粘贴测试替代。
|
||||
|
||||
## PPTX 图片识别与 OCR 生命周期
|
||||
|
||||
产品策略与限制分别见 [PPTX 图片流程](./prd.md#75-pptx-图片流程)、
|
||||
@@ -42,7 +90,7 @@ Bridge 用待完成请求计数判断队列是否空闲,正常完成后安排
|
||||
不增加 IPC、持久化设置或模型安装状态。
|
||||
|
||||
解析与回收发生在 Desktop 的 Main/Renderer,`gbagent` 不运行此 OCR Worker。
|
||||
远程 Runtime 消费的仍是 Desktop 已提取的附件文本,没有单独的 Agent 解析器或
|
||||
对于 PDF、Office 文档,远程 Runtime 消费的仍是 Desktop 已提取的附件文本,没有单独的 Agent 解析器或
|
||||
需要同步修改的远程 OCR 生命周期。
|
||||
|
||||
## Electron 验证
|
||||
|
||||
@@ -0,0 +1,266 @@
|
||||
# HTTP PaddleOCR-VL 技术设计
|
||||
|
||||
本设计实现 [PRD 的 HTTP 扩展](./prd.md#13-http-paddleocr-vl-扩展规划)和
|
||||
[逻辑规则](./logic-design.md)。下文保留实施前的接口分析与目标约定,当前文件布局、
|
||||
迁移编号、IPC 及生命周期以[生产实现说明](./implementation.md)为准;
|
||||
接口证据包含 2026-09-18 探索和[2026-09-19 生产验证](./progress.md#2026-09-19-实现与验证)。
|
||||
|
||||
## 现有接入点
|
||||
|
||||
`src/main/document-parsing-service.ts` 的 `DocumentParsingService` 统一处理聊天、知识库、
|
||||
成果导入及诊断。PDF 使用原生文本质量选择待 OCR 页;当前 `Map<pageNumber, section>`
|
||||
只保留每页一个 OCR section。PPTX 在原生文字外识别直接引用的内嵌图片。
|
||||
|
||||
本地 OCR 经 `DocumentOcrBroker`、Renderer bridge 和 PP-OCRv6 WASM Worker 运行,
|
||||
服务中的 PDF、PPTX 分支先检查已安装模型。`documentOcrSectionSchema.confidence`
|
||||
目前必填,但 `ParsedSection.confidence` 已可选。`ParsedDocument` 尚无图片资源字段。
|
||||
这些差异需显式修改,不能只替换一次网络请求。
|
||||
|
||||
计划在 Desktop Main 中增加一个小型 HTTP Provider,由解析服务按来源调用;本地 Broker
|
||||
保留原路径。HTTP、认证、图片解码和持久化全部留在 Main,Renderer 只收状态、预览和
|
||||
受管资源标识。现有 Desktop 文档解析后向 Runtime 传文本的边界继续适用;普通图片已有
|
||||
独立图片请求路径。`gbagent` 无独立 OCR Worker,HTTP OCR 本身不要求 Agent 部署变更。
|
||||
图片输入支持须核对本次有效模型配置及本地、远端 Runtime 实际消费路径,不作全 Runtime 保证。
|
||||
|
||||
## 配置与请求
|
||||
|
||||
计划扩展 `document-parsing-contracts.ts` 和 `DocumentParsingSettingsStore`:来源默认
|
||||
`local`,HTTP 类型固定为 `paddleocr-vl`,保存用户输入的 base URL、Main 加密凭据和
|
||||
类型化高级选项。设置缺少新字段时读为原有本地行为;不能把本次实测地址写为默认值。
|
||||
旧设置兼容有已保存用户配置这一具体需求。设置写入仍经共享 Zod 与可信 sender 校验。
|
||||
|
||||
认证契约为无需认证或 Bearer API Key,不新增任意请求头编辑器。Main 从加密设置读取
|
||||
密钥并构造 Authorization;Renderer 只收 configured 状态,空密钥输入表示保持现值,
|
||||
清除使用显式操作。切换来源或地址不删除凭据。当前探测未验证认证部署,不能把该计划写成
|
||||
已通过服务测试;实现须覆盖无认证、有效 Bearer、401 及凭据未配置的真实入口。
|
||||
|
||||
设置保存保留本地与 HTTP 两组值,只切换活动来源。高级布尔值采用省略/true/false,
|
||||
过滤模式及自定义标签值分开保存,发送时按 L-H2 生成 `markdownIgnoreLabels`。服务默认
|
||||
不展开成客户端猜测值,结果记录显式请求选项及服务返回的有效值(若有)。连接检查和
|
||||
测试解析要求无未保存修改;正式解析直接走 Provider,不调用 health 或 test 作为前置。
|
||||
|
||||
| 接口 | 计划用途与请求 |
|
||||
| --- | --- |
|
||||
| `GET /health` | 连通性检查,不证明推理能力 |
|
||||
| `GET /openapi.json` | 诊断部署声明的字段、类型与默认值;不动态生成任意网络调用 |
|
||||
| `POST /layout-parsing` | `file` 为 Base64;`fileType: 0` 是 PDF,`1` 是图片;固定 `returnMarkdownImages: true`、`visualize: false` |
|
||||
| `POST /restructure-pages` | 发送 `pages: [{ prunedResult, markdownImages }]`,其中 `markdownImages` 来自页面的 `markdown.images`;显式设置 `mergeTables`、`relevelTitles` |
|
||||
|
||||
默认按选中页渲染图片请求,保留准确输入页号;全页高保真可在核验页数及请求限制后使用
|
||||
整份 PDF。OpenAPI 未声明页范围参数,不能发送一个猜测的 `pageNumbers` 参数并假定生效。
|
||||
单页图片策略需要独立实现 HTTP 用的渲染入口,不能为使用旧 Worker 而要求安装 OCR 模型。
|
||||
|
||||
每次任务冻结配置,沿用原文件大小、页数、文本输出与取消边界;HTTP 增加有界响应读取和
|
||||
独立请求超时。多页请求超时不能冒称单页耗时。HTTP 状态、服务 `errorCode`、任务取消及
|
||||
图片解码错误分别保留,错误正文脱敏后限制长度。没有自动重试或 Provider 轮换。
|
||||
|
||||
## 高级参数
|
||||
|
||||
下表是接口字段映射,产品可配置性是规划;“声明”不代表部署模型可用。
|
||||
空值语义遵循 L-H2。探测未请求 `visualize: true`,调试渲染图片不作为正文插图。
|
||||
|
||||
| 选项 | 类型或服务 schema 默认 | 本次证据 |
|
||||
| --- | --- | --- |
|
||||
| `markdownIgnoreLabels` | 可空字符串数组;schema 未列默认标签集合 | 省略、`[]`、显式排除 `text/header/footer/number` 均实测有效 |
|
||||
| `useDocOrientationClassify`、`useDocUnwarping` | 可空 boolean | 仅声明,未启用验证 |
|
||||
| `useLayoutDetection` | 可空 boolean | 默认结果有版面标签;未比较开关 |
|
||||
| `useChartRecognition`、`useSealRecognition`、`useOcrForImageBlock` | 可空 boolean | 仅声明,未比较开关,不能推断对应模型已安装 |
|
||||
| `prettifyMarkdown`、`showFormulaNumber` | boolean,默认 true / false | 使用默认值;格式开关及公式编号效果未验证 |
|
||||
| `formatBlockContent`、`mergeLayoutBlocks` | 可空 boolean | 仅声明 |
|
||||
| `layoutThreshold`、`layoutNms`、`layoutUnclipRatio`、`layoutMergeBboxesMode` | 数字、boolean、数组或对象等,以 schema 为准 | 仅声明 |
|
||||
| `layoutShapeMode` | `rect/quad/poly/auto`,默认 auto | 未比较 |
|
||||
| `promptLabel`、`repetitionPenalty`、`temperature`、`topP` | 可空字符串或数字 | 仅声明,不承诺调参收益 |
|
||||
| `minPixels`、`maxPixels`、`maxNewTokens` | 可空正整数 | 仅声明,需另验有效边界 |
|
||||
| `vlmExtraArgs` | 可空 object | 任意参数对象编辑不在约定范围;不影响已约定类型化高级选项的实现 |
|
||||
| `restructurePages` | layout 请求中默认 false | 未测试该一体化入口,使用独立重组接口 |
|
||||
| `mergeTables`、`relevelTitles` | 默认 true | 独立重组接口成组 false / true 请求成功,但结果相同 |
|
||||
| `concatenatePages` | 仅独立重组请求,默认 false | 未启用验证 |
|
||||
| `outputFormats` | 可空字符串数组,如 `docx` | 仅声明,导出不在本次范围 |
|
||||
|
||||
已知服务默认排除集合为 `number`、`footnote`、`header`、`header_image`、`footer`、
|
||||
`footer_image`、`aside_text`;本次样例只实证页眉、页脚和页码的默认过滤。脚注、页眉图、
|
||||
页脚图、侧栏未构造,不把完整集合误写成 OpenAPI 明示默认或全部通过测试。
|
||||
|
||||
## 结果与资源
|
||||
|
||||
`result.layoutParsingResults[]` 包含 `prunedResult`、`markdown.text`、`markdown.images`。
|
||||
此部署的 `prunedResult` 没有 `page_index`,`page_count` 为 null;使用输入页映射和响应
|
||||
数量检查。字段缺失是本次真实差异,无需猜测版本或伪造置信度。内部 Provider 结果计划将
|
||||
confidence 改为可选,保持本地返回原值;多块内容组成逐页 section 并保留块到源页的关联,
|
||||
避免当前按页 Map 丢失块。跨页输出另带原始页集合,不覆盖逐页来源事实。
|
||||
|
||||
插图实测为裸 Base64 JPEG,Markdown 使用 `<img src="imgs/…jpg">`。同一键可在不同页
|
||||
出现,因此资源 ID 由客户端生成,Provider 图片键仅作为页内关联。计划给 `ParsedDocument`
|
||||
增加图片集合,记录页号、页内键、MIME、尺寸及受管资源标识;解析暂存阶段携带有界字节,
|
||||
由调用方提交所有权。只解码内联数据,验证格式及尺寸,不请求返回 URL。
|
||||
|
||||
现有存储不能直接宣称已覆盖该需求:
|
||||
|
||||
| 现有机制 | 计划复用与需补齐之处 |
|
||||
| --- | --- |
|
||||
| `AssistantDatabase.createImageArtifact` | 图片目前存 `artifacts.inline_content` data URI,`createInlineArtifact` 限制编码后 5 MiB。聊天解析文档改用 userData 下的文件资产与数据库引用,不复用该内联字节存储,也不借用图片生成 operation 或最多 8 项的消息生成图片列表。 |
|
||||
| `deleteLocalConversation` | 当前直接删除的任务成果仅限 markdown;新 OCR 图片必须按新增文档所有者关系显式清理,不能假设删会话自动删图。项目删除已有项目成果清理路径,需合并验证。 |
|
||||
| `KnowledgeService` 的 `managedRoot` | 现有受管原文件位于 knowledge/库 ID/来源 ID 下,reference 模式保留外部原文件路径。规划的 knowledge-assets 单独保存文档级解析产物,两种模式均不将派生图片写入用户原文件旁。 |
|
||||
| `KnowledgeDatabase` 的文档、chunk metadata | 计划保存文档图片清单与页级引用,分块只放关联 ID,不复制 Base64;删除文档、删除来源、同步替换时一并处理图片。reference 来源删除当前不清理 managedRoot,需为派生图片补齐清理。 |
|
||||
|
||||
按 FR-H8 至 FR-H12 和 FR-I3,以下为约定的 `userData` 资源目录规划,不放入 Runtime 项目
|
||||
工作目录。只有 `knowledge/<kbId>/<sourceId>/` 受管原文件布局已存在,其余目录待实现;
|
||||
这里仅列文档与附件相关资源,不是 userData 的完整文件清单。
|
||||
|
||||
```text
|
||||
<userData>/
|
||||
conversation-assets/<conversationId>/
|
||||
images/<attachmentId>/
|
||||
original.<ext>
|
||||
documents/<attachmentId>/
|
||||
original.<ext>
|
||||
parsed.md
|
||||
manifest.json
|
||||
images/<imageId>.<ext>
|
||||
knowledge/<kbId>/<sourceId>/
|
||||
<managed-original-files>
|
||||
knowledge-assets/<kbId>/<documentId>/
|
||||
parsed.md
|
||||
manifest.json
|
||||
images/<imageId>.<ext>
|
||||
temp/document-parsing/<operationId>/
|
||||
<staged-input-and-results>
|
||||
```
|
||||
|
||||
会话附件原文件统一使用 `original.<ext>`,扩展名与保存字节的格式一致;原始显示名称、MIME、尺寸
|
||||
及会话、消息、附件归属由数据库维护,不依赖磁盘文件名表达。文件导入保留输入原始字节;
|
||||
剪贴板或截图没有源文件时保留该入口取得的原始图像,不能把请求转码副本称为外部原文件。
|
||||
纯图片不要求 `parsed.md` 或 `manifest.json`。显式 OCR 后才保存关联的文档解析产物,
|
||||
通过数据库引用原图片附件,无需重复复制原图。请求用的缩放、转码副本不替换 `original.<ext>`。
|
||||
|
||||
文档清单只描述结果需要的页码、页内图片键、受管图片 ID 和正文
|
||||
引用映射,不建立版本历史或完整配置快照;数据库作为归属与引用的判断依据。随结果记录
|
||||
解析时所选 Provider 和生效选项,凭据仍只在加密设置中,查看历史结果不重新读取当前配置解析。
|
||||
|
||||
结果契约至少增加 resultId、完整程度(完整/部分/仅图片)、页/section 到资源的映射、
|
||||
来源页集合、缺页缺图诊断和上述解析元数据。任务运行状态与结果完整程度分开,避免失败
|
||||
重解析覆盖旧成功结果。无文本但有图片可持久化为仅图片结果;知识库不生成空 chunk,
|
||||
聊天不生成可发送的空 text context。原生、本地和 HTTP 结果使用同一预览契约,图片
|
||||
集合可以为空;原生解析器不因字段存在就被宣称具有新增提图能力。
|
||||
|
||||
当前聊天解析文本仅在 contexts 和发送队列中使用,不能据此宣称已长期保存。
|
||||
规划中草稿原文件、文本和图片先由应用暂存承接;撤销附件、批次失败或取消时仅清理本次
|
||||
无有效引用的资源。`temp/document-parsing/<operationId>` 用于解析任务,不能作为一律可删
|
||||
的判断依据;可恢复草稿仍引用的文件须保留,或先转入稳定资源并更新引用,再清理暂存。
|
||||
启动、退出清理同样检查引用。发送被接受及队列记录持久化前须完成资源持久化及会话归属,队列保存可恢复的附件引用,
|
||||
恢复发送不依赖内存 context ID 或外部原路径。持久化失败则不报告发送已接受,保留草稿供重试。
|
||||
取消生成不回收已归属会话的附件。删除会话时检查有效引用,仅清理独占资源;项目删除路径
|
||||
也需验证同一规则。采用暂存、现有事务和按数据库引用核对清理,不新增恢复引擎。
|
||||
|
||||
测试解析使用任务临时目录,结束预览且无有效引用后清理。知识库正文及 chunks 继续存数据库,
|
||||
`knowledge-assets` 的 `parsed.md` 为解析正文文件,随同一次结果提交保持一致,不独立修改
|
||||
或替代数据库中的检索内容。图片归知识库文档所有,不依赖聊天存活;managed 与 reference
|
||||
派生文件均存该目录,reference 外部原件保持原路径,现有 managed 原件目录不迁移。
|
||||
重解析或同步先在本次暂存目录完成新结果与必要资源校验,成功提交替换后清理无引用旧资源;
|
||||
失败或取消只删除新暂存,保留旧正文、chunks、图片及关联。聊天草稿重解析同样成功后替换
|
||||
草稿引用;历史消息和队列保持已接受结果,再解析时创建新草稿附件。若同一旧结果仍被消息
|
||||
或队列引用,新结果使用新的资源 ID 和目录,不覆盖原文件字节;这是有效引用保护,不是
|
||||
产品版本历史。旧结果没有有效引用后即可清理。
|
||||
|
||||
Markdown 提交前将 HTML 与 Markdown 图片引用改为受管资源引用,预览通过 Main 读取。
|
||||
文件资产不套用内联成果的 5 MiB 编码上限;实际文件与响应容量需在实施时明确,超限须报错,
|
||||
不静默压缩或丢弃。模型上下文仍按现有规则检查,不把图片文件全部计作提示词。跨页重组先传回页内图片
|
||||
映射,重组后再次验证引用和源页。按 FR-I5,持久化图片集合与请求图片集合分开;后者须有
|
||||
明确选中项,并校验有效模型、Runtime、数量及容量限制。选图按下节提交独立会话副本,
|
||||
数量和发送容量复用既有附件限制,不能遍历已保存图片全部发送。知识库图片文字提取与索引按
|
||||
知识库工作流路由,不依赖聊天模型视觉能力;图片向量索引不在本轮范围。
|
||||
涉及已发布数据库结构时,实施前遵循
|
||||
[数据库迁移规则](../../development/database-migrations.md),本文不预先编造迁移编号。
|
||||
|
||||
## 选图与附件请求
|
||||
|
||||
实现 L-I3 至 L-I5、L-H9 和 [UI 第 4、5 节](./ui-design.md#4-文档插图加入会话)。
|
||||
以下为需增加的契约内容,名称按实现模块风格确定,不是现有可用 IPC。
|
||||
|
||||
| 契约 | Main 行为与持久化 |
|
||||
| --- | --- |
|
||||
| 获取解析结果 | 输入归属对象 ID 与 resultId,返回正文、定位、图片描述、诊断和无凭据配置;不重新解析,不一次把全部图片 Base64 发给 Renderer |
|
||||
| 读取图片或打开原件 | 输入受管资源 ID,Main 解析已登记路径;按需提供预览图/原图或用现有系统打开入口;Renderer 不提交任意磁盘路径 |
|
||||
| 添加选图 | 输入目标 conversationId、sourceResultId 和去重 imageIds;核对目标可编辑、来源存在及共享限制,复制后提交草稿引用,返回新增和已存在附件 |
|
||||
| 提取图片文字 | 输入图片附件 ID,使用已保存 OCR 设置启动可取消任务,成功保存结果后更新草稿的发送方式,保留原图与 provenance |
|
||||
| 修改发送方式/移除附件 | 只修改目标草稿;切回图片校验能力,移除解除草稿引用并清理无引用资产,不按来源 ID 级联删除 |
|
||||
| 进度/取消/结束预览 | 通过 operationId 关联任务与所属会话或测试;扩展当前只有 reading/parsing 的进度契约以表示请求、校验、保存与取消,不伪造百分比 |
|
||||
|
||||
共享 Zod、可信 sender 和显式 preload 方法继续使用现有边界。结果读取和归属查找用
|
||||
数据库主键或索引,不扫描全部会话/全部知识库文档。UI 的目标选择由 App 通过现有项目、
|
||||
会话导航和 `startNewConversation` 管理;KnowledgeWorkspace 接收回调,不自行创建
|
||||
会话 store。已有 `onUseInChat` 会修改检索选择,不能用它代替图片添加回调。
|
||||
|
||||
文档插图复制到 `conversation-assets/<conversationId>/images/<attachmentId>/original.<ext>`,
|
||||
由 Main 从已验证的源文件复制,禁止 Renderer 回传 Base64 再导入。附件数据库字段记录
|
||||
原名称、显示名称所需信息、MIME、尺寸、原始字节数、resourceId、发送方式,以及来源类型、
|
||||
源文档/附件 ID、resultId、imageId、文档名、页码/定位。provenance 的可读信息在源删除后
|
||||
仍保留,源 ID 仅用于仍有效时跳转。跨库复制不建立删除级联外键,不做跨会话物理去重。
|
||||
|
||||
添加前在 Main 先去掉同一草稿已经引用的来源图片,再计算全批数量和请求容量;重复调用
|
||||
沿用已有附件 ID。草稿操作串行提交并用数据库唯一性约束保证幂等,不以禁用按钮作为唯一
|
||||
保证。文件复制到本次暂存后移入新附件目录,再在事务内登记文件与草稿归属;失败删除
|
||||
本批新文件,不删除已有副本。重启只核对无数据库引用的暂存或孤立新目录,已登记草稿
|
||||
资源继续保留,不增加写前日志或恢复引擎。
|
||||
|
||||
当前限制来源是 `src/shared/contracts.ts` 的 `contextIds.max(8)`、导入数组上限及
|
||||
`maximumPastedImageBytes`,Main 的 `ContextManager` 另有 8/16/12 MiB 常量;实现时将
|
||||
这些同义值集中到共享附件契约供现有导入、选图、草稿与发送复用,保持已发布数值。源文件
|
||||
限制仍区分图片与文档,不能套用生成成果 5 MiB 编码限制或发明“文档最多选几张”。
|
||||
Provider 响应和解码资源的有界读取属于解析限制,不等同发送附件数量。超过任何限制均给
|
||||
具体原因;选图整批拒绝,不复用 App 当前对新增文件 `slice` 接受剩余位置的行为。
|
||||
|
||||
`ContextManager` 对持久化图片生成既有有界请求副本,原图保持不变;提取文字模式只构造
|
||||
text context。解析父文档只拼入一次全文,图片副本仅携带简短来源及图像。草稿、消息和
|
||||
队列保存附件资源引用与发送方式,不能仅序列化会过期的内存 contextId。执行从这些资源
|
||||
恢复 context 并重新检查真实模型/Runtime 能力,完整校验后才接受发送或入队。
|
||||
仅图片父文档计入消息附件数并保留原件引用,但不创建空文本 context;发送前核对同批已有
|
||||
来自该结果的选图或提取文字,提示及恢复操作遵循 L-I5。
|
||||
|
||||
队列开始执行及重启恢复再次检查实际能力;失配沿用可恢复队列错误,保持资源及内容,
|
||||
提供恢复到草稿修正。恢复时先提交草稿引用再删队列引用,失败保留队列;用户修改提取文字
|
||||
方式再发送产生新的队列输入。取消生成保留已发送附件,删除队列只释放该项引用。分支
|
||||
会话复制已发送附件时也必须建立有效归属引用或独立副本,不能只复制原目录字符串;不改变
|
||||
既有分支产品范围。会话和项目删除使用同一按有效引用清理路径。
|
||||
|
||||
图片能力检查必须落在 Main/Runtime 请求边界,并在 UI 展示同一结果。Desktop 直连模型、
|
||||
本地 Runtime 与托管 SSH 的图片消费须分别核对;仅工具持有引用不算模型收到图。HTTP
|
||||
OCR 位于 Desktop 不自动证明选图发送不涉及 Agent;若实现改动远端附件协议或消费路径,
|
||||
必须按根 AGENTS 的共享 Linux Host 规则实测,记录具体 Runtime/模型组合及模型收到的输入。
|
||||
|
||||
## 预览实现
|
||||
|
||||
共享预览组件只接收受管结果、目标草稿状态与动作回调。设置、聊天 Modal 和知识库详情
|
||||
负责外壳,沿用 [UI 容器映射](./ui-design.md#1-容器与共享组件)。PageTabs 的 idPrefix
|
||||
按实例区分,详情滚动、页码定位和勾选状态在同一预览内保留,切换对象时清除旧对象选择。
|
||||
|
||||
现有 `MarkdownRenderer` 使用 `skipHtml`,`renderHtml` 只处理完整 HTML 或围栏,不能
|
||||
直接显示混合 Markdown 中的 OCR `<img>`、`<table>`。需为解析预览增加受限内容适配:
|
||||
用结构化 HTML 解析将已支持表格元素及受管图片节点映射到既有排版组件,移除脚本、事件
|
||||
属性和外部资源,不开启全局任意 HTML 渲染。Markdown 与 HTML 图片统一通过清单解析
|
||||
imageId,缺失引用换成诊断文字;HTML 表格保留行列及合并关系。页码链接使用结果的
|
||||
sourcePages,不从文件名猜测。继续沿用应用既有静态内容安全边界,不引入新的文档授权流程。
|
||||
|
||||
图片按可见内容加载,缩略图与原字节分开;组件关闭释放本次预览 URL。结果读取失败只
|
||||
替换相应面板并提供重试,不清掉其他正文或草稿。测试任务目录在关闭结果后清理;未关闭
|
||||
预览期间不能因为 HTTP 任务已完成就删除图片。测试重跑成功后才替换当前预览,失败
|
||||
保留上次结果并标明该结果属于上次测试。测试不建立草稿、会话或知识库引用。
|
||||
|
||||
取消贯穿渲染、Provider、重组和保存,在资源提交前检查取消;迟到响应不写入当前对象。
|
||||
保存事务已成功后的取消按完成结算,不把已提交结果删除后报告“取消成功”。退出后未完成
|
||||
任务只标中断并允许手动重试;数据库引用是清理依据,任务目录名不是删除许可。
|
||||
|
||||
## 实施与验证顺序
|
||||
|
||||
以下是同一完整功能的依赖顺序,不定义缩减版交付。验收范围以
|
||||
[完整矩阵](./ui-design.md#8-完整验收矩阵)为准。
|
||||
|
||||
1. 按已完成的 UI 设计实现来源设置、Main HTTP Provider 和可选 confidence,验证无本地模型的真实解析入口、保存前禁止测试及凭据保留。
|
||||
2. 补文件结果契约、纯图片与文档分目录、聊天与知识库归属、发送入队及删除;验证原图保留、重启重开、草稿引用保护、外部原文件移除、队列恢复、同名图片键、reference 来源删除、重解析失败和测试清理。
|
||||
3. 接入类型化高级选项及逐页 PDF/PPTX 路由;以真实开关样例验证高级模型能力。
|
||||
4. 补能触发合并的连续表格样例与来源映射,再接入可选跨页重组。不能以本次两组输出相同的请求作为该步骤完成依据。
|
||||
5. 按 L-I1 至 L-I5 接入有效图片能力、不支持提示、显式提取文字及选图入草稿和发送;覆盖成功后的文本模式、父文档正文去重、独立移除、目标选择/新建、添加幂等、共享限额、来源删除后副本持久和本地/远端 Runtime 消费。
|
||||
6. 从设置、聊天和知识库真实入口验收共享预览、完整/部分/仅图片状态、取消与重启、队列修正、资产清理;验证键盘、两主题、窄容器和原生浏览器遮挡。只有这些产品路径均完成,才可将对应需求记为已实现。
|
||||
|
||||
产品验收覆盖聊天、知识库和测试解析、HTTP 错误、缺图、取消和重开。已运行的层次、
|
||||
全仓测试及尚未覆盖的场景见进度。原独立探测脚本及历史记录保留,不与本轮调用数混算。
|
||||
@@ -0,0 +1,116 @@
|
||||
# 文档处理生产实现
|
||||
|
||||
2026-09-19。本文描述当前源码,验收证据单独见 [progress.md](./progress.md#2026-09-19-实现与验证)。
|
||||
需求 ID 沿用 [PRD](./prd.md),界面原则沿用 [UI 设计](./ui-design.md)。
|
||||
|
||||
## 解析与设置
|
||||
|
||||
`DocumentParsingService` 统一服务聊天文档、知识库、成果导入和设置测试。`HttpDocumentOcr`
|
||||
在 Main 调用 PaddleOCR-VL,`render-ocr-pdf.ts` 使用 PDF.js 和本机 Canvas 渲染选中页,
|
||||
不需要本地 OCR 权重。来源未配置时继续原有本地行为;产品没有测试地址默认值。
|
||||
|
||||
设置文件版本为 4,读取版本 1、2、3 时保留原有设置。认证支持无需认证和 Bearer API Key,
|
||||
复用 Electron safeStorage,公开快照只显示是否配置。空密钥保持现值,显式清除才删除;
|
||||
任务同时固定设置与凭据读取对象,中途保存不会更换在飞请求的地址或密钥。
|
||||
|
||||
快速文本/索引不上传。自动 PDF 只渲染需要 OCR 的页,高保真可直接发送全 PDF。
|
||||
跨页整理开启时,连续待 OCR 页组成一个 PDF 请求;不连续页分组,避免把不相邻页面合表。
|
||||
PPTX 仍按原生文本和直接引用的内嵌图片处理,定位保留幻灯片号与图片序号。
|
||||
普通聊天图片直传;“提取图片文字”独立调用已保存来源,忽略快速工作流的自动跳过设置。
|
||||
|
||||
HTTP 请求固定返回 Markdown 图片、关闭调试可视化。高级布尔值保留省略、true、false;
|
||||
过滤分别保存服务默认、全部保留、自定义及其排除集合。自定义隐藏后不清空未知标签。
|
||||
页面处理、图表/印章/图片块识别、Markdown 格式、跨页整理和类型化版面/生成参数可配置。
|
||||
部署模型能力需以用户样例确认,连接检查只执行 health 和 OpenAPI。
|
||||
|
||||
响应流上限 32 MiB,单张解码图上限 12 MiB、4000 万像素,文档文字上限 500 万字符。
|
||||
请求超时独立于本地单页超时;HTTP 状态和服务错误码保留,凭据和响应正文不出现在错误中。
|
||||
不下载服务返回的图片 URL,不自动重试或切换 Provider。
|
||||
|
||||
图片按来源页和页内键解码,以 UUID 替换 Markdown 与 HTML img 引用。同名键跨页不会覆盖。
|
||||
跨页整理的变化输出作为一个带完整 `sourcePages` 的分组正文;图片按实际返回字节匹配原页。
|
||||
无法唯一匹配时保留原逐页结果并警告。严格索引/高保真遇缺图或重组失败拒绝替换。
|
||||
服务无置信度时字段缺省,详情显示“未提供”。
|
||||
|
||||
## 文件与引用
|
||||
|
||||
```text
|
||||
<userData>/
|
||||
conversation-attachments.sqlite
|
||||
conversation-assets/<conversationId>/
|
||||
images/<resourceId>/original.<ext>
|
||||
documents/<resourceId>/original.<ext>
|
||||
documents/<resourceId>/parsed.md
|
||||
documents/<resourceId>/manifest.json
|
||||
documents/<resourceId>/images/<imageId>
|
||||
knowledge/<libraryId>/<sourceId>/<managed-original>
|
||||
knowledge-assets/<libraryId>/<documentId>/<resultId>/
|
||||
parsed.md
|
||||
manifest.json
|
||||
images/<imageId>
|
||||
temp/document-parsing/<resourceId>/
|
||||
```
|
||||
|
||||
会话资产的 `request.json` 保存已转换的请求内容,用于恢复内存 context;原件字节单独保存,
|
||||
缩放和 JPEG 请求副本不覆盖原图。提取图片文件使用 UUID 无扩展名,MIME 在清单记录。
|
||||
图片显式 OCR 后生成解析文件。切回原图保留已有结果,并重新进入图片能力校验。
|
||||
HTTP OCR、显式图片提取及结果完整程度共用 `hasExtractedDocumentText`;图片引用与生成的
|
||||
缺图警告只作预览/诊断,不算正文。无正文的显式提取失败并保留原草稿;有有效插图的文档
|
||||
结果仍可保存为仅图片,不因缺图警告改判为可发送文本。
|
||||
|
||||
`ConversationAttachmentStorage` 的专用 SQLite schema 1 保存描述、路径与
|
||||
`draft/message/queue/parsing` 引用,不在这些描述字段内存入原图 Base64。纯图片无需解析清单。
|
||||
Main 先保存资产,再保存草稿或接受队列;消息、分支建立独立引用。无有效引用才回收文件。
|
||||
会话删除、项目删除及清空数据均核对引用,启动回收已无归属的记录和孤立 UUID 目录。
|
||||
清理失败保留无引用数据库记录供下次重试,不回滚已接受的草稿。
|
||||
|
||||
助手库 schema 39 标记新附件元数据,阻止旧客户端将其当旧格式读取;没有批量改写历史附件。
|
||||
知识库 schema 13 新增 `metadata.parsedResultId` 表达式索引,结果按主键定位,不扫描全部文档。
|
||||
知识库新结果先写入独立 resultId 目录,数据库正文/chunks 发布成功后替换引用,再清理旧目录。
|
||||
原件继续遵循 managed/reference 原有布局,reference 派生文件不写入外部目录。
|
||||
旧正文重解析为空索引时保留旧结果;首次仅图片结果可预览,但显示无可索引文字。
|
||||
|
||||
会话图片从文档结果复制为独立原图文件,保存文档名、页码、resultId、imageId。
|
||||
源文档重解析或删除不影响会话副本。添加批次串行,重复来源在同一草稿内返回已有项;
|
||||
提交前重新读取目标草稿,数量/容量不符整批回滚。限额集中在 `attachment-limits.ts`:
|
||||
每消息 8 项、内存 context 16 项和 12 MiB;原文件上限仍为图片 12 MiB、文档 20 MiB。
|
||||
|
||||
重解析与发送方式改变采用新文件资源替换草稿位置,旧消息/队列保持原 resourceId。
|
||||
`attachmentId` 保留同一草稿附件身份,context 的 `id/resourceId` 随新产物更换,原始名称和来源保持。
|
||||
图片 OCR 后再次切回原图会保留原 OCR 结果的副本,用户无需重跑 OCR 才能查看。
|
||||
|
||||
## 任务与队列
|
||||
|
||||
文件导入提供 operationId、读取/解析/保存阶段和取消。HTTP AbortSignal 贯穿页渲染、识别、
|
||||
重组与提交检查。首次失败回滚本批附件,已保存原件用独立解析失败记录供重试,不作为可发送
|
||||
草稿项。用户取消清理本批暂存;退出或进程中断后,启动将未完成记录标为中断,不自动请求 OCR。
|
||||
旧结果重解析失败时保留旧草稿或知识索引。
|
||||
|
||||
队列保存接受时的附件和发送方式。入队、分发、实际执行分别检查图片能力。
|
||||
分发失败保留队列及可恢复错误,队列预览只读;“恢复到草稿”先合并并保存附件引用,
|
||||
再移除队列项,文字用空行追加,超限保持原队列。Renderer 请求恢复时不传入旧草稿文本,
|
||||
收到队列正文后通过原会话的函数式状态更新追加到最新草稿,保留等待期间的编辑和清空。
|
||||
文件附件的启动恢复不再把提前保存的用户消息误认为 Runtime 已接受并删除队列。
|
||||
|
||||
## 界面与边界
|
||||
|
||||
设置页头的测试菜单选择聊天或知识库场景,未保存禁止测试/连接检查。测试中锁定设置退出,
|
||||
可取消。诊断关闭释放测试资源,没有添加到会话入口。
|
||||
|
||||
`DocumentResultPreview` 共用于设置测试、聊天 Modal、知识文档详情。正文支持 Markdown、
|
||||
受限 HTML 表格和已登记图片;脚本、事件属性、外部资源不渲染。图片按可见区域加载 640px
|
||||
缩略图,点击大图才读原图。正文/图片定位与 PageTabs 使用结果页码;详情展示保存时配置。
|
||||
知识库返回列表保留查询和位置。历史附件可复制到当前草稿重新解析,不修改原消息。
|
||||
|
||||
附件名称、解析状态及大小与操作行分开排列;查看结果和更多操作复用 32px 图标按钮,
|
||||
提供悬停与键盘焦点提示。草稿、历史和队列共用相同操作组件,菜单及 Modal 的焦点恢复
|
||||
继续由共享机制处理。具体布局见 [附件操作菜单](./ui-design.md#12-附件操作菜单)。
|
||||
|
||||
目标会话选择显示项目与名称;新建沿用 App 的项目限制和空会话复用,保持预览。
|
||||
选择、已在草稿、移除、添加、前往会话分别处理。当前没有可编辑目标时需先选择或创建。
|
||||
草稿图片可提示模型/Runtime 能力问题,但允许保留;Main 才是发送与入队校验边界。
|
||||
本机推理监控的模型入口直达 `model`,OCR 行直达 `document-parsing`,HTTP 服务不加入本机服务列表。
|
||||
|
||||
OCR 和资产管理运行于 Desktop。直连模型及托管 OpenCode 已验证实际图片输入;Agent 未增加 OCR
|
||||
解析器或新附件传输协议。当前桌面消费已有图片协议;Continue、DeepSeek Harness 的本次真实视觉
|
||||
调用未执行,不能由设置声明推断这些组合已经过本轮验收。
|
||||
@@ -0,0 +1,175 @@
|
||||
# HTTP OCR 逻辑设计
|
||||
|
||||
需求来源为 [PRD](./prd.md#13-http-paddleocr-vl-扩展规划),场景验收见
|
||||
[User Stories](./user-stories.md)。当前生产机制及与设计的差异见[实现说明](./implementation.md),
|
||||
逐项验证见[进度](./progress.md#2026-09-19-实现与验证)。以下规则保留需求追溯职责。
|
||||
|
||||
## 输入路由
|
||||
|
||||
L-I1(FR-I1、FR-I2、FR-I4;US-I1、US-I2、US-I3、US-I6):先区分用户操作与输入类型,
|
||||
再判断实际生效的模型及 Runtime 能力。显式提取图片文字使用已保存 OCR 配置,优先于普通
|
||||
图片直传分支,也不受文档快速工作流的隐式跳过规则影响。
|
||||
|
||||
| 输入或操作 | 处理 |
|
||||
| --- | --- |
|
||||
| 普通聊天图片,实际支持图片输入 | 作为图片请求发送,不调用 OCR |
|
||||
| 普通聊天图片,实际不支持或能力未确认 | 明确提示提取文字或切换视觉模型,不静默 OCR,不声称已发送图片 |
|
||||
| 明确提取图片文字 | 调用配置的 OCR;模型视觉能力不影响此操作 |
|
||||
| PDF、Office | 沿用文档原生解析与按需 OCR 工作流 |
|
||||
| 知识库图片文字提取及索引 | 由知识库工作流决定,不读取聊天模型视觉开关作为路由依据 |
|
||||
|
||||
L-I2(FR-I3、FR-I5;US-I1、US-I4、US-I7):原图与请求副本分开,缩放、转码不覆盖原图。
|
||||
纯图片无解析正文及清单前置条件;显式 OCR 产物关联回原附件。文档图片保存与模型请求选择
|
||||
分别处理,只有明确选入请求且满足实际能力、数量和容量限制的图片才进入模型输入。
|
||||
|
||||
L-I3(FR-I2、FR-I3;US-I3、US-I11):附件身份、原始名称与来源不因 OCR 改变,发送方式
|
||||
单独记录为图片或提取文字。只有 OCR 返回非空可靠文字且结果保存成功,才切到提取文字;
|
||||
显示名派生为“原文件名 · 提取文字”。提取文字模式只拼入文本,不同时传原图或把解析中
|
||||
提取的其他图片自动附加。失败、取消、空文字保持原方式;用户可显式切回图片并重新校验
|
||||
能力。图片引用及解析器生成的缺图警告不算提取文字,即使同时保存了其他有效图片也不例外。
|
||||
重试采用当前已保存 OCR 设置,开始时说明来源;旧结果在新结果成功前保持可用。
|
||||
|
||||
## 选图与发送
|
||||
|
||||
L-I4(FR-I5;US-I7、US-I8、US-I9):预览待添加选择是短期 UI 状态,目标草稿的附件引用
|
||||
才是“已附加”的依据。进入预览默认零选择,切页签保留选择,关闭清空未添加选择。已附加
|
||||
项不能靠取消勾选移除,必须明确执行移除操作。测试预览不提供添加入口。
|
||||
|
||||
添加提交包含明确的目标会话及来源结果 imageId 集合,提交时冻结目标,不根据异步完成时
|
||||
活动会话改归属。Main 确认目标可编辑、源资源仍存在、数量与容量可接受后复制到会话所有
|
||||
的稳定文件资产,再保存草稿引用;任一复制或保存失败回滚本批新副本,保留原选择与草稿。
|
||||
无有效目标时必须先选择或新建会话,不隐式创建或采用第一个会话。活动回复不阻止下一条
|
||||
草稿编辑,但仍遵循已有 Runtime/模型选择锁定规则。
|
||||
|
||||
以“目标草稿 + 来源结果 ID + imageId”判断重复添加;来源结果 ID 只标识实际解析产物,
|
||||
不引入结果版本历史。同一草稿存在该图片引用时返回已有附件,不重复占用容量或位置。
|
||||
移除后可再次添加;发送后新草稿可再次添加。重解析生成新结果和图片 ID,不自动替换
|
||||
已附加副本。父文档与副本没有级联删除关系,来源关联用于说明及跳转,不作为副本存活条件。
|
||||
|
||||
L-I5(FR-I1、FR-I5、FR-H9;US-I2、US-I8、US-I10):选图和发送沿用 PRD 第 3.2.1 节
|
||||
现有附件限制。父文档和每张图片分别占一个附件位置;现有基线为每消息 8 个附件、Main
|
||||
16 个上下文项目及 12 MiB 暂存内容上限。实现从同一共享契约读取这些值,不在预览、IPC
|
||||
或 Provider 中定义独立的“选图上限”。图片副本使用既有图片请求转码路径,原始资产大小
|
||||
与请求副本大小分开计算,不能把尚未选中的全部插图计入本次发送容量。
|
||||
|
||||
选图批次先去掉已附加项再整批校验;超限全部拒绝并保留选择,不沿用文件粘贴入口的截取
|
||||
剩余位置行为。发送前重新读取完整草稿并校验,不依赖添加时的剩余位置。文本父附件只
|
||||
拼入一次,不为每个图片副本重复加入父文档正文;模型正文仅附简短来源信息及图片输入。
|
||||
仅图片父文档仍作为来源附件保留并计入附件数量,但不生成空 text context;同批至少有一张
|
||||
从该结果选出的图片(或其显式 OCR 文字)才可携带它发送,否则提示选择图片或移除该文档。
|
||||
|
||||
能力判定取本次实际模型与实际 Runtime 消费路径的交集。添加时显示能力问题但允许保存
|
||||
待修复草稿,发送和入队接受前必须通过校验;未知能力不能当作支持。切换模型/Runtime、
|
||||
恢复队列及真正开始执行时重新校验。队列能力失配转为可恢复失败并保留资源,用户可恢复
|
||||
到草稿修正或切换到支持的配置后重试;队列内容不会静默转文字或丢图。不以工具拿到图片
|
||||
引用代替聊天模型收到图片的证据,也不因模型支持视觉而隐式调用 OCR。
|
||||
|
||||
## 来源与选项
|
||||
|
||||
L-H1(FR-H1、FR-H2;US-H1、US-H2、US-H8):每次解析开始时读取并冻结已保存的来源、
|
||||
地址和选项。选择 HTTP 后只检查 HTTP 配置;本地模型状态不再是 HTTP 的前置条件。
|
||||
HTTP 失败不触发本地下载或切换。快速工作流优先于 OCR 设置,仍不运行 OCR。
|
||||
|
||||
L-H5(FR-H11、FR-H12;US-H12):来源切换保留各自配置,测试要求先保存;解析结果记录
|
||||
本次 Provider 与生效选项,不含凭据。查看已存结果不触发解析,修改配置不改写旧结果。
|
||||
|
||||
配置为设备级共享值;项目、会话和知识库范围决定结果归属,不创建独立 OCR 配置。应用中心
|
||||
的本机推理监控启用、常驻与窗口开关不决定 OCR 来源或可用性。HTTP 来源不依赖本地监控
|
||||
入口和模型加载状态。菜单及设置直达返回规则见 [入口与职责](./ui-design.md#11-入口与职责)
|
||||
(FR-H1、FR-H12、FR-I5;US-H16)。
|
||||
|
||||
L-H2(FR-H4;US-H4、US-H9):高级布尔项使用“服务默认 / 开启 / 关闭”语义。
|
||||
服务默认省略字段,不将未知默认值写成 `false`。`markdownIgnoreLabels` 使用三种状态:
|
||||
省略表示服务默认,空数组表示不排除标签,非空数组表示明确排除的标签集合。保存设置
|
||||
不得把空数组归一为缺省。任务内选项不随用户中途修改设置变化。
|
||||
|
||||
自定义过滤的正向“保留”开关与排除集合取反映射。首次进入自定义且无旧值时从全部保留
|
||||
开始,明确显示此初始值;不猜测服务默认。已有自定义排除列表在切换模式后保留,未知或
|
||||
不常用标签也必须呈现并保留,不能保存时丢弃。默认保存结果引用的图片;过滤使图片不再
|
||||
被正文引用时才可不提交对应图片。过滤设置不控制模型自动选图。
|
||||
|
||||
## 页面与图片
|
||||
|
||||
L-H3(FR-H2、FR-H5、FR-H7;US-H2、US-H5、US-H8):页面定位来自输入任务的原始页号。
|
||||
单页图片请求可确定其 PDF 页号或 PPTX 幻灯片号;整份 PDF 请求在核对返回数量和顺序后
|
||||
映射到原始页号,不依赖此次部署缺失的 `page_index`。不连续 OCR 页不能直接跨页合表。
|
||||
重组输出保留 `sourcePages` 集合,无法恢复对应关系时报告失败,不能猜测唯一页号。
|
||||
|
||||
L-H4(FR-H3;US-H3、US-H7):以“文档 + 原始页 + 页内图片键”关联图片。只处理有界、
|
||||
可解码的内联图片,不下载服务返回 URL。Markdown 中的 `![]()` 和 HTML `img src` 均须
|
||||
映射到保存后的受管资源;表格 HTML 需要既有安全渲染处理。模型正文与向量索引不混入
|
||||
Base64。筛除页眉图等内容时,对应图片只有仍被结果引用才需要提交。
|
||||
|
||||
## 结果与失败
|
||||
|
||||
| 条件 | 处理规则 | 关联 |
|
||||
| --- | --- | --- |
|
||||
| 原生文本可靠且未要求 OCR | 直接使用原生结果,不探测 HTTP | FR-H2 / US-H2 |
|
||||
| HTTP 与图片保存完成 | 提交逐页内容、图片引用和诊断;置信度缺失就省略 | FR-H3、FR-H7 / US-H1、US-H3 |
|
||||
| 自动聊天 OCR 失败且有可靠原生文字 | 沿用既有原生文字回退,并标明未完成 OCR 页 | FR-H6 / US-H6 |
|
||||
| 自动聊天已取得文字但部分图片失败 | 保留文字和具体缺图警告;缺图引用改成文字说明 | FR-H3、FR-H6 / US-H7 |
|
||||
| 自动聊天跨页重组失败,但逐页结果可用 | 保留原逐页正文、图片及页码,显示重组失败和部分结果;不使用映射不明的重组内容 | FR-H5、FR-H6 / US-H5、US-H6 |
|
||||
| 知识库完整索引或高保真缺页、缺图、重组失败 | 本次失败,不替换已有成功结果;允许重试 | FR-H3、FR-H5、FR-H6 / US-H3、US-H5、US-H7 |
|
||||
| 用户取消解析或应用关闭 | 中止客户端请求,不启动后续任务,仅清理本次无有效引用的临时资源;草稿、已发送、已入队引用的资源保留 | FR-H6、FR-H9 / US-H6、US-H10、US-I5 |
|
||||
| 无文字但有已保存的有效插图 | 结果为仅图片,可预览并按 L-I4 选择;聊天父文档无可发送文本,知识库不生成空 chunk,不标为文本索引成功 | FR-H3、FR-H7、FR-I5 / US-H3、US-I7 |
|
||||
| 无文字且无有效图片 | 失败,保留原件供重试,不能标为空成功 | FR-H6 / US-H15 |
|
||||
|
||||
请求状态按“准备、请求、校验、保存、完成”推进,提交前可进入失败或取消;资源及引用已
|
||||
提交后按完成结算,迟到取消不删除结果。
|
||||
HTTP 中止不保证服务端 GPU 推理立刻停止;本部署没有探测服务端取消接口。
|
||||
|
||||
L-H8(FR-H6、FR-H9、FR-H12;US-H6、US-H14、US-H15):任务运行状态与结果完整程度
|
||||
分开。已保存结果可为完整、部分或仅图片,当前任务另有运行、失败、取消或中断状态。
|
||||
打开旧结果时可以同时显示本次重解析失败,不能用单一 status 覆盖旧结果。
|
||||
自动聊天允许的可靠文本回退必须将缺页/缺图范围和警告随文本传给模型;知识库完整索引
|
||||
及高保真严格失败。快速索引只按所选原生策略完成,不因用户明确禁用 OCR 而假称漏页
|
||||
已经识别。仅图片的知识库结果可保存供预览和选图,但显示“无可索引文字”,不算索引成功。
|
||||
已有可检索结果重解析后变成仅图片时,不以空索引替换旧结果,本次报告无可索引文字并保留
|
||||
旧结果;首次导入没有旧结果时可保留图片资产和该状态。
|
||||
|
||||
首次批次导入失败或取消沿用批次回滚,失败摘要保留可重试信息但不是草稿附件。重解析
|
||||
失败不删除已有附件。任务取消通过同一 operationId/AbortSignal 传播,取消后的迟到响应
|
||||
不提交结果;应用退出停止后续请求,重启将未完成任务显示为中断,不恢复 HTTP 调用。
|
||||
有受管原件则手动重试;原件尚未保存则重新选择。重启仅根据数据库现有引用清理无主暂存,
|
||||
无需记录逐页持久运行日志。
|
||||
|
||||
## 资源归属
|
||||
|
||||
L-H6(FR-H8、FR-H9、FR-I3;US-H10、US-H11、US-I4、US-I5):草稿资源暂存于应用目录;
|
||||
发送被接受及队列记录持久化前,文档完成原文件、解析正文、图片的持久化及会话关联,纯图片
|
||||
完成原图持久化及关联。失败保留草稿供重试,不标为已发送。
|
||||
恢复队列读取持久化附件引用;取消生成不删除已提交资源。移除草稿附件、取消解析或批次失败
|
||||
只清理本次无有效引用的资源。启动、退出或定时清理不得一律删除临时目录;可恢复草稿引用
|
||||
的文件须继续可用。删除会话只释放其归属和引用,无其他有效引用时才删除文件。
|
||||
|
||||
L-H7(FR-H10、FR-H11;US-H11、US-H13):知识库文档独立持有正文、chunks 和图片关系,
|
||||
两种来源模式的派生资源均归应用管理。删除文档或来源时按有效引用清理;外部原文件不属于
|
||||
派生资源清理范围。聊天和知识库重解析均先完成新结果及必要资源保存,再替换引用;失败或
|
||||
取消清理新暂存,旧结果保持可用。旧资源仅在替换成功且无引用时回收。
|
||||
|
||||
已发送消息及已接受队列的正文、选图、发送方式和资源引用冻结。历史消息“再次解析”产生
|
||||
新草稿附件,不修改消息;草稿重解析成功仅更新草稿引用,其他有效引用保持原结果。修改队列
|
||||
附件须先恢复整条输入到草稿,再重解析或改变发送方式,不额外复制一条仍待发送的输入。队列
|
||||
恢复到草稿时先保存草稿引用再移除队列引用,失败保持队列。队列删除只释放该项引用;
|
||||
取消正在生成的回复保留已发送输入。会话删除和项目删除均使用同一引用清理规则。
|
||||
目标已有草稿时,将恢复文字以空行追加、附件按来源去重合并,提交前校验合并后的数量与
|
||||
容量;超限则保留队列及原草稿,提示先发送或整理草稿,不覆盖已有输入。
|
||||
恢复等待期间允许继续编辑;文字追加到恢复完成时该会话的最新草稿,不覆盖期间的输入,
|
||||
也不复活已清空的旧文字。切换会话不会改变原恢复目标。
|
||||
|
||||
L-H9(FR-I5、FR-H9、FR-H10;US-I8、US-I9):从聊天或知识库选出的图片在添加成功时
|
||||
已是会话独立副本,其来源名称、定位和源 ID 随附件保存。来源删除或重解析不删除副本,
|
||||
来源跳转失效不妨碍打开原图和发送。移除父文档只解除父文档引用,移除副本只解除该图片
|
||||
引用。草稿、消息、队列、分支等已有有效引用共同决定是否清理文件;不为跨库引用建立
|
||||
全局去重或资源保活服务。知识库与聊天原资产各自清理,复制时失败只回滚本批新资源。
|
||||
|
||||
## 完整性检查
|
||||
|
||||
来源选择、缺省值、正向过滤、混合页、图片键、完整/部分/仅图片结果、失败取消、原图保存、
|
||||
显式 OCR 发送方式、选图幂等、目标选择、共享限额、发送入队、重解析及清理均已有确定规则。
|
||||
组件和布局见 [UI 设计](./ui-design.md),逐项验收见其完整矩阵;这些范围没有留待后续产品
|
||||
决策的选图缺口。
|
||||
|
||||
实际直连和托管 OpenCode 图片消费、扫描 PDF 与跨页合表示例已取得真实证据,见进度。
|
||||
其他 Runtime、本地 OCR 各平台和部署高级模型的本轮验证范围仍需分别核对。
|
||||
模型声明与接口成功不替代效果测试;跨页整理默认关闭,失败明确报告。
|
||||
@@ -22,7 +22,7 @@ GoodBuddy 需要用同一条可信文档解析链路服务以下场景:
|
||||
- XLSX 工作表 XML 与共享字符串;
|
||||
- PPTX 幻灯片文字。
|
||||
|
||||
聊天附件、知识库和测试解析通过统一的 `DocumentParsingService` 使用已保存的工作流。
|
||||
聊天文档附件、知识库和测试解析通过统一的 `DocumentParsingService` 使用已保存的工作流。
|
||||
扫描 PDF 和 PPTX 幻灯片内嵌图片可使用已安装、已校验的本地 OCR 模型识别;
|
||||
PPTX 的当前能力和边界见 [PPTX 图片流程](#75-pptx-图片流程)。
|
||||
|
||||
@@ -31,7 +31,11 @@ PPTX 的当前能力和边界见 [PPTX 图片流程](#75-pptx-图片流程)。
|
||||
- DOC、XLS、PPT 等旧版二进制 Office 格式不支持;
|
||||
- Office 解析主要提取文字,不能完整保留表格、公式、图表和版面;
|
||||
- DOCX、XLSX 内嵌图片尚未接入 OCR;
|
||||
- Office 格式转换和远程 OCR 服务尚未接入。
|
||||
- Office 格式转换尚未接入;HTTP PaddleOCR-VL 已接入,部署高级模型能力需要使用文件测试。
|
||||
|
||||
聊天文档原件、解析正文和提取图片现保存为会话文件资产,数据库记录草稿、消息和队列引用。
|
||||
已有历史附件若未保存原件,不会被升级自动补齐。第 13 节保持需求定义,当前实现与证据分别见
|
||||
[生产实现](./implementation.md)和[进度](./progress.md#2026-09-19-实现与验证)。
|
||||
|
||||
## 3. 产品原则
|
||||
|
||||
@@ -48,7 +52,7 @@ PDF 不是所有文档唯一的中间格式。解析应同时保留:
|
||||
|
||||
| 场景 | 默认预设 | 行为 |
|
||||
| --- | --- | --- |
|
||||
| 聊天附件 | 自动解析 | 优先快速提取,文本不足时按需 OCR,完整解析文本加入当前请求 |
|
||||
| 聊天文档附件(PDF、Office 等) | 自动解析 | 优先快速提取,文本不足时按需 OCR,完整解析文本加入当前请求 |
|
||||
| 知识库导入 | 完整索引 | 完整解析、按页或工作表定位、按需 OCR、分块与索引 |
|
||||
| 扫描文档 | OCR | 页面渲染、文字识别、置信度与定位保留 |
|
||||
| 表格分析 | 语义优先 | 单元格和值优先,PDF 或图片补充图表与打印布局 |
|
||||
@@ -82,6 +86,20 @@ PDF、DOCX、PPTX 和 XLSX 的解析结果保留章节定位及全文,不在
|
||||
错误显示在输入区。剪贴板中的虚拟文档若没有本地路径,提示先保存到本地后添加;
|
||||
无本地路径的截图继续使用图片粘贴入口。
|
||||
|
||||
#### 3.2.2 纯图片与文档的路由(规划)
|
||||
|
||||
普通聊天图片默认作为图片输入发送,跳过文档解析与 OCR;是否可发送取决于本次实际生效的
|
||||
模型和 Runtime 图片输入能力。下表定义提示、显式 OCR 操作和原图持久化的验收要求,
|
||||
各 Runtime 的真实验证范围见进度文档。
|
||||
|
||||
| ID | 产品要求与验收 |
|
||||
| --- | --- |
|
||||
| FR-I1 | 普通图片附件默认直接送入支持图片输入的模型,不自动 OCR。当前模型或 Runtime 不支持时明确提示,用户可选择“提取图片文字”或切换支持图片输入的模型;不得静默转成 OCR 文本并声称已发送原图。 |
|
||||
| FR-I2 | 用户明确选择“提取图片文字”时,使用文档解析中已保存的 OCR 来源与选项,即使聊天模型支持图片输入也执行 OCR。成功后附件显示“原文件名 · 提取文字”和“仅发送提取文字,原图保留”,本次仅发送文字;可显式改回发送原图并重新校验能力。失败、取消或未提取到文字保持原发送方式及草稿,不隐式切换 Provider。 |
|
||||
| FR-I3 | 纯图片附件长期保留原始图片及其会话、消息、附件归属,重启后可查看。请求可按实际格式或容量限制生成缩放、转码副本,但不得覆盖原图。纯图片不强制生成 parsed.md 或解析清单;显式 OCR 才产生关联的解析结果。持久化完成后才接受发送或保存队列引用,删除按有效引用清理,草稿引用的资源也须保留。 |
|
||||
| FR-I4 | PDF、Office 的原生解析、按需 OCR 与纯图片直传分开。知识库中的图片文字提取和索引按知识库工作流决定,不随聊天模型视觉能力改变;这不代表当前已支持独立图片导入或图片向量检索。 |
|
||||
| FR-I5 | 聊天与知识库解析预览提供图片多选,默认零选择;“添加所选图片”将图片复制为明确目标会话草稿的独立视觉附件,保留文档、页码及图片来源,不自动发送或附加全部图片。区分待添加勾选与已在草稿,重复添加同一来源图片幂等。父文档文本仅加入一次,父文档与已复制图片独立移除;知识库删除或重解析不影响会话副本。无有效会话时提供选择或新建会话流程。添加与发送共用既有附件数量、容量及有效模型/Runtime 校验,超限整批拒绝并保留选择,不静默丢图或抽样。 |
|
||||
|
||||
### 3.3 本地优先
|
||||
|
||||
- 文本层和本地 OCR 均在设备上处理;
|
||||
@@ -113,18 +131,20 @@ OCR 模型区沿用语音模型管理模式:
|
||||
- “打开模型仓库”直接显示在 OCR 模型卡片右上角,并使用当前下载源对应仓库;
|
||||
- 模型操作即时生效,解析策略仍通过分类页头的“保存设置”提交。
|
||||
|
||||
### 4.1 第一阶段字段
|
||||
### 4.1 设置字段
|
||||
|
||||
- 聊天附件预设:`auto`、`fast-text`、`high-fidelity`;
|
||||
- 知识库预设:`complete-index`、`fast-index`、`high-fidelity`;
|
||||
- PDF OCR 策略:`auto`、`always`、`disabled`;
|
||||
- OCR 来源:第一阶段固定为 `local`,远程服务入口禁用;
|
||||
- OCR 来源:`local` 或 HTTP PaddleOCR-VL,旧设置仍使用本地;
|
||||
- 本地 OCR 模型:`pp-ocrv6-tiny`、`pp-ocrv6-small`、`pp-ocrv6-medium`;
|
||||
- 单文档最大页数;
|
||||
- OCR 并发数;
|
||||
- 单页超时。
|
||||
|
||||
OCR 来源使用互斥选择。本地模型选中后才显示模型下拉列表、按需下载、导入和本地 OCR 参数;远程服务计划接入 MinerU、PaddleOCR-VL 等接口,第一阶段保持可读但禁用。来源选择本身就是用户的明确决策,不再显示额外的“隐私与云端处理”授权区。
|
||||
OCR 来源使用互斥选择。本地模型选中后才显示模型下拉列表、按需下载、导入和本地 OCR 参数;
|
||||
远程显示 PaddleOCR-VL 地址、认证及高级选项。两套配置分别保留,来源选择本身就是用户的
|
||||
明确决策,不再显示额外的“隐私与云端处理”授权区。MinerU 不在本次范围。
|
||||
|
||||
## 5. 架构
|
||||
|
||||
@@ -139,7 +159,7 @@ OCR 来源使用互斥选择。本地模型选中后才显示模型下拉列表
|
||||
└─ ParsedDocument merger
|
||||
```
|
||||
|
||||
`DocumentParsingService` 是唯一场景入口:
|
||||
`DocumentParsingService` 是文档解析的统一场景入口;普通聊天图片直传不经过此接口:
|
||||
|
||||
```ts
|
||||
type DocumentParsingPurpose = 'chat-attachment' | 'knowledge-index'
|
||||
@@ -158,7 +178,8 @@ type DocumentParsingService = {
|
||||
|
||||
## 6. 统一结果
|
||||
|
||||
第一阶段兼容现有 `ParsedDocument`,并逐步扩展:
|
||||
现有 `ParsedDocument` 文本结构如下;第 13 节要求补齐图片、解析配置与持久化引用,具体
|
||||
契约由 [HTTP 技术设计](./http-paddleocr-vl-technical-design.md#结果与资源)维护:
|
||||
|
||||
```ts
|
||||
type ParsedDocument = {
|
||||
@@ -363,29 +384,15 @@ DOC、XLS、PPT 通过 `DocumentConversionProvider` 转换:
|
||||
|
||||
`auto` 工作流可以从 OCR 回退到可靠的原生文本,但不能把空结果标记为成功。知识库导入失败时保留来源和可重试上下文。
|
||||
|
||||
## 11. 实施阶段
|
||||
## 11. 实施范围与顺序
|
||||
|
||||
### 阶段一
|
||||
本地 OCR 基线与已验证能力见第 2、7 节及聊天附件技术说明。第 8 节的旧 Office 转换和
|
||||
扩展语义解析保留为独立规划,不因 HTTP 接入而承诺已经实现。
|
||||
|
||||
- 新增文档解析设置分类和持久化契约;
|
||||
- 建立 `DocumentParsingService`,供聊天和知识库共用;
|
||||
- 将无文本 PDF 识别为可触发 OCR 的明确状态;
|
||||
- 接入 PP-OCRv6 Tiny、Small、Medium 的双来源下载、校验、ZIP 离线迁移、删除与
|
||||
WASM Worker;
|
||||
- 实现真实文件测试和六平台验证入口。
|
||||
|
||||
### 阶段二
|
||||
|
||||
- 增强 DOCX、XLSX、PPTX 语义结构;
|
||||
- 实现按页混合文本层与 OCR;
|
||||
- 增加版面、表格和阅读顺序。
|
||||
|
||||
### 阶段三
|
||||
|
||||
- 增加 LibreOffice 和 API 转换 Provider;
|
||||
- 支持 DOC、XLS、PPT;
|
||||
- 增加 MinerU、PaddleOCR-VL 等远程 OCR 服务连接配置;
|
||||
- 增加高保真工作流和解析结果预览。
|
||||
第 13 节及 FR-I1 至 FR-I5 是本次约定的完整功能范围:HTTP Provider、设置与高级选项、
|
||||
三入口预览、文档和原图持久化、显式图片 OCR、文档选图加入会话及发送、失败恢复与清理。
|
||||
实施依赖顺序见 [技术设计](./http-paddleocr-vl-technical-design.md#实施与验证顺序),不构成
|
||||
分批产品交付或推迟选图发送的许可。产品完成须覆盖 [完整验收矩阵](./ui-design.md#8-完整验收矩阵)。
|
||||
|
||||
## 12. 验收
|
||||
|
||||
@@ -405,3 +412,75 @@ DOC、XLS、PPT 通过 `DocumentConversionProvider` 转换:
|
||||
- 测试解析不会创建聊天消息或知识库文档;
|
||||
- 选择本地模型时没有任何文档上传;
|
||||
- 文档中的提示词不会改变系统、模式或工具权限。
|
||||
|
||||
## 13. HTTP PaddleOCR-VL 扩展(规划)
|
||||
|
||||
状态:已接入生产解析、设置、预览及资源流程。部署实测、验收层次与未覆盖项见
|
||||
[进度](./progress.md#2026-09-19-实现与验证),行为规则见 [逻辑设计](./logic-design.md)。
|
||||
|
||||
用户需要使用已有内网 OCR 服务解析扫描件,并保留插图、表格和来源页码;文档中的页眉、
|
||||
页脚有时包含合同编号或说明,不能只提供服务默认过滤结果。
|
||||
|
||||
| ID | 产品要求与验收 |
|
||||
| --- | --- |
|
||||
| FR-H1 | 在文档解析设置中明确选择本地 OCR 或 HTTP PaddleOCR-VL,保存服务地址和所需认证。HTTP 路径无需安装本地 OCR 模型;已有配置继续选本地来源。 |
|
||||
| FR-H2 | 聊天文档附件、知识库导入和测试解析使用同一来源及解析设置。PDF 原生文本优先、快速模式禁用 OCR、高保真模式全页 OCR 的既有规则继续适用。PPTX 仍按直接内嵌图片识别并保留幻灯片定位。纯图片路由及显式 OCR 遵循 FR-I1、FR-I2。 |
|
||||
| FR-H3 | 默认保存 HTTP 解析返回、被结果引用的插图字节及页码、Markdown 关联,重开后可查看;同名图片键仅在页内有效,跨页不得覆盖。支持 Markdown 图片及 HTML img、table 的预览。测试产物不进入聊天或知识库。图片缺失、超限或无法保存必须显示具体警告或失败,不能将带断图的结果表示为完整成功。 |
|
||||
| FR-H4 | 高级设置允许保留或排除页眉、页脚、页码、脚注等标签,也允许恢复服务默认值。配置方向分类、去扭曲、版面检测、图表、印章、图片块 OCR 和 Markdown 格式选项时,应展示部署是否仅声明接口、是否经过真实样例验证。 |
|
||||
| FR-H5 | 可选跨页表格合并与标题层级调整。启用后保留原始页面来源和图片引用;没有观察到合并效果时不得宣传为已支持跨页表格恢复。 |
|
||||
| FR-H6 | HTTP 错误、超时、取消和图片持久化失败可诊断、可重试。取消后不再发起后续页或重组请求,不自动切换服务或本地模型。无文字且无有效图片时失败;仅有图片时明确标为仅图片结果,可预览和选择图片但不能发送空文本附件。部分解析不能标成完整成功。 |
|
||||
| FR-H7 | 测试解析能核对文字、表格、图片数量、页码、过滤设置与耗时。服务没有置信度时显示“未提供”,不补成 1 或 0。 |
|
||||
| FR-H8 | 已发送的聊天文档附件须长期保存原文件、解析文本和提取图片;应用重启后能从历史附件重新查看,成功持久化后移动或删除外部原文件不影响查看。资源属于会话,存放于应用 userData 管理的文件资源中,数据库保存归属和引用关系,不依赖 Runtime 项目工作目录。 |
|
||||
| FR-H9 | 未发送文档附件使用应用管理的暂存资源;移除、导入失败或取消时仅清理本次无有效引用的暂存,保留其他附件和草稿。发送被接受及队列记录持久化之前完成会话归属和资源持久化,恢复队列不能依赖已丢失的内存 contexts 或外部原路径。删除会话清理其独占资源,仍被草稿或其他有效对象引用的资源须保留;不得按临时目录名在启动或退出时一律删除。 |
|
||||
| FR-H10 | 知识库解析文本与 chunks 继续入库;图片归知识库文档所有,独立于聊天会话。managed 与 reference 来源的解析派生文件均由应用管理,不写入外部原文件目录。重解析或同步只有在新结果及必要资源成功保存后才替换旧结果;失败或取消保留旧结果,删除文档或来源时清理无引用的派生资源。 |
|
||||
| FR-H11 | 每次解析使用启动时已保存的 Provider 和生效选项,并随结果记录供详情查看,不保存凭据副本。修改配置不改写已保存结果;草稿或知识库再次解析时才采用新配置,成功才替换。历史消息和已入队输入保持已接受内容,重新解析通过添加到草稿完成,不改写原消息或队列。失败不得损坏旧文本、图片或来源关联。 |
|
||||
| FR-H12 | 设置中的来源切换保留本地与远程各自配置,高级选项默认折叠;测试前须保存设置。测试、聊天附件和知识库详情共用正文、图片、详情预览,支持页码互跳、打开原件、查看保存时配置、失败及部分结果。聊天卡显示解析进度、取消、重试及完整程度;界面支持窄容器、键盘和辅助技术。具体交互由 UI 设计维护。 |
|
||||
|
||||
### 13.1 范围与存储边界
|
||||
|
||||
本轮已实现配置、HTTP 适配器、数据库引用、共享预览和会话选图链路。
|
||||
具体文件布局、schema 与已验证行为由[生产实现说明](./implementation.md)维护。
|
||||
FR-H8 至 FR-H12 的资源与预览要求针对已解析文档及其产物,也适用于本地或原生解析结果,
|
||||
不要求所有附件都生成解析文件。纯图片遵循 FR-I1 至 FR-I3;图片保存以解析器实际返回的
|
||||
内容为准,不据此承诺本地解析新增插图提取能力。
|
||||
|
||||
聊天文档的原文件、解析文本和图片是应用持有的文件资产,数据库负责关联会话、消息、附件
|
||||
和资源。目录与文件名规划由
|
||||
[技术设计的结果与资源](./http-paddleocr-vl-technical-design.md#结果与资源)维护。历史数据若未保存
|
||||
这些内容,不能显示为已有完整解析资源;本轮不要求恢复缺失历史内容或批量重解析。
|
||||
|
||||
不接入 MinerU、旧 Office 转换、
|
||||
DOCX/XLSX 图片 OCR、图片向量检索、服务安装管理或 DOCX 导出。服务接受 PNG 的探测结果
|
||||
也不改变聊天截图目前作为图片附件的行为。HTTP 上传路径不因 Ask/Execute 模式改变;
|
||||
沿用第 3.3 节的来源选择与 Main 凭据边界,不增加逐文档授权。
|
||||
不增加完整配置快照、结果版本历史或通用恢复系统;仅记录本次结果需要的来源、选项和资源关系。
|
||||
|
||||
### 13.2 配置与预览行为
|
||||
|
||||
界面的信息架构、控件、文案、状态、响应式和无障碍由 [UI 设计](./ui-design.md)唯一维护。
|
||||
来源与保存规则见其第 2 节,共享正文/图片/详情预览见第 3 节,文档选图加入会话见第 4 节,
|
||||
纯图片 OCR 与文档卡片见第 5 节,知识库交互见第 6 节。测试资产在结束预览后清理,不提供
|
||||
加入聊天或知识库操作;正式文档中的图片使用 FR-I5 的显式选择路径。
|
||||
|
||||
### 13.3 用户验收
|
||||
|
||||
以下为产品验收条件;已执行范围与尚未执行项在进度中逐项注明:
|
||||
|
||||
| 场景 | 可观察结果 | 需求 |
|
||||
| --- | --- | --- |
|
||||
| 发送扫描文档后关闭应用,移动或删除外部原文件,再启动并打开历史附件 | 可打开应用保存的原文件,查看原解析正文、表格、图片和页码,无需重新 OCR | FR-H3、FR-H8 |
|
||||
| 文档消息进入发送队列后重启,再恢复发送 | 附件与原解析内容仍可用,不要求重新选择外部文件;取消生成不删除已保存附件 | FR-H8、FR-H9 |
|
||||
| 草稿移除附件、取消解析或批次导入失败 | 本次未提交资源被清理;先前附件、草稿和已发送内容仍可用 | FR-H9 |
|
||||
| 删除一段含文档的会话,再查看其他会话或知识库中的关联文档 | 被删会话独占资源被清理,其他有效引用的原文件与图片仍能打开 | FR-H9、FR-H10 |
|
||||
| 两页返回同名图片键,并使用 HTML img 和 table | 两页图片各自对应正确页码,表格可读,正文引用不串页、不出现未说明的断图 | FR-H3 |
|
||||
| 切换本地与远程来源,修改页眉选项并测试 | 各来源配置保留;未保存时提示保存;保存后测试采用新值,三个结果视图可相互核对 | FR-H1、FR-H4、FR-H7、FR-H12 |
|
||||
| 已保存结果后修改 Provider 或过滤项,再查看旧结果 | 旧正文与图片不变,详情仍显示原解析所用来源与选项;草稿或知识库再次解析成功后才替换,历史消息重解析进入新草稿 | FR-H11 |
|
||||
| 对已有知识库文档重解析,发生服务错误、缺图或取消 | 旧正文、chunks、图片及页码仍可使用;重试成功后显示新结果并清理无引用旧资源 | FR-H6、FR-H10 |
|
||||
| 分别导入 managed、reference 来源,随后删除来源 | 派生图片均在应用管理范围;删除后无引用派生资源清理,外部原文件不被派生资源清理误删 | FR-H10 |
|
||||
| 从聊天文档选两张插图并重复点击添加,再移除父文档 | 草稿只增加两份独立图片附件;父文档在场时正文仅一次,移除后图片仍可发送 | FR-I5 |
|
||||
| 从知识库选图,选择或新建会话后添加;随后重解析或删除源文档 | 仅明确选定会话的草稿变化,会话图片及已发送来源说明仍可用 | FR-I5、FR-H10 |
|
||||
| 视觉模型配合不支持图片的 Runtime,或恢复队列时能力已变化 | 阻止该请求并保留附件,提供切换、提取文字或移除的恢复路径,不静默降级 | FR-I1、FR-I5 |
|
||||
| 图片提取文字成功、失败、取消及再次改回原图 | 成功后明确仅发文字且原图可开;其余状态不丢原图,改回后重新校验图片能力 | FR-I2、FR-I3 |
|
||||
|
||||
完整验收覆盖见 [UI 验收矩阵](./ui-design.md#8-完整验收矩阵)。清理还需核对应用资源目录和
|
||||
数据库引用;仅凭界面中看不到附件不能证明文件已删除。
|
||||
|
||||
@@ -0,0 +1,360 @@
|
||||
# HTTP PaddleOCR-VL 实施与验证
|
||||
|
||||
## 状态
|
||||
|
||||
当前状态以 [2026-09-19 实现与验证](#2026-09-19-实现与验证)、[附件操作布局复验](#2026-09-19-附件操作布局复验)、[全局浮层复验](#2026-09-19-全局浮层复验)及[空正文与草稿恢复修正](#2026-09-19-空正文与草稿恢复修正)为准。
|
||||
以下至“菜单核对检查”的内容是实施前历史记录,保留原日期与原调用数。
|
||||
|
||||
2026-09-18:完成规划文档和独立 HTTP 探索脚本。FR-H1 至 FR-H12 均未完成生产实现,
|
||||
现有设置、解析器、数据库、界面与 Agent 未因本次任务改变。下一步实现顺序见
|
||||
[技术设计](./http-paddleocr-vl-technical-design.md#实施与验证顺序)。
|
||||
|
||||
同日按用户确认补充 [PRD](./prd.md#131-范围与存储边界) 的会话文件资源、数据库关联、
|
||||
生命周期及配置预览验收,并修正技术设计中复用聊天内联图片存储的建议。本轮仅修改文档,
|
||||
未新增服务调用;此前探测记录和下述调用计数保持原意。当前聊天解析文本仍用于临时 contexts
|
||||
与队列,长期附件保存、重启预览和队列资源恢复均待实现与产品验收。
|
||||
|
||||
2026-09-18 图片路由文档核对:新增 FR-I1 至 FR-I5 及对应场景、逻辑,目录规划见
|
||||
[结果与资源](./http-paddleocr-vl-technical-design.md#结果与资源)。源码核对确认
|
||||
`ContextManager.storeImage/enrichRequest` 已走图片请求且会转码 JPEG,队列序列化保存上下文;
|
||||
这不等于原图文件持久化。`ModelRuntime` 有图片能力检查,IPC 的图片工具路径另有引用处理,
|
||||
尚不能宣称所有 Runtime 已提供统一的不支持提示与 OCR 选择。`DocumentParsingService`
|
||||
当前单独处理 PDF、PPTX,其他格式转原生解析;显式纯图片 OCR 入口仍待实现。
|
||||
`KnowledgeService.managedRoot` 由 Main 配为 userData/knowledge,受管原件按库、来源保存。
|
||||
本次仅更新既有文档,保留原有探索脚本和历史证据,未调用 OCR 或聊天模型服务。
|
||||
|
||||
2026-09-18 完整交互设计:新增 [UI 设计](./ui-design.md),同步 FR-I5、图片 OCR 发送方式、
|
||||
历史/队列输入冻结及对应故事、逻辑、技术契约。设置、三入口预览、文档选图到会话、无有效
|
||||
会话时的选择/新建、独立副本、幂等与移除、共享附件限制及完整验收矩阵均已写入设计。
|
||||
这些是文档完成项,FR-H1 至 FR-H12、FR-I1 至 FR-I5 的新增生产能力仍未完成。
|
||||
|
||||
源码核对:设置实际使用 `SettingsCategoryHeader` 和 Portal 诊断框;`App.tsx` 提供聊天
|
||||
reading 与知识库 master-detail 壳层,KnowledgeWorkspace 不再嵌套壳层;共享
|
||||
`PageTabs`、`SegmentedControl`、焦点辅助函数可复用。现有 MarkdownRenderer 的
|
||||
`skipHtml` 不能直接覆盖 OCR 混合 HTML 图表,技术设计已明确适配工作。附件 8 项、Main
|
||||
16 项/12 MiB 规则已存在但部分常量仍分散,设计要求集中复用,没有新增选图配额。
|
||||
|
||||
剩余实现与验证包括 HTTP 和认证、高级选项及有效跨页样例、资产与数据库引用、预览与
|
||||
选图生产接线、本地/远端实际图片输入、取消重启和删除清理。按
|
||||
[技术实施顺序](./http-paddleocr-vl-technical-design.md#实施与验证顺序)推进,不代表允许
|
||||
分批缩减产品范围。本轮未修改产品代码或探索脚本,未新增网络请求或模型调用。
|
||||
|
||||
## 菜单核对
|
||||
|
||||
2026-09-19 对照当前工作区源码检查入口,未运行 Electron;以下为源码事实,不是新增功能验收。
|
||||
目标规则由 [UI 设计](./ui-design.md#11-入口与职责)维护。
|
||||
|
||||
| 入口 | 当前实现与证据 | 与完整目标的差距 |
|
||||
| --- | --- | --- |
|
||||
| 设置 → 文档解析 | [分类定义](../../../src/renderer/src/settings-categories.ts)包含 `document-parsing`;[解析设置](../../../src/renderer/src/DocumentParsingSettingsSection.tsx)提供本地模型管理、场景测试及纯文本诊断预览 | HTTP 配置、正文/图片/详情共享预览尚未接入;当前没有可用远程来源切换控件 |
|
||||
| 应用中心 → 本机推理监控 | [应用定义](../../../src/renderer/src/ApplicationCenter.tsx)仍为四项;[监控](../../../src/renderer/src/LocalInferencePage.tsx)显示本地服务并加载/释放 OCR | 不承担远程 OCR 配置或管理;关闭监控入口不等于关闭解析能力 |
|
||||
| 监控 → 模型与连接设置 | [IPC](../../../src/main/ipc.ts)的 `localInferenceOpenSettings` 只发出无分类的 `settingsOpen`;[App](../../../src/renderer/src/App.tsx)仅打开设置,[SettingsPanel](../../../src/renderer/src/SettingsPanel.tsx)默认分类为 `runtime` | 文案与默认落点不一致,未直达模型或文档解析;本轮仅记录,产品跳转未修复 |
|
||||
| 聊天附件 | App 的附件按钮直接选文件;当前卡片显示名称、容量、缩略图或图标、文本 tooltip 和移除 | 尚无逐附件更多菜单、显式 OCR、解析结果预览、重解析或文档选图 |
|
||||
| 知识库文档 | [DocumentsView](../../../src/renderer/src/KnowledgeWorkspace.tsx)提供任务、打开来源、失败文档重试和分块管理;来源行另有同步,库页头另有“用于当前对话” | 解析文档详情、成功文档重解析及选图须新增,不能把分块管理 Modal 当作已有完整解析预览 |
|
||||
| 新建选图目标会话 | App 的 `startNewConversation` 返回布尔值,拒绝消息通道项目,并在复用空会话或新建后切到聊天 | 预览保留、目标 ID 取得及返回确认仍需接线;直接调用不满足 US-I9 |
|
||||
|
||||
本轮更正文档中“既有文档详情”的描述,补齐菜单矩阵及 US-H16 / A-15;队列修改统一先
|
||||
恢复到草稿,显式图片 OCR 的上传说明与快速模式区别保持一致。根 UI 规范与双语功能清单
|
||||
同步标明本轮范围;本地 OCR 仍为现有能力,HTTP 和新附件流程仍为规划。
|
||||
未修改产品代码、探索脚本或既有应用排序,OCR、文本及视觉模型调用均为 0。
|
||||
|
||||
## 2026-09-19 实现与验证
|
||||
|
||||
HTTP PaddleOCR-VL 已接入 `DocumentParsingService` 的生产 PDF/PPTX 路径,设置支持来源切换、
|
||||
Bearer 加密凭据、过滤、三态参数、跨页整理及连接诊断。新增会话文件资产、数据库归属、
|
||||
可恢复草稿、历史预览、图片 OCR、选图独立副本、队列查看/恢复和解析中断记录。
|
||||
知识库 managed/reference 派生资源单独保存,重解析成功才替换正文、chunks 和资源引用。
|
||||
具体机制及设计差异见 [implementation.md](./implementation.md)。
|
||||
|
||||
### 验证层次
|
||||
|
||||
本轮 Windows x64、Electron 43.2.0、Node 24.18.0,使用当前工作区源码构建到
|
||||
`node_modules/.document-e2e-out`,不覆盖其他运行中的 `out`,不改正式 profile。
|
||||
测试保留 context isolation、sandbox 和禁用 Node integration,未改可信 sender 校验。
|
||||
完整 App 的文件选择对话框由驱动返回合成 fixture;其余设置、解析、存储、预览和输入框
|
||||
使用生产组件、preload 与 IPC。知识库批量生命周期部分通过生产 preload 调用,单独标注。
|
||||
|
||||
原始报告位于获准临时目录 `C:/Users/jiang/AppData/Local/Temp/opencode`。下表目录为该目录
|
||||
的子目录;这些是本机取证路径,不是产品默认值。源码没有写入测试端点。
|
||||
|
||||
| 场景 | 层次及结果 | 证据目录 |
|
||||
| --- | --- | --- |
|
||||
| 自动/高保真扫描 PDF | 生产服务直调,无 OCR Broker 和本地权重;两页正文、两张内联插图,自动模式逐页渲染 | `document-production-XERonK` |
|
||||
| 设置、测试、聊天附件、选图、显式 OCR | 完整 App;保存 HTTP 设置,HTML 表格、图片、测试隔离、文档选图、父附件独立移除、普通图片提取文字 | `document-app-GXKuZv` |
|
||||
| 草稿及 OCR 结果重启 | 完整 App;两份草稿恢复、解析正文重开;重启没有 OCR 请求 | `document-app-MWn5Zn`、`document-app-GXKuZv` 的 restart 报告 |
|
||||
| 实际输入框发送 | 完整 App;文档选图和 OCR 文本从 Composer 发送,回答 `Yellow`,草稿清空,历史预览存在 | `document-app-fuFTEY` |
|
||||
| 已发送附件重启 | 完整 App;历史 OCR 正文包含合成金额,未重新解析 | `document-app-fuFTEY/restart-report.json` |
|
||||
| managed/reference 导入、失败重解析、成功替换、删来源 | 当前 App 的生产 preload/IPC;两种模式各有两图四 chunks,故障注入为关闭的本机端口;失败保留 resultId/chunks,成功更换,删除不损坏会话图片 | `document-app-CBANUC`,前次 `document-app-n65dt4` |
|
||||
| 解析中断恢复 | 完整 App 导入;驱动在 HTTP 发出前暂停并终止应用,重启显示中断,手动重试成功;未把暂停请求算作 OCR 调用 | `document-app-YmDUR9` |
|
||||
| 队列能力失配、重启、恢复草稿 | 生产 IPC;首次暂扣 Renderer dispatch 以固定未执行队列状态,重启恢复真实分发;模型能力设为不支持图片,队列保留;附件恢复且文字合并,不重复排队 | `document-app-YyagTN` |
|
||||
| 直连实际图片消费 | 当前生产 Main/IPC/ModelRuntime;观察 Provider 请求包含图片并收到 `Yellow` | `document-app-VmNZIG` |
|
||||
| 托管 OpenCode 实际图片消费 | 当前 Desktop 至共享 Linux x64 Host;使用已有 Host 凭据和固定主机密钥,独立 `/root/tmp/goodbuddy-document-*` 目录;透传代理只计数与核对图片,Provider 返回 `Yellow` | `document-app-gzC5ZV` |
|
||||
| 托管 OpenCode 文档选图发送 | 生产导入、持久化选图、移除父文档后发图;Provider 请求有图,回答 `Yellow`,测试目录回收 | 最终 `document-app-iZSxV8`,前次 `document-app-zqnBBt` |
|
||||
| 跨页表格效果 | 生产解析服务;两页连续设备发票表从两个 HTML table 变为一个,32 个条目各出现一次;分组来源页为 `[1,2]` | `document-production-Twr0N4/report.json`、`table-check.json` |
|
||||
| 图片文件与引用回收 | 真实 SQLite/文件测试;原字节、重启、共享消息引用、队列与草稿独立、数据库无原图 Base64、解析中断原件保留 | `conversation-attachment-storage.test.ts` |
|
||||
| HTTP 认证及失败 | 真实 loopback HTTP fixture;生产客户端 Bearer、401、取消和 health/OpenAPI;公网/内网认证部署未测 | `http-document-ocr.integration.test.ts` |
|
||||
|
||||
HTTP OCR 本身只运行在 Desktop。未修改 Agent daemon、传输协议或远端 OCR 实现;远端验证
|
||||
使用当前 Desktop 接入既有 Agent 图片链路,不将此次结果记作新 Agent 包发布或安装验收。
|
||||
|
||||
### 界面取证
|
||||
|
||||
`document-app-GXKuZv` 的视觉复验覆盖浅/深主题、约 1279×800、959×720、719×640、
|
||||
680×561 CSS 像素及 200% 缩放下约 639×400。原生最小窗口限制使请求的 640×420 实际为
|
||||
680×561,不能写作实际验证了 640×420。十组测量中页签未收缩、主体可滚动、操作区可见,
|
||||
无横向溢出。原生 `Right` 和 `Escape` 通过;最初驱动用 `ArrowRight`,未产生 Electron
|
||||
原生键事件,修正后复验。已打开审阅浅色窄窗口和深色 200% PNG。
|
||||
|
||||
诊断初次完整 App 截图发现 HTTP 被显示为“本地 OCR”、附件按钮继承固定 28px 宽度,
|
||||
均已修正。测试菜单统一到分类页头,诊断关闭恢复测试按钮焦点。知识库使用详情/返回列表,
|
||||
普通历史和队列分别只读;队列修改入口为“恢复到草稿”。
|
||||
|
||||
隔离生产构建启动有 4 条打包字体 data URI 被现有 CSP 阻止的控制台记录,本轮未修改字体或
|
||||
CSP。截图使用系统字体回退可读;此项不能算作无控制台告警。
|
||||
|
||||
### 自动检查
|
||||
|
||||
最终完整 `npm test`:399 个文件通过、9 个跳过;4,779 项通过、67 项跳过,耗时 816.55 秒。
|
||||
最新 `npm run typecheck`、`npm run lint` 通过。隔离 `electron-vite build` 通过,保留既有
|
||||
workspace-management 动静态导入警告。没有提交或推送。
|
||||
|
||||
首次全仓运行有 9 项失败,其中 8 项是新增参数/schema 后的旧断言,1 项为并行画布导航的
|
||||
Quill 加载失败;最终完整运行均已通过。中途一轮超过 240 秒工具期限,未产生完成报告,
|
||||
不计为通过。其他聚焦失败包括新增测试缺少 cleanup、旧附件失效行为断言、诊断焦点返回,
|
||||
均保留修正后的回归。
|
||||
|
||||
队列验证发现提前保存用户消息导致重启删除未执行队列的缺陷,已修正并复验。另有测试驱动
|
||||
误读升级页首次 load、未等待菜单挂载、文本换行比较器使用了转义字符串等失败;这些没有
|
||||
被记作产品已通过,最终队列证据采用 `document-app-YyagTN`。
|
||||
|
||||
### 调用计数与边界
|
||||
|
||||
按保存报告可核对 **28 次远程 layout、1 次 restructure、0 次远程 health/OpenAPI GET**。
|
||||
另有一次表格测试被工具超时终止:设置已进入第二次 layout 阶段,可以确认它包含 2 次
|
||||
layout,是否已经发出 restructure 缺少 dispatch 日志。故本轮已知 layout 合计 **30 次**;
|
||||
restructure 可核对 1 次,另最多 1 次状态不明。未将 2026-09-18 探测计入本轮。
|
||||
|
||||
模型请求报告中可观察到 7 次 Provider dispatch,包含早期驱动提前退出/超时的 3 次。
|
||||
`document-app-zMeC7X` 的远端调用不经过 Desktop fetch,另从 Agent 的操作 ID 记录确认
|
||||
1 次已完成;完整 Composer 发送 `document-app-fuFTEY` 使用独立 Runtime,桌面 fetch 计数
|
||||
不覆盖其内部 Provider 请求,已收到真实 `Yellow`,该次至少 1 次调用。
|
||||
因此本轮可精确核对 **8 次模型请求,另有至少 1 次完整 UI Runtime 请求**;早期队列驱动
|
||||
超时且缺少报告的轮次不能给出完整 Provider attempt 数。后续驱动在 dispatch 时单独写计数,
|
||||
不以输入框提交数代替实际调用数。
|
||||
|
||||
未执行 macOS、Linux Desktop/arm64、本地 OCR 各档位、Continue/DeepSeek Harness 的本轮真实
|
||||
图片消费;未验证方向分类、去扭曲、印章、图表和公式选项的效果,也未验证真实磁盘满场景。
|
||||
跨页表格结论只覆盖上述连续表格,标题重定级没有独立有效样例。完整 App 的原生浏览器
|
||||
遮挡没有在本轮单独实测,继续使用共享 Modal 遮挡机制及其既有回归。
|
||||
|
||||
上述未测项保留在验收矩阵,不能将 A-01 至 A-15 的全部分支写成已通过。
|
||||
|
||||
## 2026-09-19 附件操作布局复验
|
||||
|
||||
对应 FR-H12。检查实际工作区后保留已有 OCR、存储、预览与队列实现,修正草稿、历史消息
|
||||
及队列预览的操作布局。查看结果与更多操作改为同排 32px 图标按钮,草稿移除使用同尺寸
|
||||
叉号;保留含附件名的可访问名称、悬停提示和键盘聚焦说明。文件名省略,状态与大小可换行。
|
||||
导入中的取消按钮也改为图标,进度卡使用三列,避免取消文字挤入固定宽度按钮。
|
||||
|
||||
文件名核对覆盖 `ContextManager` 导入、显式 OCR、重解析、`ConversationAttachmentStorage`
|
||||
和结果清单。源码将 UUID 用于资源标识,显示名来自导入文件名;已有存储测试验证原名称在
|
||||
重启后保留。本轮没有截图对应的原文件及记录,无法确认该 UUID 名称的来源,未作猜测性改名。
|
||||
|
||||
`AttachmentActions.test.tsx` 验证图标名称、预览关闭和菜单 Escape 焦点恢复、菜单方向键
|
||||
及队列只读动作。`tests/attachment-layout.electron.test.ts` 加载生产 `ChatMessageRow`
|
||||
与样式,在 Windows Electron 中验证浅/深主题、1000/720/360px 宽度及 200% 缩放;两个
|
||||
操作均为 32px、同排,长文件名截断,卡片高度低于 110px,无页面横向溢出。原生 Tab、Space
|
||||
打开菜单及 Escape 返回通过。此用例替代设置快照接口,不调用 OCR,不作为完整 App 解析验收。
|
||||
|
||||
新增 Electron 用例初次受系统缩放的亚像素取整影响,测得根容器宽 360.57px;驱动固定
|
||||
device scale factor 后复验。驱动 Enter 未产生按钮激活,改用原生 Space 后菜单流程通过。
|
||||
这些驱动失败未记作产品通过,也未通过修改页面溢出样式绕过测量。
|
||||
|
||||
定向 10 文件、116 项测试通过,覆盖 HTTP 客户端及 loopback、解析服务、设置、会话文件
|
||||
存储、ContextManager、预览、附件操作及共享 UI。App 附件筛选的 3 项测试通过,包含导入
|
||||
进度中的图标取消入口;新增 Electron 用例单独通过。最终
|
||||
`npm run typecheck`、`npm run lint` 通过。`npm test` 完整运行 885.06 秒:400 个文件
|
||||
通过、9 个跳过;4,782 项通过、67 项跳过。该运行启动时尚未创建新增 Electron 用例,
|
||||
因此其通过结果单独记录;导入取消入口的最终改动由随后 App 定向测试覆盖。
|
||||
日志仍有既有 jsdom Canvas 提示和测试临时仓库的换行提示,没有测试失败。
|
||||
|
||||
本轮未修改 Main、Preload、Agent 或 Runtime 请求链路,未发起真实 OCR、文本或视觉模型
|
||||
请求。前轮实际调用与未测验收项仍以上节记录为准,不能由本次界面回归推断全部高级选项、
|
||||
Runtime 组合和跨平台验收已完成。没有提交或推送。
|
||||
|
||||
复核前轮 `document-app-GXKuZv/report.json`、`document-app-fuFTEY/restart-report.json`
|
||||
及 `document-app-iZSxV8/report.json`:设置和显式图片 OCR、重启后无 OCR 读取、远端图片
|
||||
消费记录均为 passed。文档扫描无阻断项;复核候选为字段清单和验证边界,保留其事实限定。
|
||||
|
||||
## 复现
|
||||
|
||||
脚本为 [`scripts/paddleocr-vl-probe.mjs`](../../../scripts/paddleocr-vl-probe.mjs),
|
||||
使用 Node 内置 fetch 和已安装的 `@napi-rs/canvas`,不新增依赖。`--endpoint`、`--out`
|
||||
必填,无产品地址默认值;`--out` 必须是已存在的临时或 Git 忽略目录,由调用者选择。
|
||||
脚本在其中创建独占子目录,不覆盖历史运行。中文 fixture 需 Microsoft YaHei 或 Noto Sans
|
||||
CJK SC;缺字体导致文字未绘制时中文断言会失败,先检查 `fixture.png`。
|
||||
|
||||
本次实际执行命令(地址仅是获准使用的测试目标):
|
||||
|
||||
实测时脚本名为 `.cjs`;交付前按仓库脚本惯例改为 `.mjs`,只调整模块导入,复现时使用
|
||||
下列最终路径。请求、fixture 和断言逻辑未变。
|
||||
|
||||
```powershell
|
||||
node scripts/paddleocr-vl-probe.mjs --endpoint http://10.7.0.152:8080 --out C:/Users/jiang/AppData/Local/Temp/opencode --mode discover
|
||||
node scripts/paddleocr-vl-probe.mjs --endpoint http://10.7.0.152:8080 --out C:/Users/jiang/AppData/Local/Temp/opencode --mode full --timeout-ms 60000
|
||||
```
|
||||
|
||||
`discover` 执行 health、OpenAPI 共 2 个 GET;`full` 另执行 4 次 layout 请求和 2 次
|
||||
restructure 请求。无自动重试,每请求默认超时 60 秒、最多 120 秒,响应流上限 32 MiB。
|
||||
完整运行至多 8 个请求,默认最坏请求等待预算 480 秒;失败停止后续调用并写 report。
|
||||
不读取用户资料、不使用凭据、不跟随重定向、不获取返回的图片 URL。
|
||||
|
||||
输出包括合成 PNG、两页纯图像 PDF、OpenAPI、选项 schema、各请求选项、逐页 Markdown、
|
||||
摘要、解码后的插图与 `report.json`。摘要记录 HTTP 状态、耗时、页标签、图片格式、尺寸、
|
||||
字节数、SHA-256 和页内引用对应关系,不输出 Base64 或返回 URL。Markdown 保留服务原文,
|
||||
图片本地文件名映射在 `*-summary.json`;它不是已改写资源路径的产品预览。
|
||||
|
||||
## 实测结果
|
||||
|
||||
环境:Windows x64,Node 24.18.0。fixture 为 1200 × 1600 像素合成报告,包括中英文、
|
||||
页眉页脚、页码、风景插画和两列表格;PDF 两页均为嵌入 JPEG,无原生文本层。与先前仅文字
|
||||
PNG、文本 PDF 探测不同,本次验证了扫描型输入和插图输出。
|
||||
|
||||
最终运行目录:`C:/Users/jiang/AppData/Local/Temp/opencode/paddleocr-vl-L8LJIm`,
|
||||
运行时间为 `2026-09-18T05:04:33.802Z`。23 项断言全部通过,8 个请求均 HTTP 200。
|
||||
|
||||
| 请求 | 选项差异 | 耗时 | 结果 |
|
||||
| --- | --- | --- | --- |
|
||||
| PNG 默认 | 省略 ignore labels,图片 true,可视化 false | 1439 ms | 804 字符,中文及金额正确;页眉页脚不进入 Markdown |
|
||||
| PNG 保留标签 | `markdownIgnoreLabels: []` | 937 ms | 875 字符,页眉页脚可见 |
|
||||
| PNG 排除正文 | `['text','header','footer','number']` | 931 ms | 696 字符,金额正文被排除;表格和插图保留 |
|
||||
| 两页 PDF | 保留标签 | 2490 ms | 每页 875 / 847 字符,Page 1 / Page 2 顺序正确,均含表格和插图 |
|
||||
| 重组关闭 | `mergeTables: false, relevelTitles: false` | 146 ms | 两页文字、图片引用保留 |
|
||||
| 重组开启 | 两项均 true | 167 ms | 两页文字、图片引用保留,与关闭组 Markdown 相同 |
|
||||
|
||||
PNG 插图为裸 Base64 JPEG,解码后 899 × 482、23,708 字节;PDF 两页分别为
|
||||
899 × 482、25,849 字节和 898 × 481、25,585 字节。未观察到 URL 或 data URI 图片;
|
||||
这两种返回形式的分支尚无真实服务验收。Markdown 插图是 HTML `<img>`,表格是 HTML
|
||||
`<table>`,不能只按 Markdown `![]()` 链接提取资源。
|
||||
|
||||
本次 `prunedResult` 没有 `page_index`,`page_count` 是 null。第一个 full 探测曾把
|
||||
零起始 page_index 当断言,因此退出非零;服务请求本身全部成功。该运行同时发现默认
|
||||
sans-serif fixture 没有显示中文。修正字体并将页码检查改为合成页标记顺序后完整复测。
|
||||
第一次 full 的两页图片键相同,证实不能直接用 Provider 图片键作为文档级唯一标识。
|
||||
|
||||
## 调用计数
|
||||
|
||||
本轮实际发送:1 次 discovery 加 2 次 full,共 **6 个 GET、8 次 layout 推理请求、4 次
|
||||
restructure 请求**;layout 输入共 10 页图像(每轮 3 张 PNG 加 2 页 PDF)。重组接口内部
|
||||
是否另调用模型无法从客户端确认,以上计数为 HTTP 调用数。文本模型调用 0 次。
|
||||
最初一次终端内联 Node 命令因 shell 转义在发请求前语法失败,不计为 HTTP 请求。
|
||||
|
||||
此前对话的探测记录另有 3 次成功 layout 请求(首次 PowerShell 图片请求、Node 双语 PNG、
|
||||
两页文本 PDF),不包含在上述本轮计数;合并已知历史则为 11 次 layout、4 次 restructure,
|
||||
历史 GET 数未追溯。
|
||||
|
||||
## 能力边界
|
||||
|
||||
已验证 FR-H3/FR-H4/FR-H7 对应的服务基础事实:图片字节、页内链接、表格结构、中英文、
|
||||
页眉页脚过滤及有界报告。未完成这些需求的产品持久化、界面与端到端验收。
|
||||
|
||||
重组请求成功仅验证接口形状及图片传递。fixture 中虽然有“continued”表格,但开关前后
|
||||
输出相同,不能证明真实跨页合并或标题重定级生效。方向分类、去扭曲、图表、印章、图片块
|
||||
OCR、公式编号、输出格式等只发现 schema,未主动启用探测,详见
|
||||
[参数矩阵](./http-paddleocr-vl-technical-design.md#高级参数)。
|
||||
|
||||
未测认证、HTTP 异常返回、服务端取消、大文件、并发、其他操作系统、原生/扫描混合页的
|
||||
产品选页策略、PPTX HTTP 路径和图片生命周期。单次样例耗时不构成吞吐或质量基准。
|
||||
测试产物留在独占临时目录供复核,未写入聊天或知识库。
|
||||
|
||||
## 检查
|
||||
|
||||
已运行脚本 `--help`、语法检查及上述真实 full 探测。最终 `.mjs` 的 `--help`、
|
||||
`node --check`、`npx eslint scripts/paddleocr-vl-probe.mjs` 均通过;27 个相对文件链接及
|
||||
锚点检查通过,`git diff --check` 无空白错误。
|
||||
文档扫描的 2 个阻断候选均位于既有本地 OCR 文档(PRD 的按幻灯片计数、聊天技术说明的
|
||||
Worker 整体回收),是具体实现对比,保留原文。本次新增内容没有阻断项;复核候选主要是
|
||||
已测/未测边界、字段清单和生命周期规则,人工审阅后保留事实限制。
|
||||
没有生产功能变更,不运行整个产品测试集。
|
||||
|
||||
2026-09-18 PRD 补充检查:7 份 Markdown 的 30 个相对文件链接及锚点通过,含未跟踪文档的
|
||||
空白与冲突标记检查通过,相关 `git diff --check` 无空白错误。使用 deai-writing 扫描并人工
|
||||
复核,新增内容无阻断项;既有的 2 处实现对比按上述理由保留。复核候选为参数清单、验收条件
|
||||
和实现边界,未删除这些必要限定。本轮未运行产品测试或服务探测,未修改产品代码。
|
||||
|
||||
2026-09-18 图片路由补充检查:7 份 Markdown 的 36 个相对链接及锚点通过,含未跟踪文档的
|
||||
空白与冲突标记检查通过,`git diff --check` 无空白错误。deai-writing 扫描仍为既有 2 个
|
||||
阻断候选,按上述实现对比理由保留;79 个复核候选经通读核对,主要为参数枚举、验收和
|
||||
能力限制,新增内容无阻断项。本轮未运行产品测试、真实服务调用或提交。
|
||||
|
||||
2026-09-18 完整 UI 文档检查:8 份 Markdown 的 58 个相对链接(其中 30 个含锚点)均通过;
|
||||
17 项需求、27 个用户场景、14 条逻辑规则的 ID 引用有效,每项需求均有场景与逻辑追溯。
|
||||
检查包含未跟踪文档的行尾空白、文件末尾换行及冲突标记,`git diff --check` 通过。
|
||||
目标目录中的缩减交付和选图待定措辞已核对修正;技术实现顺序和未实测能力边界保留。
|
||||
|
||||
使用 `python -X utf8` 运行 deai-writing 的 `deai_scan.py docs/features/document-processing --ext .md`,
|
||||
新增内容无阻断项;既有 2 处实现对比按此前记录的理由保留。人工复核参数清单、需求条件、
|
||||
状态规则及否定句候选,保留防止误发、丢图、误删和夸大验证范围的明确限制。通读核对 UI
|
||||
容器、选择/附加状态、原图/文字发送方式、资源归属和失败替换规则,未将设计计作生产完成。
|
||||
本轮 OCR 请求、文本/视觉模型调用均为 0;未运行产品测试、构建或远端 Host 验证,未提交。
|
||||
|
||||
2026-09-19 菜单核对检查:文档处理目录及关联根文档、导航文档共 13 份 Markdown,185 个
|
||||
相对链接和其中 70 个锚点均通过;含未跟踪文档的行尾空白、末尾换行和冲突标记检查通过。
|
||||
`git diff --check` 通过。deai-writing 扫描文档处理目录仍为既有 2 个阻断候选,按前述具体
|
||||
实现对比理由保留;新增内容无阻断项,复核项保留必要的能力边界和菜单状态限制。关联文档
|
||||
新增段落经人工审校;本轮没有产品代码变更,未运行产品测试或实机菜单验收。
|
||||
|
||||
## 2026-09-19 全局浮层复验
|
||||
|
||||
修复前源码在 Electron 中复现:“测试解析”的 `AnchoredMenu` 层级为 129,低于设置
|
||||
遮罩的 130;通知层级为 75,且仍在被设为 `inert` 的应用壳层内。两者的鼠标命中均失败。
|
||||
共享修复及后续约束见[复发缺陷规则](../../quality/recurring-defects.md#modal-内的菜单与通知被遮挡或隔离)。
|
||||
|
||||
- `FloatingPortal.test.tsx` 覆盖 Modal 切换时保持同一通知宿主和内容、焦点循环及菜单归属。
|
||||
- `overlay-layering.electron.test.ts` 使用生产组件与受控 IPC,验证浅/深主题下的
|
||||
1280×800、960×720、720×640、640×420;包含通知在 Modal 前/后出现、点击关闭、
|
||||
Tab/Shift+Tab/Escape、解析场景菜单与二级结果、应用中心、本机推理监控和原生 `dialog`。
|
||||
- 完整 App 使用当前 Main、Preload、Renderer 的独立构建及空白 profile,从侧栏设置入口
|
||||
修改最大页数为 99,确认生产读取值及成功通知;原生鼠标点击“测试解析”菜单后,生产 IPC
|
||||
解析本地 TXT,结果展示 70 字符,Escape 逐层关闭并恢复焦点。仅文件选择器返回合成文件;
|
||||
未替换解析、保存或通知。模型及 OCR 服务调用均为 0。
|
||||
- 已通过图片读取工具逐张审阅 fixture 的 16 张 `light/dark-宽度-menu/notification.png`,
|
||||
以及完整 App 的保存通知、测试菜单和解析结果三张 PNG;文案、主题、菜单与通知层级正常。
|
||||
|
||||
临时证据位于批准目录下的 `overlay-baseline-0919`、`overlay-layering-0919` 和
|
||||
`overlay-full-app-0919-r2`。完整 App 首轮因驱动选择器错误失败,修正后通过;仍记录到
|
||||
4 条既有 data-font CSP 警告。本轮实际 UI 环境为 Windows x64、Electron 43.2.0,
|
||||
未验证 macOS、Linux 或辅助技术的实际语音播报。
|
||||
本轮临时 profile、基线缓存和独立构建已清理,保留脚本、脱敏报告、源码/产物哈希及截图。
|
||||
|
||||
最终 `npm test`:403 个文件通过、9 个跳过,4,793 项通过、67 项跳过,耗时 909.69 秒。
|
||||
`npm run typecheck`、`npm run lint` 及隔离目录的 Electron Vite 生产构建通过;
|
||||
相关文档的 48 个相对文件链接有效,`git diff --check` 通过。
|
||||
|
||||
## 2026-09-19 空正文与草稿恢复修正
|
||||
|
||||
对应 L-I3、L-H7、L-H8。修复前已复现:无识别文字、有效插图与缺图警告并存时,
|
||||
警告被当作正文,显式提取后请求没有原图;队列恢复等待期间输入的文字也会被旧草稿覆盖。
|
||||
实现和判定归属见[生产实现说明](./implementation.md),未增加持久状态或修改数据 schema。
|
||||
|
||||
- 解析、结果完整程度和 HTTP Provider 共用提取正文判定。8 个解析/存储/共享 UI 测试文件
|
||||
203 项通过,其中 loopback HTTP 回归覆盖缺图警告、正文存在与否及保存后的 `images-only`。
|
||||
- 生产 `App` 的 6 项回归通过,覆盖空草稿、已有草稿、恢复期间编辑、清空、失败及切换会话。
|
||||
IPC 响应由测试控制完成时机,不能称为完整桌面到 Main 的新增端到端验收。
|
||||
- 当前源码在隔离 Electron 中通过真实 `nativeImage`、HTTP 客户端、附件 SQLite/文件存储、
|
||||
`reparseDraft` 与 `enrichRequest` 复验:仅图片及图片加缺图警告均拒绝文字切换,
|
||||
草稿仍引用原附件,发往 Runtime 的请求保留一张图片;真实正文控制组仍切为文字发送。
|
||||
三次请求均发往本机合成 OCR 服务,外部 OCR/文本/图像 Provider 请求为 0。
|
||||
- 本轮 OCR 判断和草稿合并仍位于 Desktop,共用同一 Main 请求构造与 Renderer 草稿机制;
|
||||
没有远端 OCR 分支、Agent 协议或模型桥变更,不据此扩大远端视觉模型验收结论。
|
||||
|
||||
验证用的临时 fixture、Electron profile 和数据库已清理。首次 OCR 探针因临时目录的模块
|
||||
解析失败,调整启动器的依赖路径后通过;新会话切换测试最初遗漏全局队列读取,修正
|
||||
fixture 后通过。上述均为验证工具问题,不修改产品行为来满足错误 fixture。
|
||||
|
||||
合并画布与 Continue 修正后的 `npm test` 完整结束:405 个文件通过、9 个跳过,
|
||||
4,826 项通过、67 项跳过,耗时 982.48 秒,无失败。最终 `npm run typecheck`、
|
||||
`npm run lint` 及助手数据库 101 项单独复验均通过。会话列表测试更新为无 100 条截断的
|
||||
既定行为,共享高对比度测试检查全部 forced-colors 块;没有为旧断言恢复产品限制。
|
||||
隐藏 Electron 窗口的双帧等待实测令布局场景耗时约 63 秒,因此只把测试子进程期限从
|
||||
60 秒改为 90 秒,并对自有临时目录增加有界删除重试;完整布局断言未减少。
|
||||
@@ -0,0 +1,315 @@
|
||||
# 文档解析与附件 UI 设计
|
||||
|
||||
本文定义 [PRD](./prd.md) FR-H1 至 FR-H12、FR-I1 至 FR-I5 的完整交互,状态决策以
|
||||
[逻辑设计](./logic-design.md)为准。当前实现细节见[生产实现](./implementation.md),验证
|
||||
状态记在 [进度](./progress.md)。配置、解析、预览、选图、发送和资源清理属于同一验收范围。
|
||||
|
||||
## 1. 容器与共享组件
|
||||
|
||||
遵循根目录 [UI 设计系统](../../../UI-DESIGN.md),保留当前应用容器结构。下表依据
|
||||
2026-09-18 源码核对,标为新增的内容需要实现,不能当作现有 API。
|
||||
|
||||
| 位置 | 现有结构及设计用法 |
|
||||
| --- | --- |
|
||||
| 设置中心 | `SettingsPanel.tsx` 整窗 Modal 内的 `DocumentParsingSettingsSection.tsx`;继续使用 `SettingsCategoryHeader`、`settings-section`、`field`,不套第二个 PageShell 或一级标题 |
|
||||
| 聊天 | `App.tsx` 的 `PageShell variant="reading"`;附件卡位于原 Composer 内,待发送队列仍在 Composer 外,不在助手工作栏增加附件栏目 |
|
||||
| 知识库 | `App.tsx` 已提供 `PageShell variant="master-detail"`,`KnowledgeWorkspace.tsx` 内含库导航与 DocumentsView;需在文档区域新增解析详情及返回列表,不再嵌套页面壳层或新建知识库级页签 |
|
||||
| 解析预览 | 新增共享内容视图,分别嵌入设置诊断 Modal、聊天附件 Modal 和知识库文档详情;共享数据和交互,外层容器负责标题、范围、关闭与滚动 |
|
||||
| 页签与选择 | `WorkspacePrimitives.tsx` 的 `PageTabs variant="segmented"` 承载正文、图片、详情;来源及过滤模式使用 `SegmentedControl`,不能用它替换页签语义 |
|
||||
| 浮层 | Modal 沿用 body Portal、`activateModalFocus` 与 `trapTabFocus`;测试及附件菜单复用 `AnchoredMenu`,由共享 `FloatingPortal` 挂入所属 Modal 并在 top layer 绘制。通知遵循根 UI 规范 |
|
||||
| 图文内容 | 复用 `MarkdownRenderer.tsx` 的排版、表格滚动和静态 HTML 处理边界;补齐受管图片与 OCR HTML 表格适配,现有 Markdown 渲染器并未直接支持这些资源 |
|
||||
| 状态与反馈 | `ScopeBadge`、`EmptyState`、共享按钮、`toggle-row` Switch、应用 `onNotify`;图片多选用 Checkbox,失败重试就地显示 |
|
||||
|
||||
全部颜色、间距、字体、边框、圆角和动效使用根设计令牌。连续正文使用阅读字号,图片格不
|
||||
加装饰阴影。长文件名截断并提供完整名称,不能挤走状态、关闭和发送。图片和解析结果不是
|
||||
生成图片成果,不使用生图操作卡或生图重试入口。
|
||||
|
||||
### 1.1 入口与职责
|
||||
|
||||
本表规定完整功能的入口和配置归属,源码现状及证据统一见
|
||||
[菜单核对记录](./progress.md#菜单核对)。应用中心的四应用清单、启用、常驻和排序继续遵循
|
||||
[应用导航](../application-tool-navigation/README.md),文档解析不新增第五个应用或右侧工作栏栏目。
|
||||
|
||||
| 用户任务 | 主入口与路由 | 范围及目标行为 |
|
||||
| --- | --- | --- |
|
||||
| 配置解析与 OCR | 侧栏底部“设置” → “文档解析”(`document-parsing`) | 设备级共享配置,分别保留聊天与知识库工作流;HTTP 地址、凭据和高级选项只在此维护,不随当前项目或会话切换 |
|
||||
| 管理本地 OCR 文件 | 同一“文档解析”分类的本地模型区 | 下载、校验、ZIP、删除和目录操作;全局模型下载源仍在“平台功能 / 通用设置”,与 OCR 来源选择分开 |
|
||||
| 查看或释放本地 OCR 内存 | “应用中心” → “本机推理监控”(`local-inference` Modal),或其常驻入口 | 只管理本机执行服务;应用启用及常驻控制入口,不控制解析功能;HTTP 服务不出现在本机服务列表,也不提供远程启停 |
|
||||
| 添加文件或图片 | “当前对话” → “添加附件”,或输入框粘贴 | 先加入当前草稿,再按输入类型处理;上传按钮继续直接选文件,不改成先选 OCR 的菜单 |
|
||||
| 查看聊天解析结果 | 草稿或历史消息的附件卡 | 规划中的共享预览;来源消息与目标草稿分别显示,历史输入只读 |
|
||||
| 查看知识文档结果 | “知识库” → 选库 → “文档与来源”中的文档操作 | 规划新增“查看解析结果”和文档详情;返回恢复库、列表筛选和位置;不改变活动会话 |
|
||||
| 把文档插图用于聊天 | 聊天或知识文档预览 → “图片” → “添加所选图片” | 明确目标会话草稿后复制;设置测试预览无此操作;已有“用于当前对话”仍负责知识库检索范围,不代替选图 |
|
||||
|
||||
从解析失败或未配置提示提供“前往文档解析设置”时,须直达 `document-parsing` 分类并保留
|
||||
来源预览、目标和草稿;关闭设置返回原位置。跳转沿用设置中心离开保护,不复制配置表单。
|
||||
保存后只更新后续解析采用的配置,不自动重试、重解析或发送。本机推理监控现有“模型与连接
|
||||
设置”不是分类直达入口,不将它作为 HTTP OCR 配置路径。
|
||||
|
||||
### 1.2 附件操作菜单
|
||||
|
||||
下表规定交互。卡片提供查看入口,移除保留独立按钮;次操作放在卡片“更多”菜单,
|
||||
菜单项包含附件名称以区分多个文件。普通图片的“提取图片文字”可作为就近次操作,不能和
|
||||
菜单形成两套状态或重复执行。菜单复用 `AnchoredMenu` 的定位、键盘和焦点恢复。
|
||||
|
||||
草稿、历史消息和队列预览中的“查看解析结果”“更多附件操作”使用共享 `icon-button`
|
||||
视觉,图标分别为文档查阅和省略号,点击区域为 `32px × 32px`,在独立操作行内排列。
|
||||
按钮提供含文件名的可访问名称、悬停提示及键盘焦点提示;草稿移除和导入取消使用同尺寸
|
||||
叉号。导入卡按进度图标、文件信息、取消按钮排列,进度条独占下一行。
|
||||
文件名保持单行省略,悬停可读完整名称;解析状态、发送方式和大小在元数据行按空间换行,
|
||||
不将次操作拉伸成整行文字按钮。布局以卡片所在容器宽度为准,沿用主题令牌和共享焦点环。
|
||||
|
||||
| 对象 | 查看操作 | 可改变内容的操作 |
|
||||
| --- | --- | --- |
|
||||
| 草稿文档 | 查看解析结果、打开原文件 | 使用当前设置重新解析、移除;图片多选只在预览中进行 |
|
||||
| 草稿图片,发送原图 | 打开原图;已有 OCR 结果时可查看 | 提取图片文字、移除 |
|
||||
| 草稿图片,发送提取文字 | 查看解析结果、打开原图 | 改为发送原图、使用当前设置重新提取文字、移除 |
|
||||
| 历史消息附件 | 查看已保存结果、打开原件 | 需要重解析或改变发送方式时先添加到当前可编辑草稿;不修改原消息 |
|
||||
| 已入队附件 | 仅有附件时显示“查看附件”,查看入队时冻结的结果与发送方式;无附件时隐藏入口 | 修改整条输入使用“恢复到草稿”,按 L-H7 合并;不直接修改队列附件 |
|
||||
| 知识文档 | 查看解析结果、打开来源、管理分块 | 使用当前设置重新解析;失败时使用重试,不再并列一个同义重解析按钮;来源同步仍属于来源级操作 |
|
||||
| 设置测试结果 | 查看正文、图片、详情及原件 | 重新测试、关闭;没有加入草稿、知识库或发送操作 |
|
||||
|
||||
操作依据对象、资源和任务状态显示。解析中仅保留可用旧结果的查看、进度和取消,不允许重复
|
||||
解析;缺少结果或原件时说明缺失原因并给出重选入口。已有历史数据不显示能恢复完整资源的
|
||||
假菜单。没有受管解析资产的外部知识库结果沿用其已有查看能力,不提供本地重解析或选图。
|
||||
尚未接入生产链路的动作不先加入应用菜单或附件菜单充当可用功能。
|
||||
|
||||
## 2. 文档解析设置
|
||||
|
||||
### 2.1 信息顺序与保存
|
||||
|
||||
分类页头为“文档解析”,说明“配置聊天文档附件和知识库导入使用的解析方式”。右侧依次为
|
||||
“测试解析”“保存设置”,仅保存为主按钮。现有两处场景测试按钮合并到页头测试入口,入口
|
||||
展开“聊天附件 / 知识库导入”选择后打开文件对话框;场景预设仍各自编辑。
|
||||
|
||||
内容依次为运行状态、使用场景、OCR 来源与对应配置、默认折叠的高级设置。原生解析、OCR、
|
||||
转换分别显示状态;远程 OCR 未配置不把原生解析标成不可用,旧 Office 转换沿用现状说明。
|
||||
切换来源不发网络请求,不清空隐藏配置,也不改变已保存来源。显示“有未保存的修改”,
|
||||
所有字段、过滤项和来源切换参与设置中心已有离开保护。
|
||||
|
||||
未保存时禁用测试和连接检查,旁边持续说明“请先保存设置,再测试解析”,通过
|
||||
`aria-describedby` 关联;不得使用旧配置测试。保存期间防止重复操作,失败保留输入并在
|
||||
分类页头显示原因。保存成功走应用通知,测试成功直接打开结果并呈现状态,不再同时弹
|
||||
“测试成功”通知。取消文件选择不清除上次结果、不创建任务。
|
||||
|
||||
### 2.2 来源配置
|
||||
|
||||
“OCR 来源”使用共享分段控件,选项为“本地模型”“远程服务”,下方显示已保存来源与当前
|
||||
编辑值是否不同。聊天和知识库分别保留工作流选择,不增加每会话 Provider 配置副本。
|
||||
|
||||
本地区域保留已有模型档位、下载源、安装校验、取消下载、ZIP 导入导出、删除及打开目录。
|
||||
本地模型缺失只影响需要本地 OCR 的任务。切到远程后不要求先安装本地模型。
|
||||
|
||||
远程区域固定服务类型“HTTP PaddleOCR-VL”,包含有持久标签的服务地址、认证方式、密钥
|
||||
输入和“已保存凭据 / 未配置凭据”。仅显示已实现的认证方式,技术契约见
|
||||
[配置与请求](./http-paddleocr-vl-technical-design.md#配置与请求)。更改地址、临时选择无需
|
||||
认证或切回本地都不清除已保存密钥;清除使用显式操作。密钥不回填明文。
|
||||
|
||||
配置旁持续说明“需要 OCR 的页面或图片将发送到此服务;快速文本模式不自动上传,主动提取图片文字仍使用此服务”。不增加
|
||||
逐文件授权。“检查连接”是远程区域的次操作,成功文案为“服务可连接,解析能力需用文件
|
||||
测试”;显示检查时间,不把 health 成功呈现为图表、印章等模型已可用。
|
||||
|
||||
### 2.3 高级设置
|
||||
|
||||
高级设置用原生 `details/summary` 默认折叠,按用途分组,不铺开 API 字段墙。
|
||||
|
||||
| 分组 | 控件与文案 | 生效语义 |
|
||||
| --- | --- | --- |
|
||||
| 内容保留 | 过滤模式“服务默认 / 全部保留 / 自定义”;自定义内为“保留页眉文字、页眉图片、页脚文字、页脚图片、页码、脚注、侧栏文字”正向 Switch | “保留”开启代表对应标签不进入排除列表;服务默认与空列表不同,见 L-H2 |
|
||||
| 页面处理 | 方向分类、页面去扭曲、版面检测 | 各项原生选择框“服务默认 / 开启 / 关闭”,三态不能伪装成二态 Switch |
|
||||
| 内容识别 | 图表识别、印章识别、图片块文字识别 | 同上;持续显示“接口已声明,尚未实测”或带测试条件的验证信息 |
|
||||
| Markdown 格式 | 美化 Markdown、显示公式编号、格式化块内容、合并版面块 | 同上,不把未知服务默认显示成关闭 |
|
||||
| 跨页整理 | “合并跨页表格”“调整标题层级”共享 Switch,默认关闭 | 明确会增加重组请求,保留源页集合;结果无变化如实报告 |
|
||||
| 解析限制 | 最大页数、超时与现有适用的并发参数 | 显示单位和共享契约允许范围;本地单页超时与 HTTP 请求超时分开,不对未实现参数展示可保存控件 |
|
||||
|
||||
过滤项在文字和图片两个小组排列,标签使用正向“保留”。自定义从未编辑过时以全部保留
|
||||
初始化并标明这一行为;已有自定义值切换模式后保留,不能根据未知的服务默认猜开关位置。
|
||||
技术或已有配置含其他排除标签时以带名称的补充行显示并可恢复保留,例如 `text`;不能保存
|
||||
一次表单就丢掉未列在常用组的排除项。“恢复服务默认”改变草稿,保存后才生效。
|
||||
|
||||
正文引用的插图默认保存,不设置“自动发送全部插图”。保留页眉图片等内容过滤与保存已引用
|
||||
图片是两件事:被过滤且未引用的图片无需保存。高级能力证据仅描述相应部署和样例;没有证据
|
||||
仍可由用户明确配置并测试,错误指出关联选项,完整实现范围包含这些已约定能力。
|
||||
|
||||
## 3. 共享解析预览
|
||||
|
||||
### 3.1 框架与正文
|
||||
|
||||
头部显示文件名、来源范围、结果状态、“打开原文件”和关闭或返回。设置测试标为“测试结果”,
|
||||
聊天标明会话,知识库显示库名与 `ScopeBadge`。下一行固定正文、图片、详情三个 PageTabs,
|
||||
页签及直接包装层 `flex: 0 0 auto`,仅当前面板滚动。查看已存结果不发 OCR 请求。
|
||||
|
||||
正文面板按来源页或章节呈现 Markdown、HTML 表格及受管图片。页面导航显示真实页号;
|
||||
PPTX 显示“幻灯片 N”,没有分页的原生文档显示工作表或标题定位,不生成假页码。点击图片
|
||||
旁的“第 N 页”或“查看正文位置”切到正文并聚焦对应标题;正文图片的“在图片中查看”反向
|
||||
定位图片卡。跨页表格显示全部来源页链接,不把合表结果归给单一页。
|
||||
|
||||
“打开原文件”经 Main 打开保存的文件。聊天使用会话原件;知识库 managed 使用受管原件,
|
||||
reference 使用原路径。reference 原件失效时显示“原文件不可用”,已保存正文与图片仍可看。
|
||||
测试原件只在测试预览生命周期内使用,不承诺关闭预览后持久保留。旧附件没有解析资产时显示
|
||||
“此附件未保存解析结果”,提供重新添加文件入口,不显示空白成功或自动补跑 OCR。
|
||||
|
||||
### 3.2 图片与详情
|
||||
|
||||
图片面板显示“已保存 N 张”及缺失数量,按原始页、页内顺序排列。每卡含缩略图、可读名称、
|
||||
页码或定位、格式、尺寸和打开大图操作;图片使用 contain,不裁掉图表坐标轴。大图沿用共享
|
||||
整窗浮层规则,Escape 只关闭顶层并返回图片卡,不能误关闭整个设置。
|
||||
|
||||
正文中的缺图替换为具体说明,例如“第 3 页图片 2 未保存:服务仅返回链接”,图片面板保留
|
||||
不可选择的缺失项及原因。没有插图时显示“此结果没有提取图片”,说明原生解析器可能只提取
|
||||
文字;加载中、读取失败、没有图片和筛选无结果分别呈现。仅有图片的结果显示“仅图片,无
|
||||
可发送正文”,仍可预览并选择图片,不把空正文附件当成已就绪文本。
|
||||
|
||||
详情显示解析时间、实际工作流、Provider、无凭据的服务标识、原格式、总页数、OCR 页数、
|
||||
文字量、图片保存情况、耗时、生效选项、服务默认项以及按页警告。服务未提供置信度显示
|
||||
“未提供”。配置取自这次结果,不取当前设置;无法得知服务默认实际值时显示“服务默认,
|
||||
实际值未提供”。跨页整理分别显示请求状态、是否观察到变化和来源映射,HTTP 200 不等于
|
||||
“合并成功”。详情内不展示密钥、授权头、Base64 或原始错误响应。
|
||||
|
||||
### 3.3 测试与重解析
|
||||
|
||||
测试运行显示文件、场景、阶段、已完成页数(可确定时)和取消;未知进度使用不定进度条,
|
||||
不模拟百分比。测试期间设置禁止关闭,用户可先取消,结算后再关闭。测试失败就地保留原因
|
||||
和“重试”;有部分产物时可以查看,但不能标为成功。关闭测试预览后释放无引用测试资源。
|
||||
测试预览不提供“添加到会话”,防止诊断文件进入用户草稿或知识库。
|
||||
|
||||
“使用当前设置重新解析”是聊天草稿和知识库文档的次操作,点击前显示将使用的已保存来源。
|
||||
历史消息和队列冻结已接受的输入;其预览只读。历史消息使用“添加到当前草稿重新解析”,
|
||||
产生草稿附件而不改写已发送正文;队列修改使用“恢复到草稿”,避免原队列和修改稿重复发送。
|
||||
重解析期间旧结果仍可查看,顶部显示“正在重新解析,
|
||||
当前显示上次结果”;成功才替换,失败或取消保留旧结果及单独的本次失败说明。
|
||||
|
||||
## 4. 文档插图加入会话
|
||||
|
||||
### 4.1 选择与添加
|
||||
|
||||
聊天和知识库预览的图片卡提供原生 Checkbox,标签含文档名、页码和图片序号;默认零选择,
|
||||
打开预览不自动选图。点缩略图只放大,不勾选。页签切换保留本次勾选,关闭预览清除尚未
|
||||
添加的选择;已添加状态由目标草稿查询得到,不用 Checkbox 冒充已附加状态。
|
||||
|
||||
底部操作条显示目标会话及项目、待添加数量、已在草稿数量和“添加所选图片”。仅对未附加
|
||||
图片显示可勾选项;已附加卡显示“已在此会话草稿”与“从草稿移除”。取消勾选只取消本次
|
||||
待添加选择,不删除已经附加的图片。无选择时禁用添加并说明原因,无默认全选行为。
|
||||
|
||||
添加只将选中的图片复制为目标草稿中的视觉附件,不发送消息、不附带整份知识库正文、不
|
||||
自动启用知识库,也不重跑 OCR。成功后清除已提交勾选,显示已附加状态,提供“前往会话”
|
||||
并通过一次应用通知反馈;失败保留勾选和原草稿,操作条就地显示原因,不重复通知。
|
||||
|
||||
同一来源结果的同一 imageId 加入同一草稿是幂等操作。点击两次或从正文和图片面板重复
|
||||
添加都只产生一个附件;已发送后在新一条草稿中可再次选择。重新解析生成的新图片不按
|
||||
文件名、页内键或视觉相似度与旧图片合并,也不自动替换草稿中的旧副本。
|
||||
|
||||
### 4.2 目标会话
|
||||
|
||||
从聊天当前草稿打开时,目标为该草稿;从历史附件打开时,目标为当前可编辑草稿,头部明确
|
||||
区分来源消息与目标。知识库有有效活动会话时显示“添加到:会话名”,并保留其草稿文字。
|
||||
浏览知识库不会自动改变活动会话或把知识库归属当作目标项目。
|
||||
|
||||
没有可编辑活动会话、会话已删除或当前为不可编辑消息通道时,保留图片选择,操作条显示
|
||||
“选择会话后添加”,提供“选择会话”和“新建会话”。选择复用应用的项目与最近会话导航信息,
|
||||
新建沿用现有 `startNewConversation` 的项目限制与空会话复用规则;该函数当前会切到聊天,
|
||||
实现时须补齐目标会话返回和预览恢复接线,不能直接调用后假定选择仍在。需要先选可创建项目时在同一流程
|
||||
显示原因和项目入口。新建或选择后回到预览核对目标,用户点击添加才复制,不自动发消息。
|
||||
取消目标选择保留图片选择,不创建空附件。
|
||||
|
||||
提交时冻结显示的目标 conversationId。异步期间切换到其他会话不改变归属,完成提示带原
|
||||
目标名称;目标已删除则失败并清理本次复制。重新打开预览按当前目标重新计算已附加状态。
|
||||
|
||||
### 4.3 发送与移除
|
||||
|
||||
选出图片的卡片显示“文档名 · 第 N 页 · 图片 M”,副标题为“文档插图 · 图片输入”。来源
|
||||
跳转在原结果仍存在时可用;原结果删除后仍显示来源文字,副本照常打开与发送。
|
||||
|
||||
当前草稿本来含父文档时,父文档只提供一次解析文本,选出的图片分别占普通图片附件位置;
|
||||
不为每张图片再次拼入父文档全文。移除父文档不级联删除已复制图片,反馈“已移除文档,
|
||||
所选图片仍在草稿中”;移除图片不删父文档、不改知识库。移除只释放对应草稿引用,已发送
|
||||
或队列仍引用的资源保留。数量按父文档和每个图片附件分别计数。
|
||||
|
||||
添加前和发送前均核对共享附件数量、容量和本次模型与 Runtime 图片能力,详见
|
||||
[L-I3 至 L-I5](./logic-design.md#选图与发送)。超限拒绝整个本次选图批次并保留选择,列出
|
||||
当前占用和可调整项,不能只取前几张。能力不匹配不丢图:草稿可保留图片,发送阻止并给出
|
||||
“切换模型或 Runtime”“提取图片文字”“移除图片”。切换入口返回后保留草稿并重新校验。
|
||||
活动回复期间沿用已有模型选择锁定规则,图片仍可进入下一条草稿,不能借预览绕开该锁定。
|
||||
能力失配的队列项沿用队列的可恢复错误呈现,提供“恢复到草稿”供修改。恢复合并保留原
|
||||
草稿,超限时队列与草稿均不改变并提示先整理草稿;具体合并规则见 L-H7。
|
||||
|
||||
## 5. 普通图片与文档附件状态
|
||||
|
||||
普通图片默认显示原图缩略图、“图片输入”及“提取图片文字”次操作。是否允许发送由实际
|
||||
生效模型和 Runtime 共同决定,未知能力显示“尚未确认图片输入能力”,不能当作支持。
|
||||
支持视觉也允许明确 OCR。OCR 中显示“正在提取文字”和取消,成功后同一附件变为
|
||||
“原文件名 · 提取文字”,持续显示“仅发送提取文字,原图保留”,可查看正文和打开原图。
|
||||
原始文件名不被磁盘重命名;来源与发送方式独立保存。“改为发送原图”恢复图片模式并重新
|
||||
校验能力,不需要重新 OCR。失败、取消或没有文字时保持原先发送方式,不声称转换完成。
|
||||
文档选出的图片执行 OCR 时采用相同交互,并保留文档、页码、图片序号来源。
|
||||
|
||||
| 附件状态 | 显示及操作 | 发送条件 |
|
||||
| --- | --- | --- |
|
||||
| 读取、解析、校验、保存中 | 文件名、阶段、批次序号、已知页数、取消 | 本批完成前阻止发送及重复导入,草稿文字可继续编辑 |
|
||||
| 完整结果 | “解析完成”、页数、文字量、已保存图片数;查看解析结果、打开原件、移除 | 持久化和能力校验通过 |
|
||||
| 部分结果 | “部分解析:第 N 页 OCR 失败”等持续提示;查看、重试、移除 | 仅逻辑允许的自动聊天结果可发送,发送内容携带不完整说明;不弹重复确认 |
|
||||
| 仅图片结果 | “仅图片,无可发送正文”;查看和选择图片 | 选择至少一张该结果的图片或其提取文字后可发送;父文档保留来源并占附件位置,但不生成空文本上下文;也可移除父文档后单独发图片 |
|
||||
| 失败或取消 | 具体原因或“已取消”;重试、移除 | 不可发送该项;批次回滚后保留失败摘要和重新选择入口 |
|
||||
| 中断后重启 | “解析已中断”;从已保存原件重试,原件未保存则重新选择 | 不自动恢复 HTTP 推理,不把未完成状态变成成功 |
|
||||
| 旧附件缺少资源 | “此附件未保存解析结果”;重新添加文件 | 不将旧元数据当成新的可恢复资源 |
|
||||
|
||||
导入仍遵守既有批次回滚:失败或取消时移除本批新增的未提交附件,保留之前草稿。失败摘要
|
||||
不是可发送卡片。已添加附件重解析失败则保留其旧卡片,不适用首次导入回滚。取消停止后续
|
||||
客户端请求;文案不承诺服务端 GPU 任务立即停止。取消生成与取消解析不同,前者不删除
|
||||
已提交附件。入队保存失败保留草稿,队列显示不得早于资源持久化完成。
|
||||
|
||||
## 6. 知识库与生命周期呈现
|
||||
|
||||
DocumentsView 的文档操作增加“查看解析结果”,在新增文档详情区域呈现共享预览,保留来源、
|
||||
同步、任务和 chunk 管理入口。图片预览不建立图片向量索引。正文页码与现有 chunk 来源
|
||||
定位相连,图片选择不依赖该库是否已启用聊天检索。
|
||||
|
||||
完整索引或高保真缺页、缺图、重组失败时,本次任务标失败;旧文档仍可检索并显示“上次
|
||||
结果仍可用”。首次导入失败没有可用旧结果时显示失败与重试,不能出现已索引成功标记。
|
||||
删除文档或来源沿用危险操作确认,说明知识库派生资源会清理;已经复制到会话的图片不受
|
||||
影响。删除聊天会话遵循会话删除流程,不在图片预览再实现平行删除系统。
|
||||
|
||||
草稿和队列恢复后仍能打开原图与解析结果。移除队列项仅释放该项引用,取消生成保留消息
|
||||
附件;资源清理结果不能仅凭卡片消失验收,文件及数据库检查见验收矩阵。
|
||||
|
||||
## 7. 响应式与无障碍
|
||||
|
||||
按容器可用宽度响应,助手工作栏展开后不能只看窗口宽度。宽布局图片格多列,窄布局单列,
|
||||
操作条换行;知识库按根规范在窄窗口转为列表和详情单面板,并提供返回列表。设置继续使用
|
||||
既有左右分类 Modal;预览 Modal 使用共享尺寸与外边距,不另设全屏页面。正文表格独立
|
||||
横向滚动,不撑宽 Composer 或 Modal。200% 缩放时目标会话、警告、取消和发送仍可达。
|
||||
|
||||
PageTabs 保留 tablist、tab、tabpanel、aria-selected、游标焦点和方向键/Home/End。
|
||||
Switch 用 `role="switch"`,图片选择保留 checkbox 语义;图片卡内选择与放大为独立焦点。
|
||||
表单字段有持久标签,错误用 aria-describedby 关联;进度使用 polite 实时区域,只播报
|
||||
阶段和有意义的页数变化,失败使用 alert。图片替代文字描述来源,不凭空生成内容描述。
|
||||
|
||||
Modal 初始聚焦关闭按钮,确认删除初始聚焦取消。Tab 循环、背景隔离、顶层 Escape 和焦点
|
||||
返回使用既有辅助函数;触发卡片被删除时返回附件区或文档列表的稳定入口。整窗遮罩纳入
|
||||
共享 no-drag 选择器,原生浏览器 viewport 使用既有遮挡机制,不靠提高 z-index。
|
||||
主题和 reduced-motion 遵循全局设置,不仅凭颜色区别部分、失败与完整结果。
|
||||
|
||||
## 8. 完整验收矩阵
|
||||
|
||||
以下为产品验收要求,是否通过及验证层次以进度记录为准。场景细节见
|
||||
[User Stories](./user-stories.md),实现验证路径见 [技术设计](./http-paddleocr-vl-technical-design.md#实施与验证顺序)。
|
||||
|
||||
| 用例 | 覆盖与可观察结果 | 追溯 |
|
||||
| --- | --- | --- |
|
||||
| A-01 配置与升级 | 旧配置继续本地;来回切换保留两套配置及凭据;不安装本地模型即可用 HTTP;保存失败、未保存测试和离开保护可恢复 | FR-H1、H11、H12;US-H1、H8、H12 |
|
||||
| A-02 工作流 | 聊天/知识库混合 PDF、图片型 PPTX、快速模式、高保真及纯图片分别走约定路径,保留真实页码 | FR-H2、I1、I4;US-H2、H8、I1、I6 |
|
||||
| A-03 高级参数 | 页眉页脚正向开关、服务默认、空排除列表、其他标签往返不丢失;声明与实测分开;未知选项或模型缺失明确报错 | FR-H4、H7;US-H4、H9 |
|
||||
| A-04 跨页整理 | 用能触发合并的样例验证表格、标题和 sourcePages;无变化、映射失败均如实显示,不把请求成功当质量通过 | FR-H5;US-H5 |
|
||||
| A-05 预览 | 三入口共用正文/图片/详情;同名图片键不串页;HTML img/table、页码互跳、原件打开、历史配置、置信度缺省正确 | FR-H3、H7、H11、H12;US-H3、H12、H14 |
|
||||
| A-06 失败与取消 | 401/429/5xx、超时、损坏/超限文件、URL 图片、缺页缺图、磁盘保存失败;自动聊天部分与严格工作流失败不同;取消停止后续请求且可重试 | FR-H3、H6、H9;US-H6、H7、H15 |
|
||||
| A-07 显式 OCR | 支持视觉仍可提取文字;成功改名并明确仅发文字、原图可开;失败/取消/空文字保持原方式;改回原图重验能力 | FR-I1、I2、I3;US-I2、I3、I11 |
|
||||
| A-08 文档选图 | 零默认选择、勾选与已附加区分、重复添加幂等;父文档文字仅一次;父文档/图片独立移除;重新解析不替换已复制图片 | FR-I5;US-I7、I8 |
|
||||
| A-09 知识库选图 | 有活动会话、无会话、不可编辑通道、选择/新建取消、异步切会话;只加入明确目标草稿且不自动发消息或启用检索 | FR-I5、H10;US-I9 |
|
||||
| A-10 能力与限制 | 模型支持但 Runtime 不支持、未知能力、切换后失配、队列恢复失配;普通附件与选图共用数量/容量检查,整批超限不静默截取 | FR-I1、I5;US-I2、I10 |
|
||||
| A-11 持久化 | 发送/入队/草稿后重启,外部原文件移走,正文/原图/图片仍可开;解析中重启显示中断;入队失败保留草稿 | FR-H8、H9、I3;US-H10、H15、I4、I5 |
|
||||
| A-12 替换与清理 | managed/reference 重解析失败保留旧 chunks;成功替换;移除草稿、队列、会话、项目、库来源均核对文件和 DB;KB 删除不损坏会话副本,其他有效引用保留 | FR-H9、H10、H11、I5;US-H11、H13、I8、I9 |
|
||||
| A-13 测试隔离 | 测试不创建会话/知识记录,不提供选图入草稿;取消文件选择无副作用,关闭预览清理测试资产 | FR-H7、H12;US-H14 |
|
||||
| A-14 界面质量 | 浅/深主题、宽/中/窄窗口与工作栏压缩、200% 缩放、纯键盘、嵌套 Modal、浏览器 viewport 遮挡、短窗口页签不收缩 | FR-H12、I5;US-H14、I12 |
|
||||
| A-15 菜单与返回 | 全局解析配置与本机监控分离;附件菜单随草稿/历史/队列状态变化;设置直达返回保留选择;知识库“用于当前对话”不替代选图,新建目标后恢复预览 | FR-H1、H11、H12、I2、I5;US-H16、I9 |
|
||||
|
||||
跨平台、真实本地模型和远端 Runtime 的执行证据按现有仓库规则记录。Mock、静态稿和
|
||||
健康检查均不能替代完整产品入口验收。
|
||||
@@ -0,0 +1,44 @@
|
||||
# HTTP OCR 用户场景
|
||||
|
||||
本文件覆盖 [HTTP 扩展](./prd.md#13-http-paddleocr-vl-扩展规划) FR-H1 至 FR-H12 及
|
||||
[图片路由](./prd.md#322-纯图片与文档的路由规划) FR-I1 至 FR-I5。当前实现及证据见
|
||||
[生产实现](./implementation.md)和[进度](./progress.md#2026-09-19-实现与验证),未执行的场景不视为通过。
|
||||
|
||||
| ID | Given / When / Then |
|
||||
| --- | --- |
|
||||
| US-H1 | 给定设备没有本地 OCR 模型,用户保存可用 HTTP 服务并测试扫描 PDF,应返回逐页文字和图片;切回本地来源时再检查本地模型。对应 FR-H1、FR-H7。 |
|
||||
| US-H2 | 给定包含文本页和扫描页的 PDF,用户以自动聊天或完整索引方式导入,仅需要 OCR 的页进入所选 Provider,结果保留原始页码;快速模式不上传,高保真按全页处理。对应 FR-H2。 |
|
||||
| US-H3 | 给定两页有相同图片键的文档,解析完成并重开聊天或知识库时,两页图片均可查看且来源正确;删除一份文档不损坏另一份的图片。对应 FR-H3。 |
|
||||
| US-H4 | 给定页眉包含合同编号,用户选择保留页眉页脚后再次测试,能看到原先被默认过滤的内容;恢复服务默认或设置空排除列表的含义不同且可辨认。对应 FR-H4。 |
|
||||
| US-H5 | 给定两页连续表格,用户启用重组后可检查表格结果及来源页集合;合并错误、无变化或无法关联页码时,测试信息如实显示。对应 FR-H5、FR-H7。 |
|
||||
| US-H6 | 给定 HTTP 返回 401、429、5xx 或超时,导入提示对应错误;用户修正设置后可重试。解析期间取消则停止后续请求并清理本次未提交图片,已有草稿与索引保留。对应 FR-H6。 |
|
||||
| US-H7 | 给定服务只返回图片 URL,客户端不访问该 URL,明确报告图片未保存;自动聊天可按逻辑规则保留文字及警告,知识库完整索引和高保真不可将其当作完整结果。对应 FR-H3、FR-H6。 |
|
||||
| US-H8 | 给定现有本地设置和图片型 PPTX,升级后默认仍走本地;主动选择 HTTP 后识别直接内嵌图片并标注“幻灯片 N · 图片 M”,不把图片响应顺序当作幻灯片号。对应 FR-H1、FR-H2。 |
|
||||
| US-H9 | 给定高级开关在 OpenAPI 中存在但对应模型未安装,启用测试时报告服务错误和选项名称,不以连接成功代替能力验证,也不静默忽略用户设置。对应 FR-H4、FR-H7。 |
|
||||
| US-H10 | 给定文档附件已发送或被发送队列接受,用户关闭应用并删除外部原文件,再启动后可打开保存的原文件、正文及图片,队列可恢复附件内容;取消生成不会删除附件。对应 FR-H8、FR-H9。 |
|
||||
| US-H11 | 给定草稿、已发送会话和知识库分别持有文档,移除草稿或取消导入只清理本次暂存;删除会话只清理无其他引用的资源,知识库图片仍可查看。对应 FR-H9、FR-H10。 |
|
||||
| US-H12 | 给定本地与远程均有配置,切换来源后配置仍在;修改高级选项时须保存后才能测试,结果可切换正文、图片和详情视图。再改配置并打开历史附件时,旧结果及其所用选项不变。保存失败保留草稿,关闭或切分类遵循未保存保护。对应 FR-H11、FR-H12。 |
|
||||
| US-H13 | 给定已有聊天草稿解析结果或 managed/reference 知识库文档,重解析失败或取消后旧正文和图片仍可查看,知识库旧 chunks 仍可检索;成功后才替换。历史消息及已入队内容不会被替换,再解析产生新草稿。删除知识库来源清理无引用派生资源,不误删外部原文件。对应 FR-H10、FR-H11。 |
|
||||
| US-H14 | 给定测试、聊天、知识库中同一类解析结果,用户查看正文、图片和详情,页码及图片可互相定位,HTML 表格可读,缺失置信度显示未提供;打开原件遵循各自归属。测试不提供选图入会话,关闭预览后清理测试资源,reference 原件缺失仍可看保存的正文和图片。对应 FR-H3、FR-H7、FR-H12。 |
|
||||
| US-H15 | 给定导入或保存中断、磁盘写入失败、无文字或部分 OCR 失败,界面区分失败、中断、仅图片及部分结果;重启可从保存的原件重试,未保存则提示重选。自动聊天可靠文本可带警告发送,完整索引和高保真不替换旧结果;失败批次回滚且原草稿保留。对应 FR-H6、FR-H9、FR-H12。 |
|
||||
| US-H16 | 给定从聊天或知识库预览进入“前往文档解析设置”,应直达同一全局分类,关闭后保留原选择、目标和草稿;保存不自动重解析或发送。关闭本机推理监控应用不禁用解析,HTTP 来源无需加载本地模型。知识库“用于当前对话”继续选择检索范围,“添加所选图片”只复制图片;新建目标后回到预览,明确点击添加才改变附件。历史附件不可就地改写,队列修改须先恢复到草稿。对应 FR-H1、FR-H11、FR-H12、FR-I2、FR-I5。 |
|
||||
|
||||
## 图片与草稿验收
|
||||
|
||||
| ID | Given / When / Then |
|
||||
| --- | --- |
|
||||
| US-I1 | 给定本次模型和 Runtime 支持图片输入,用户添加普通图片并发送,模型收到图片输入,不调用 OCR;如为请求生成缩放或转码副本,历史附件仍可打开原图。对应 FR-I1、FR-I3。 |
|
||||
| US-I2 | 给定实际模型或 Runtime 不支持图片输入,用户发送图片时收到明确提示,可选择提取文字或切换视觉模型;未作选择前不自动 OCR,也不声称模型已看到原图。切换后按实际能力重新判断。对应 FR-I1。 |
|
||||
| US-I3 | 给定聊天模型支持视觉,用户仍明确选择“提取图片文字”,系统调用已保存 OCR;成功后附件显示“原文件名 · 提取文字”和“仅发送提取文字,原图保留”,请求只有提取文字。失败时原图、草稿及原发送方式保留,允许重试。对应 FR-I2。 |
|
||||
| US-I4 | 给定纯图片消息已发送或入队,关闭应用并移除外部原文件后重开,原图仍可查看,队列可恢复;没有执行 OCR 的图片不要求存在解析正文或清单。对应 FR-I3。 |
|
||||
| US-I5 | 给定可恢复草稿引用暂存附件,应用退出再启动时不因临时目录清理丢失附件;移除该引用后仅清理无其他引用的资源。对应 FR-H9、FR-I3。 |
|
||||
| US-I6 | 给定同一知识库工作流,切换聊天模型是否支持视觉不会改变图片文字提取和索引策略;PDF、Office 仍遵循文档解析规则。该场景需在相应导入格式实现后验收。对应 FR-I4。 |
|
||||
| US-I7 | 给定文档已保存多张提取图片,打开预览为零选择,不发模型请求。勾选图片后点击添加,目标会话草稿出现带文档名、页码、图片序号的视觉附件;关闭前未添加的勾选不算附件,重新打开按草稿显示已附加状态。重复点击只添加一次,不自动全量发送。对应 FR-I5。 |
|
||||
| US-I8 | 给定草稿含父文档及从中选出的图片,发送时父文档全文只加入一次,图片分别作为图片输入;移除父文档后图片仍在,移除图片不影响文档。重解析父文档不会替换已复制图片,已发送图片及来源信息重启可用。同一图片移除后可再加,发送后可加到新草稿。对应 FR-I5、FR-H9。 |
|
||||
| US-I9 | 给定知识库预览有选择,存在可编辑活动会话时显示其名称并添加到该草稿;无会话或当前通道不可编辑时,可选择或新建会话,取消保持选择。添加中切换会话不改变目标,目标被删除则失败且不留下新副本。添加不启用知识检索、不带整篇文档、不自动发消息;源文档重解析或删除后会话图片仍可用。对应 FR-I5、FR-H10。 |
|
||||
| US-I10 | 给定草稿已有附件,选图批次超过共享数量或容量上限时整批拒绝且保留选择和草稿;重复项不重复占位。添加后切换到不支持图片的模型或 Runtime,发送明确阻止并允许切换、提取文字或移除。队列重启后实际能力不匹配同样保留资源并提供恢复到草稿修正,不静默丢图。对应 FR-I1、FR-I5。 |
|
||||
| US-I11 | 给定普通图片或文档插图正在 OCR,取消、失败或空文字不会改变发送方式;成功后可打开保留的原图并“改为发送原图”,按新能力重新校验。改显示名不改原始文件名,插图仍保留文档和页码来源;重试按当前保存配置且不破坏旧结果。对应 FR-I2、FR-I3、FR-I5。 |
|
||||
| US-I12 | 给定窄输入区、知识库单面板或嵌套预览,用户仅用键盘可切页签、勾选、放大、添加、取消和关闭;Escape 只处理顶层,关闭后焦点恢复。浅深主题、200% 缩放、工作栏展开及原生浏览器同时显示时,目标、状态和主操作仍可见可用。对应 FR-H12、FR-I5。 |
|
||||
|
||||
首次配置、成功解析、失败重试和取消均须从产品入口验收;独立脚本结果只证明指定部署的
|
||||
接口行为。图片保留不等于后续文本模型会自动接收图片字节,也不等于知识库支持图片检索。
|
||||
@@ -10,6 +10,8 @@ FastGPT/RAGFlow 完整 App 与专属配置、真实远程预检索仍待验收
|
||||
|
||||
## 文档导航
|
||||
|
||||
后续与监督者的关系:故事线图谱引用现有知识库资料,补充工作事件与知识演变视图;计划支持双向定位和预览后补充本地条目,不复制资料正文、不改变外部知识库只读边界。该集成尚未实现,权威定义见[监督者产品设计](../conversation-supervision/supervisor-prd.md) FR-S7 至 FR-S9。
|
||||
|
||||
| 文档 | 权威职责 |
|
||||
| --- | --- |
|
||||
| [知识库检索与分块增强 PRD](./prd.md) | 检索、分块、诊断和产品验收 |
|
||||
@@ -29,6 +31,13 @@ FastGPT/RAGFlow 完整 App 与专属配置、真实远程预检索仍待验收
|
||||
| [2026-09-13 产品验证摘要](./external-knowledge-validation-2026-09-13.json) | 生产服务与本机 HTTP MCP、Dify 桌面、凭据重开、调用计数、最终自动检查状态及远程阻断的脱敏证据 |
|
||||
| [2026-09-13 UI 与真实生成复验](./external-knowledge-follow-up-2026-09-13.json) | 后续 UI 修正、三家生产 IPC 生成、Dify 完整 App 回答与引用、视觉 fixture 及本轮请求计数 |
|
||||
|
||||
## 模型图谱抽取
|
||||
|
||||
图谱抽取不设置独立的输出 token 预算。OpenAI Chat Completions 和 Responses
|
||||
请求省略输出上限参数,由模型服务决定;Anthropic 必填的 `max_tokens` 沿用默认文本
|
||||
模型连接的最大输出配置,未配置时使用应用共享协议默认值,不再固定为 8192。
|
||||
服务端仍可能因自身限制返回长度截断;既有超时、取消、响应大小边界和失败诊断保持不变。
|
||||
|
||||
## 术语
|
||||
|
||||
- **外部实例**:一个由用户配置的 Dify、FastGPT 或 RAGFlow 服务地址及其认证信息。
|
||||
|
||||
@@ -16,6 +16,14 @@
|
||||
|
||||
## 验证证据
|
||||
|
||||
### 2026-09-20 清理孤立的内存图谱搜索
|
||||
|
||||
- 删除仅剩测试调用的 `searchGraph`、专属评分/遍历辅助函数、搜索上限及测试 fixture。
|
||||
实际检索仍通过 `KnowledgeDatabase.hybridSearchWithDiagnostics` 与 `graphSearchScored`;
|
||||
图谱抽取、归一化、合并、数据库结构和已发布迁移均保持不变。
|
||||
- `graph-extractor.test.ts` 与 `knowledge-database.test.ts` 合计 55 项通过,保留合并时
|
||||
丢弃失去端点关系的回归;没有调用外部模型。
|
||||
|
||||
### 2026-09-13 知识库布局与自动名称修复
|
||||
|
||||
- 创建类型的共享分段控件被长表单压缩,修复共享不收缩规则,不添加页面专属高度。
|
||||
|
||||
@@ -1,18 +1,79 @@
|
||||
# Magic Notes
|
||||
|
||||
Magic Notes is the local notes, checklist and AI-comment workbench. Desktop
|
||||
actions and authorized Agent/MCP tools write to the same desktop SQLite store.
|
||||
actions and authorized Agent/MCP tools use the same desktop store: SQLite for
|
||||
metadata and indexes, and local files for entry bodies and binary assets.
|
||||
The open workbench subscribes to successful writes so users can see external
|
||||
changes without leaving the page or clicking a refresh button.
|
||||
|
||||
The workbench opens on a responsive note-card overview. Opening a note replaces
|
||||
the overview with a continuous record stream, a narrow left index and independent
|
||||
right AI comments. Both side panes can collapse. Index items show one real
|
||||
first-page canvas thumbnail with page count, or a text summary; clicking scrolls
|
||||
without replacing the editor or losing a draft. Opening a note shows a blank composer
|
||||
above the history, with text/canvas selection and Save. Creating a record resets
|
||||
that composer; editing a saved record requires its explicit Edit action. The separate To-dos view
|
||||
keeps search, status filters and grouped tasks on one page, with direct completion
|
||||
and a stable left list/right detail layout for instructions, source content and AI
|
||||
comments. The divider can be dragged and remembers its width; each note group
|
||||
can independently collapse its tasks while retaining its title and count.
|
||||
Narrow containers use on-demand detail with an explicit return to the
|
||||
mounted list. One content-sized button switches to the other view in the page
|
||||
header immediately before New note; status filters sit beside search and wrap
|
||||
when needed. Note detail removes the surrounding frame and stream padding while
|
||||
keeping editor borders and the AI divider. Returning from a source note restores the task
|
||||
context and protects unsaved editor changes. A successful save followed by no
|
||||
further edits does not prompt again on return. Narrow note layouts use an index
|
||||
drawer and bounded bottom AI pane.
|
||||
|
||||
## Documents
|
||||
|
||||
- [Technical design](./technical-design.md): database notifications, process
|
||||
boundaries, refresh lifecycle and regression coverage.
|
||||
- [Technical design](./technical-design.md): file storage, schema 38 migration,
|
||||
backup boundaries, database notifications, process boundaries, canvas and AI
|
||||
contracts, refresh lifecycle and regression coverage.
|
||||
- [Progress](./progress.md): implementation and validation evidence.
|
||||
- [UI design system](../../../UI-DESIGN.md#137-魔法笔记): authoritative workbench
|
||||
interaction rules, including selection and draft preservation.
|
||||
- [Canvas integration](../../../src/renderer/src/magic-canvas/README.md): React
|
||||
controller API, PeopleLib core limits, asset adaptation and rendering lifecycle.
|
||||
|
||||
An entry is a saved rich-text record within a note. A todo is a checklist item
|
||||
derived from an entry. An unsaved draft belongs to the open editor; it is not
|
||||
An entry is a saved rich-text or paged-canvas record within a note. Its body lives
|
||||
in `notes/<noteId>/entries/<entryId>.json` beside note-local binary assets;
|
||||
`note.json` is a derived membership manifest. SQLite retains metadata, indexes,
|
||||
revisions, todos and comments. API reads hydrate assets back into the existing
|
||||
content format. A todo is a checklist item derived from rich text
|
||||
or the canvas Quill flow. An unsaved draft belongs to the open editor; it is not
|
||||
written to storage by a background refresh.
|
||||
|
||||
Backups must coordinate SQLite and the adjacent `notes/` tree while note writes
|
||||
are paused or the app is closed. A database-only backup cannot restore these
|
||||
bodies. There is no product one-click backup action; see the
|
||||
[backup contract](./technical-design.md#backup-and-restore).
|
||||
|
||||
## Canvas Scope
|
||||
|
||||
The integrated PeopleLib editor uses Fabric for paged annotations and Quill for
|
||||
body text flowing across pages. It supports paper templates, pen/highlighter,
|
||||
object erasing and selection/transforms, floating text, images, PDF backgrounds
|
||||
with native text extraction, and raster PDF export. Saves are manual. Undo/redo
|
||||
belongs to the current annotation page or Quill body mode; it is not a global
|
||||
document history. There is no infinite canvas or PNG download button.
|
||||
Editors and saved viewers support view zoom and responsive Fit width without
|
||||
changing saved content or export dimensions; see the
|
||||
[canvas zoom reference](../../../src/renderer/src/magic-canvas/README.md#view-zoom).
|
||||
|
||||
Drafts, saved entries and todos sourced from canvases support AI comments using
|
||||
the default model's image-input capability. The Canvas pages to send setting
|
||||
selects the first 1-8 pages in current canvas order, defaulting to 1. It counts
|
||||
pages in one canvas, not entries, and leaves full-canvas saving and export intact.
|
||||
Image-capable models receive complete captures and extracted text for that prefix.
|
||||
Text-only models receive the same page range as an explicitly labelled
|
||||
text fallback; a purely visual canvas entry or draft reports that an image-capable
|
||||
default model is required. Canvas comments are requested manually in `immediate`
|
||||
mode, or after a manual save in `after-save-auto` mode. Analysis failure does not
|
||||
undo a successful save. Detailed retention rules are in the
|
||||
[AI contract](./technical-design.md#canvas-analysis-and-comments).
|
||||
|
||||
Canvas implementation is complete and final validation is in progress. The
|
||||
[progress record](./progress.md) distinguishes confirmed production-path evidence
|
||||
from the pending final repository checks.
|
||||
|
||||
@@ -1,5 +1,713 @@
|
||||
# Magic Notes Progress
|
||||
|
||||
## 2026-09-24: Resizable Todo List and Collapsible Note Groups
|
||||
|
||||
- Todo list/detail uses a 1px divider with a transparent 9px pointer target and
|
||||
8px content padding on each side. Dragging and keyboard resizing persist the
|
||||
list width; narrow layouts retain the saved desktop preference.
|
||||
- Source-note headings independently collapse their task rows while retaining
|
||||
the count and selected detail. Buttons support native keyboard activation.
|
||||
- Focused workspace, disclosure and Electron layout tests passed: 3 files,
|
||||
115 tests. Native Electron input verified dragging, keyboard expansion,
|
||||
width restoration after reload and narrow-window transitions. Typecheck and
|
||||
lint passed. A full-suite validation attempt was cancelled; no full-suite
|
||||
success is claimed for this change.
|
||||
|
||||
## 2026-09-20: Shared Inline Help
|
||||
|
||||
The canvas page-count question mark now uses `InlineHelp`, superseding the native
|
||||
`title` mechanism recorded below. The overview description, comment-direction
|
||||
help and new text-entry hints also use the shared component. Interaction rules
|
||||
are maintained in [the UI design system](../../../UI-DESIGN.md#614-inlinehelp-同行帮助).
|
||||
|
||||
The final verification ran `npm test -- --reporter=default --reporter=json
|
||||
--outputFile.json=<temporary-report>`: **412 files passed, 9 skipped; 4,858 tests
|
||||
passed, 67 skipped, no failures**, in 1,016.17 seconds. The JSON report confirms
|
||||
`success: true`. `npm run typecheck` and `npm run lint` passed. The focused help,
|
||||
application, SSH, Skills and MCP run also passed **6 files / 54 tests**. SSH password
|
||||
help now keeps authentication and encrypted-storage consequences visible; Skills
|
||||
and MCP headers no longer retain an empty description paragraph. Pin help and
|
||||
disable consequences use separate Chinese and English translation keys.
|
||||
|
||||
`tests/inline-help.electron.test.ts` renders production `ApplicationCenter` /
|
||||
`ApplicationSettingsView`, `SettingsPanel`, retrieval and activity components with
|
||||
fixture data. Native Electron input verified hover, focus, click pinning, Escape
|
||||
isolation, outside dismissal and Modal ownership. Chromium AX checks confirmed
|
||||
unique description IDs and accessible text before, during and after expansion.
|
||||
Light/dark checks cover 1280x800 and 640x480 consumer views, plus 1000x700 and
|
||||
360x320 shared-component views. Computed styles and captured screenshots confirm
|
||||
that canvas-count and pin help match model settings: 28px buttons, 14px icons,
|
||||
4px label gaps, zero center offset, identical button colors/margins and tooltip
|
||||
typography/padding/borders in each theme. No CSS difference was reproduced, so
|
||||
this pass adds comparison assertions without changing styles.
|
||||
|
||||
The focused run retained 28 screenshots and `evidence.json` under the temporary
|
||||
`inline-help-final` artifact directory selected by `GOODBUDDY_HELP_ARTIFACTS`.
|
||||
This validates real Electron rendering with fixture services, not a packaged
|
||||
full App or live provider. Help actions triggered zero consumer operations and
|
||||
zero model calls. The full suite also passed both production canvas-analysis
|
||||
Electron cases with substituted model output; live-provider checks remain opt-in.
|
||||
|
||||
## 2026-09-19: Compact Page Count Setting
|
||||
|
||||
- The page-count select now uses the existing `.field` settings control styling.
|
||||
A title-adjacent `CircleHelp` icon uses the shared `icon-button` and native
|
||||
`title` hint; the full description is screen-reader-only and remains associated
|
||||
with both controls through `aria-describedby`. The compact row uses flex and
|
||||
keeps the label and help icon together with inline-flex, including at narrow
|
||||
widths. Control styling and the native hint mechanism remain shared. Page
|
||||
selection and persistence behavior are unchanged.
|
||||
- `npx vitest run src/renderer/src/ApplicationCenter.test.tsx` passed 14 tests.
|
||||
Focused ESLint and a temporary TypeScript configuration rooted at
|
||||
`ApplicationCenter.tsx` with its imported dependencies passed. No full suite,
|
||||
full typecheck, Electron/live-model test or model request was run in this UI pass.
|
||||
- The commit preparation had only inspected status, diffs and history. The index
|
||||
remained empty and HEAD remained `ecf09f4`; no staging or commit was performed.
|
||||
The later same-line adjustment passed the same 14-test UI suite before commit.
|
||||
- The coordinating agent subsequently reported full typecheck and lint passing.
|
||||
The first full test command timed out at 200 seconds; the second completed but
|
||||
exposed only the RUN banner without a summary. No full-suite pass is claimed.
|
||||
|
||||
## 2026-09-19: Canvas Pages to Send
|
||||
|
||||
- Implemented the page-count preference and input selection described in
|
||||
[Canvas Analysis and Comments](./technical-design.md#canvas-analysis-and-comments).
|
||||
Application settings and configuration tools persist the same field; historical
|
||||
missing values read as 1. All three production analysis handlers use the saved
|
||||
count for text and images. Saving retains the complete canvas.
|
||||
- Focused verification passed **15 files / 271 tests**:
|
||||
`npx vitest run src/main/application-settings-store.test.ts src/main/magic-notes/magic-note-analyzer.test.ts src/main/magic-notes/canvas-ipc.integration.test.ts src/renderer/src/ApplicationCenter.test.tsx src/renderer/src/MagicNotesWorkspace.test.tsx src/renderer/src/MagicCanvasEditor.test.tsx src/renderer/src/MagicCanvasThumbnail.test.tsx src/renderer/src/magic-canvas src/shared/goodbuddy-config-contracts.test.ts src/shared/magic-note-pages.test.ts src/main/goodbuddy-config-service.test.ts src/renderer/src/UpdateSettingsSection.test.tsx`.
|
||||
Coverage includes 1/8 boundaries, invalid counts, missing historical fields,
|
||||
restart persistence, settings events, config-tool writes, 50-page source
|
||||
preservation, current page-array order, selected PDF/object/flow text, missing
|
||||
flow text and selected pages with no readable text.
|
||||
- `npx vitest run tests/magic-notes-analysis.electron.test.ts` passed **2 tests**,
|
||||
one per image capability. The real settings form changes the default from 1 to
|
||||
2 through production preload/IPC and the file-backed settings store. Workspace,
|
||||
Quill and Fabric then analyze saved entries, edited saves, new drafts, new saves
|
||||
and canvas-source todos through registered IPC/analyzer/SQLite. Explicit page
|
||||
breaks and automatic overflow both exclude later-page text. Browsing page three
|
||||
at 75% zoom still sends pages one and two. This exposed scaled DOMRect rounding
|
||||
at column starts; the corrected boundary retains each page's first character.
|
||||
- `npx vitest run src/renderer/src/App.test.tsx -t application` passed **5 tests**
|
||||
with 279 unrelated cases filtered out. Focused ESLint and `git diff --check`
|
||||
passed. Full `npm test`, typecheck and lint were not run, per user request.
|
||||
- Electron uses an isolated feature window, real application code and temporary
|
||||
data, with DOM actions/Quill edits and substituted model output. It does not
|
||||
validate a packaged full App, physical input devices or a live provider.
|
||||
Real model calls: **0**. Analysis uses desktop `createDefaultModelRuntime`;
|
||||
deployed gbagent and desktop-to-Agent execution are unaffected. Existing user
|
||||
changes were retained; no commits or user-data writes were made by this task.
|
||||
|
||||
## 2026-09-19: Canvas Content and History Preservation
|
||||
|
||||
- Page-break deletion now removes only the embed, retaining existing paragraph
|
||||
terminators and checklist/heading formats. Mid-line insertion retains Quill's
|
||||
native paragraph split rather than guessing which newline is disposable.
|
||||
- Oversized input is rejected before the canvas-local History records it, then
|
||||
reverted with its inverse Delta. Accepted undo/redo and user selections survive;
|
||||
normal rich-text editors retain their original History module.
|
||||
- `npx vitest run tests/canvas-core.electron.test.ts src/renderer/src/magic-canvas`
|
||||
passed 35 tests across 5 files. Electron exercised actual Quill selection and history, then production
|
||||
SQLite/file create, update and reopen retained the same checklist todo.
|
||||
This uses Quill API edits in an isolated Electron window, not physical keyboard
|
||||
input or the full App/IPC save flow.
|
||||
- Focused ESLint for the four changed source/test files and `git diff --check`
|
||||
passed. The integrated full suite passed 4,826 tests with 67 skipped
|
||||
(405 files passed, 9 skipped); full typecheck and lint passed.
|
||||
The existing hidden-window layout scenario measured about 63 seconds, so its
|
||||
child deadline is now 90 seconds with bounded temporary-directory cleanup
|
||||
retries. All geometry, input and persistence assertions remain intact and passed.
|
||||
No model calls, persistence/schema changes, remote Runtime changes or commits.
|
||||
|
||||
## 2026-09-19: Canvas Image Import Size
|
||||
|
||||
- Raised the canvas image file limit to 20 MiB inclusive and updated its error
|
||||
message and derived restoration bound. The authoritative limit is in the
|
||||
[canvas integration reference](../../../src/renderer/src/magic-canvas/README.md#editor-limits).
|
||||
- Real Electron exercised File input, FileReader, Fabric insertion, flush, model
|
||||
conversion and remount: 2 MiB + 1 byte and exactly 20 MiB were retained;
|
||||
20 MiB + 1 byte was rejected with the updated message. Fixtures use a valid
|
||||
one-pixel PNG padded to each byte boundary, not a large decoded pixel image.
|
||||
- Production preload/registered IPC handlers with mocked Electron transport and
|
||||
real SQLite/files passed 20 MiB create, update and database reopen checks.
|
||||
Shared canvas validation and file persistence impose no smaller byte budget.
|
||||
Rich-text attachment budgets and the PDF import limit are unchanged.
|
||||
- Validation: `npx vitest run tests/canvas-core.electron.test.ts src/main/magic-notes/canvas-ipc.integration.test.ts src/main/magic-notes/magic-note-storage.test.ts src/main/magic-notes/rich-content.test.ts src/renderer/src/magic-canvas`
|
||||
passed 66 tests in 8 files. `npm run typecheck` and `npm run lint` passed.
|
||||
The full suite and a combined native Electron IPC save scenario were not run.
|
||||
|
||||
## 2026-09-19: Resizable Record Index
|
||||
|
||||
- The record index defaults to 168px and supports pointer resizing from its right
|
||||
edge and keyboard resizing between 140px and 320px. Index and AI separators share
|
||||
event handlers, styles and accessibility semantics. The existing layout storage
|
||||
key now includes `indexPaneWidth`; constrained window sizes do not overwrite the
|
||||
saved desktop width. Layout behavior is defined in the
|
||||
[technical design](./technical-design.md).
|
||||
- Collapsing removes both the column and its 9px separator while retaining the
|
||||
header toggle. At widths up to 800px the existing fixed 168px drawer remains,
|
||||
without an index separator. Thumbnail images retain contain sizing.
|
||||
- Focused verification: 147 tests passed across `MagicNotesWorkspace.test.tsx`,
|
||||
`MagicCanvasThumbnail.test.tsx` and `WorkspacePrimitives.test.tsx`. Coverage includes
|
||||
pointer bounds/cancel, unrelated pointer IDs, keyboard limits, persistence,
|
||||
independent collapse and narrow layouts. `npm run typecheck`, `npm run lint`
|
||||
and `git diff --check` passed. No full test suite was run for this change.
|
||||
- `npx vitest run tests/magic-notes-layout.electron.test.ts` passed with current
|
||||
production Workspace/CSS/i18n in Electron and fixture note IPC. Native mouse and
|
||||
keyboard input verified default/min/max widths, both pane handles, resizing with
|
||||
AI open and closed, a 329px stream increase when a 320px index is hidden, and
|
||||
actual localStorage restoration after page reload. Content sizes were 1280x800,
|
||||
820x720 and 720x640; the constrained desktop stream remained at least 300px,
|
||||
narrow drawer width was 168px, and no document horizontal overflow occurred.
|
||||
This verifies renderer geometry and browser layout preferences, not the full App
|
||||
shell or note database persistence. No model calls were made.
|
||||
|
||||
## 2026-09-19: Final Full-Suite Verification of Six Fixes
|
||||
|
||||
- Ran `npm test` with a 1,200,000 ms command timeout: **4,756 passed,
|
||||
11 failed, 67 skipped**; files: **395 passed, 1 failed, 9 skipped**.
|
||||
Started at 12:59:14 and completed in 754.02 seconds. This was a shared,
|
||||
concurrently changing worktree, not a frozen source snapshot.
|
||||
- All full-run failures were in `src/main/magic-notes/canvas-ipc.integration.test.ts`.
|
||||
`TypeError: contextManager.cancelImport is not a function` occurred during
|
||||
disposal at test line 91 and reopen at line 77 (cases at 198, 238 and 265).
|
||||
Evidence: the concurrent attachment-import diff adds `ContextManager.cancelImport`
|
||||
and calls it unconditionally from the IPC disposer; the notes fixture supplied
|
||||
only `clear`. The output mapped the IPC frame to line 8942; the inspected current
|
||||
source had the actual call at line 8967.
|
||||
- Added only the missing no-op `cancelImport` to the notes IPC fixture and the
|
||||
equivalent environment fixture in `tests/support/magic-notes-analysis-main.ts`.
|
||||
The latter was exposed by the first focused rerun: **397 passed, 1 failed,
|
||||
0 skipped**, with the same disposal error reported at
|
||||
`tests/magic-notes-analysis.electron.test.ts:56`. No production code was changed
|
||||
in this verification pass.
|
||||
- Final five-file focused command: `npx vitest run
|
||||
src/main/magic-notes/canvas-ipc.integration.test.ts
|
||||
src/renderer/src/MagicNotesWorkspace.test.tsx src/renderer/src/App.test.tsx
|
||||
tests/magic-notes-analysis.electron.test.ts tests/magic-notes-navigation.electron.test.ts`.
|
||||
Result: **396 passed, 2 failed, 0 skipped** in 121.12 seconds; files:
|
||||
**4 passed, 1 failed**. Notes Workspace (108), IPC (11) and both Electron probes
|
||||
(2) all passed, totaling **121 notes tests**.
|
||||
- The remaining failures are App project-activity cases acknowledging foreground
|
||||
and background local success from persisted messages, at
|
||||
`src/renderer/src/App.test.tsx:1430` (missing completion text) and `:1418`
|
||||
(still running instead of completed). Standalone `npx vitest run
|
||||
src/renderer/src/App.test.tsx` reproduced **275 passed, 2 failed, 0 skipped**
|
||||
in 58.44 seconds. Evidence of the unrelated attachment interaction: test line
|
||||
1394 takes `saveLocal.mock.calls.at(-1)` and lines 1408-1411 build completion
|
||||
messages from that call; the concurrent App diff adds an attachment-save queue
|
||||
calling `saveLocal` with `messages: []` at line 2741, including when send clears
|
||||
attachments at line 7620. These tests do not enter notes. App and attachment
|
||||
files were left unchanged by this verification pass.
|
||||
- Focused ESLint passed for both adjusted fixtures. The previously reported full
|
||||
typecheck/lint results were not rerun. `git diff --check` passed after the
|
||||
documentation update (only LF-to-CRLF warnings). The full suite
|
||||
was not repeated after fixture fixes; this is not an all-green full-suite claim.
|
||||
Live model calls in this pass: 0. No commits. Earlier progress sections retained.
|
||||
|
||||
## 2026-09-19: App Leave Guard and Title Drafts
|
||||
|
||||
- App navigation registers the workspace's existing canvas-flush and dirty-draft
|
||||
confirmation through `onBeforeLeave`. Sidebar, launcher and conversation jumps
|
||||
share the gate. Settings closes after its own leave approval before notes asks
|
||||
for confirmation. Cache limits and continuous-record/index behavior are unchanged.
|
||||
- Non-title responses preserve dirty titles; title-save responses acknowledge only
|
||||
the submitted input and retain later edits.
|
||||
- Focused suites passed: `MagicNotesWorkspace.test.tsx` (99 tests) and `App.test.tsx`
|
||||
(277 tests). The expanded settings-to-notes confirmation cases also passed.
|
||||
`npm run typecheck`, `npm run lint` and `git diff --check` passed.
|
||||
- `tests/magic-notes-navigation.electron.test.ts` passed with actual Windows Electron,
|
||||
Workspace, Quill and canvas components. It checks AI-time title input, pending
|
||||
canvas flow input, Continue editing and confirmed discard. API responses are
|
||||
controlled fixtures; this is not a packaged full-App/persistent-IPC probe.
|
||||
Model calls: 0. Full repository test suite was not run.
|
||||
|
||||
## 2026-09-19: Analysis Source Revisions
|
||||
|
||||
- Main/shared/DB implement the source revision and comment invalidation rules in
|
||||
[Canvas Analysis and Comments](./technical-design.md#canvas-analysis-and-comments).
|
||||
Canvas todo writeback rejects stale sources even without pre-existing comments;
|
||||
affected saved todo comments are cleared with a revision increment and stable ID.
|
||||
- Shared `magicNoteCanvasAnalysisText` preserves analyzer page-number semantics.
|
||||
Regression coverage includes blank-page insertion with unchanged plain text,
|
||||
geometry changes, missing/stale capture revisions and source edits during runs.
|
||||
- Focused Vitest: 5 files, 162 tests passed (database, storage, rich content,
|
||||
analyzer and production preload/IPC integration). `npm run typecheck` and focused
|
||||
ESLint passed for the initial Main/shared changes.
|
||||
- Workspace now passes capture revisions, save-response revisions selected by the
|
||||
exact created/edited entry ID, and selected todo source revisions. It uses the
|
||||
shared page-numbered analysis text for automatic text-comment analysis decisions.
|
||||
Draft analysis is unchanged. Regression coverage includes an external refresh
|
||||
completing during capture without pairing the old image with a newer revision.
|
||||
- UI adaptation validation: Workspace (108 tests), production IPC integration
|
||||
(11 tests), both Electron probes (2 tests), and App (277 tests) passed, 398 total.
|
||||
Workspace fixtures validate every analysis request with the production schemas.
|
||||
`npm run typecheck`, `npm run lint` and `git diff --check` passed.
|
||||
- `tests/magic-notes-analysis.electron.test.ts` runs actual Workspace/Quill/Fabric
|
||||
captures through production preload, registered Electron IPC, source revision
|
||||
checks, analyzer and SQLite. Saved-entry analysis, edit/create automatic analysis
|
||||
and source-canvas todo analysis all persist `canvas-images` comments. Only model
|
||||
output and unrelated environment services are substituted; live model calls: 0.
|
||||
The earlier navigation/title Electron regression also passes. No full suite run.
|
||||
|
||||
## 2026-09-19: Default Composer
|
||||
|
||||
- Opening a note now shows a blank text/canvas composer below its title and above
|
||||
the continuous history. Removed the New record button, locale keys and creation
|
||||
visibility state. A successful create resets the editor key and content while
|
||||
retaining its type; automatic analysis does not enter editing. Existing records
|
||||
still require explicit Edit and retain normalized dirty baselines and revision keys.
|
||||
- The index toggle stands directly before the title. Header height now explicitly
|
||||
supplies the narrow drawer offset without relying on the removed button.
|
||||
This supersedes the explicit-create and post-create-edit behavior recorded below.
|
||||
- Focused validation: 5 files, 149 tests passed (`MagicNotesWorkspace`, both editors,
|
||||
`MagicCanvasThumbnail`, `WorkspacePrimitives`). `npm run typecheck`, `npm run lint`
|
||||
and `git diff --check` passed. No full test suite was run.
|
||||
- Temp Electron validation rebuilt current Workspace/PageShell, Quill, Fabric, CSS,
|
||||
production preload and registered IPC, backed by AssistantDatabase and note files.
|
||||
Text/canvas create-save-reopen, blank composer return without confirmation, real
|
||||
editor changes requiring confirmation, thumbnail scroll preserving the top draft,
|
||||
explicit text/canvas edit-save and database close/reopen all passed. Automatic
|
||||
analysis capture-before-reset is covered by the focused regression; model calls: 0.
|
||||
- Light/dark checks at actual content widths 1287, 947, 547 and 362px passed document
|
||||
overflow, continuous history, narrow drawer width, placement below the title and
|
||||
close-on-navigation assertions. Reviewed wide light and narrow dark drawer PNGs.
|
||||
Evidence: `C:/Users/jiang/AppData/Local/Temp/opencode/composer-default-Tq8Ui8/`;
|
||||
runner: `node C:/Users/jiang/AppData/Local/Temp/opencode/run-composer-default.mjs`.
|
||||
- Probe boundaries: isolated feature window, not the full App shell; Quill API input
|
||||
and DOM button clicks, not native keyboard coverage. Early probe failures came
|
||||
from inactive canvas text-tool input, an unconstrained fixture parent, and measuring
|
||||
the inner navigation width instead of the drawer border box. Corrected probes
|
||||
passed without further production changes. No commits or real user-data writes.
|
||||
|
||||
## 2026-09-19: Independent Layout Review
|
||||
|
||||
- Rechecked the action-placement changes in Electron using production PageShell,
|
||||
Workspace, Fabric, Quill and CSS. The earlier bare-workspace screenshots omitted
|
||||
PageShell gutters; no extra stream padding was added to compensate for that fixture.
|
||||
- Fixed narrow record headers forcing each action onto a separate full-width row,
|
||||
touching canvas inner/outer borders in create/read mode, and missing spacing below
|
||||
the Records / AI tabs. Canvas create/edit/read inner frames now share 12px insets.
|
||||
- Fixed Quill percentage heights stretching an empty text composer beyond the
|
||||
visible area. The text save footer and validation now belong to the same composer
|
||||
card. Save handlers, analysis rules and draft-switch semantics are unchanged.
|
||||
- Text toolbar SVGs and picker menus use theme colors; native scrollbars in the
|
||||
notes workspace follow light/dark mode.
|
||||
- Final Temp probe: `canvas-layout-review.mjs final`, with captures prefixed
|
||||
`canvas-layout-final-` and measurements in `canvas-layout-final-metrics.json`.
|
||||
Checked create/edit/read for canvas and text at 1300x950, 960x800, 560x800 and
|
||||
375x720 content sizes in both themes (48 combinations), plus AI-pane captures.
|
||||
Assertions passed for document/main horizontal overflow, canvas action bounds
|
||||
and equal heights, independent side scrolling, reachable text Save, and side
|
||||
collapse/reopen. Inspected final wide, medium, narrow and deep-narrow dark images.
|
||||
- Focused validation: `npx vitest run src/renderer/src/MagicNotesWorkspace.test.tsx
|
||||
src/renderer/src/MagicNoteEditor.test.tsx src/renderer/src/MagicCanvasEditor.test.tsx`
|
||||
passed 102 tests. `npm run typecheck` and `npm run lint` passed. No full suite run.
|
||||
- Scope: actual Windows Electron renderer with an in-memory DesktopApi fixture,
|
||||
not a packaged full-App or persistent-IPC validation. No model calls, runtime or
|
||||
remote changes, commits, or user-data writes.
|
||||
|
||||
## 2026-09-19: Canvas Action Placement
|
||||
|
||||
- Canvas creation places Analyze and Save at the right of the type selector;
|
||||
editing places Analyze, Cancel and Save in the record header. Save remains the
|
||||
primary action. Canvas hints and footer actions were removed, including unused
|
||||
English/Chinese locale keys. Text actions and existing handlers are unchanged.
|
||||
- Focused workspace/editor tests: 2 files, 93 passed. Typecheck and lint passed.
|
||||
Coverage includes all three comment modes, top placement, order, no duplicate
|
||||
footer, cancellation, saving and manual/automatic analysis. No full suite run.
|
||||
- A Temp Electron/Vite probe uses production Workspace, Fabric, Quill and CSS
|
||||
with an in-memory DesktopApi fixture. Creation and editing were checked at
|
||||
1300px and 560px window widths for top placement, visible Save and no horizontal
|
||||
document overflow. Screenshots are `canvas-actions-{create,edit}-{wide,narrow}.png`
|
||||
in the Temp opencode directory. No real model calls were made.
|
||||
|
||||
## 2026-09-19: Review Corrections
|
||||
|
||||
- Fixed local creation selecting an Agent's concurrent append. The database
|
||||
returns the exact `createdEntryId` alongside detail, typed through shared API
|
||||
and preload. The workspace uses that ID for selection, analysis and subsequent
|
||||
edits. Existing Agent/MCP note projections retain their public shape.
|
||||
- Text-edit save/cancel now use the shared analysis cleanup: pending timers and
|
||||
queued content are removed, late comments are ignored, and abandoned settings
|
||||
preparation cannot dispatch a draft request. The composer content is independent.
|
||||
- Local and external deletion reconcile the actual selected ID when no dirty
|
||||
editor must be retained. A later append no longer changes the fallback selection.
|
||||
- Thumbnail abort immediately invokes core destruction, whose synchronous phase
|
||||
cancels PDF/Fabric work. Final cleanup awaits the same promise and removes the
|
||||
temporary surface. Regression tests cover both unmount and content replacement
|
||||
while capture is pending, and verify the next queued thumbnail completes.
|
||||
- Post-fix focused run: **13 files, 257 passed, 0 failed**. Coverage includes
|
||||
`MagicNotesWorkspace`, `MagicCanvasThumbnail`, editor/content/core/model tests,
|
||||
production canvas IPC integration, note storage, assistant database, MCP gateway
|
||||
and preload note events. `npm run typecheck` and `npm run lint` passed.
|
||||
- The user reported a full-suite result of **4,713 passed, 0 failed before these
|
||||
review corrections**. That result does not validate the fixes above. No full
|
||||
suite or Electron probe was rerun in this correction pass. Model calls: 0.
|
||||
|
||||
## 2026-09-19: Record Index Header Toggle Correction
|
||||
|
||||
- Moved the record index toggle immediately before New record in the detail
|
||||
header, retaining its accessible name, title, controls and expanded state.
|
||||
Collapsing hides the whole index and removes its grid column and border width.
|
||||
Narrow layouts use the same toggle for a 168px overlay below the header, with
|
||||
no reserved rail. This supersedes the 40px rail described in the earlier run.
|
||||
- Focused validation: `npx vitest run src/renderer/src/MagicNotesWorkspace.test.tsx
|
||||
src/renderer/src/WorkspacePrimitives.test.tsx` passed 130/130 tests.
|
||||
`npm run typecheck` and `npm run lint` passed. No full suite was run.
|
||||
- Actual Electron geometry checks compiled the current Workspace and production
|
||||
CSS with fixture note data, covering window widths 1300, 960, 700 and 375px with
|
||||
AI both open and closed. Wide stream widths increased by exactly 168px on
|
||||
collapse; narrow stream widths stayed at 687.43 and 362.29px. Hidden index
|
||||
geometry was zero, closed streams began at the layout's left edge, and no
|
||||
horizontal overflow occurred. The header toggle remained hit-testable directly
|
||||
before New record. Native Tab skipped hidden records, programmatic focus on
|
||||
hidden records failed, Enter reopened the index, and narrow Escape restored
|
||||
toggle focus. AI widths were unchanged by index toggling.
|
||||
- Evidence: `C:/Users/jiang/AppData/Local/Temp/opencode/record-index-results-Xvgnsw/results.json`.
|
||||
Reproduce with `node C:/Users/jiang/AppData/Local/Temp/opencode/run-record-index-geometry.mjs`.
|
||||
This checks an isolated Electron renderer, not the full App shell or storage
|
||||
path. Model calls: 0.
|
||||
|
||||
## 2026-09-19: Continuous Stream and Save Baseline Correction
|
||||
|
||||
- Restored all entries to the continuous stream, including while composing. The
|
||||
168px left index and independent right AI pane collapse separately. Index and
|
||||
AI source clicks scroll without discarding drafts or remounting the editor.
|
||||
At widths up to 800px the index uses a 40px rail with a 168px drawer; AI retains
|
||||
its bounded bottom layout. This supersedes the single-record interaction below.
|
||||
- Dirty checks now compare against the editor's initialized content. Canvas waits
|
||||
for its initial flush before enabling input or reporting readiness; text reports
|
||||
Quill's initialized Delta. Save/analysis revisions remount with a fresh baseline,
|
||||
while unchanged background revisions retain it. Failed writes do not advance it.
|
||||
- The actual saved formatted-text callback had the same values but a different
|
||||
Delta field order (`insert, attributes` in storage; `attributes, insert` from
|
||||
Quill), reproducing the old false dirty comparison. Saved PDF content matched
|
||||
both the readiness callback and subsequent flush in the final probe.
|
||||
- Final focused validation: 15 files / 222 tests passed, covering Workspace, both
|
||||
editors, thumbnail/content, shared primitives, canvas core/model, analyzer, file
|
||||
storage, production canvas IPC and canvas text. `npm run typecheck` and
|
||||
`npm run lint` passed.
|
||||
- The full suite completed with 4,730 passed, 1 failed and 67 skipped (713.99s).
|
||||
Its sole failure was the shared contrast test finding the first dark descendant
|
||||
rule instead of the root token block. The test now matches the exact root
|
||||
selector; all 36 shared primitive tests passed afterward, also included in the
|
||||
final 222-test run. The full suite was not repeated after that test fix and the
|
||||
final editor initialization regression additions.
|
||||
- Actual Electron 43.2.0 validation used built renderer assets over `file://`, the
|
||||
production preload, IPC handlers, AssistantDatabase and note files. It verified
|
||||
three simultaneous entries, real thumbnail pixels, scroll-only draft retention,
|
||||
independent collapse, text/canvas create and update followed by a clean return,
|
||||
a new post-save edit requiring confirmation, and a real revision conflict keeping
|
||||
the failed draft. A two-page PDF survived database reopen and three consecutive
|
||||
saves in the same editing context, with unchanged PDF bytes, advancing revisions
|
||||
and no prompt on return. Six entries remained mounted through the layout checks.
|
||||
- Layout captures covered light/dark at content widths 1287, 947, 547 and 362px,
|
||||
with no document horizontal overflow. Narrow drawer opening, 168px geometry,
|
||||
record navigation and automatic closing passed. Temporary evidence is under
|
||||
`C:/Users/jiang/AppData/Local/Temp/opencode/canvas-sqlite-N9japl/`; reproduction:
|
||||
`node C:/Users/jiang/AppData/Local/Temp/opencode/run-canvas-sqlite.mjs continuous-notes.html`.
|
||||
The earlier development-server attempt hit Vite PDF dependency optimization;
|
||||
the built-asset run passed. This validates the production feature path in an
|
||||
isolated Electron window, not the full App shell or a packaged distribution.
|
||||
- `node tests/magic-canvas-wheel.electron.mjs` passed all 34 wheel and full-page
|
||||
height checks. No storage/core wheel changes or model calls were needed.
|
||||
|
||||
## 2026-09-19: Single-Record Workspace (Superseded)
|
||||
|
||||
- Note detail mounts one selected record or an explicitly opened new-entry editor.
|
||||
Saving a new entry selects it; further saves update the same ID and use revisions
|
||||
returned by saves and automatic comments. Record/AI source switches share draft
|
||||
confirmation, and clean external changes refresh without replacing dirty drafts.
|
||||
- A collapsible Records / AI comments region defaults to Records. Each canvas
|
||||
record has one real first-page thumbnail with total page count; text records show
|
||||
summaries. Visible thumbnails render serially and capture only page zero at 240px.
|
||||
The main editor and side region scroll independently; narrow layouts retain the
|
||||
bounded bottom region. UI contract: [design system](../../../UI-DESIGN.md#137-魔法笔记).
|
||||
- Focused validation: 9 files / 142 tests passed across workspace, canvas editor,
|
||||
content, shared primitives, canvas core/model and canvas IPC integration.
|
||||
`npm run typecheck` and `npm run lint` passed. No full `npm test` run in this task.
|
||||
- Actual Electron renderer probe used production Workspace, Quill, Fabric and CSS
|
||||
with an in-memory DesktopApi fixture. It verified one main entry, real thumbnail
|
||||
pixels, one thumbnail for a two-page entry, record switching, independent scroll,
|
||||
collapse/reopen, AI switching, discard/cancel, AI and todo source selection,
|
||||
returning to todo context, and preserving canvas drafts through external updates
|
||||
and deletion. One create followed by three updates included two
|
||||
canvas updates and continuing to edit a newly created record. At 547px content
|
||||
width the document scroll width remained 547px. This is renderer validation,
|
||||
not a complete App-shell or persistent-IPC end-to-end run.
|
||||
- Reproducible local probe: `C:/Users/jiang/AppData/Local/Temp/opencode/run-single-entry-probe.mjs`;
|
||||
screenshot: `single-entry-narrow.png` in the same temporary directory.
|
||||
- Existing `node tests/magic-canvas-wheel.electron.mjs` passed all 34 Electron
|
||||
checks for wheel behavior and full-page editor/viewer height at 900px and 360px.
|
||||
Storage, wheel and page-height behavior were retained. Model calls: 0.
|
||||
|
||||
## 2026-09-19: Full-Page Canvas Height
|
||||
|
||||
- Removed fixed saved-viewer and composer heights. Natural layout includes the
|
||||
current paper, toolbar and padding, with outer vertical scrolling and inner
|
||||
horizontal scrolling. Different page sizes still paginate within one entry.
|
||||
- `node tests/magic-canvas-wheel.electron.mjs`: 34 checks passed in Electron
|
||||
43.2.0, including editor/saved-viewer geometry at 900px and 360px, portrait to
|
||||
landscape pagination, no inner vertical or outer horizontal overflow, and
|
||||
native outer vertical scrolling. This mounts the production core with product
|
||||
CSS and representative wrappers, not the complete App shell. The original
|
||||
wheel fixture has an explicit test-only height constraint to retain both-axis
|
||||
coverage; edge tests wait for the compositor after programmatic scrolling.
|
||||
- Focused canvas/editor/content/workspace tests: 7 files, 95 tests passed.
|
||||
Full typecheck and lint passed; the final probe edit also passed focused lint.
|
||||
The full test suite was not rerun for this height-only change. Model calls: 0.
|
||||
|
||||
## 2026-09-19: File Storage and Schema 38
|
||||
|
||||
- Replaced SQLite body payloads with file-backed rich-text and canvas entries.
|
||||
SQLite retains metadata, membership, indexes, revisions, todos and comments;
|
||||
hydrated API content stays compatible. File layout, write/repair semantics,
|
||||
migration and backup requirements are defined in the
|
||||
[storage contract](./technical-design.md#file-storage-and-writes).
|
||||
- The startup worker converts one entry per transaction and verifies hydrated
|
||||
readback before clearing the legacy payload. Reclamation checks the freelist
|
||||
on every attempt, including retries after conversion has finished. Committed
|
||||
saves/deletes/resets remain successful when manifest or GC cleanup fails;
|
||||
failures are logged and cleanup is retried on later access or reopen.
|
||||
- Storage implementation handoff reports 135/135 focused tests passed across
|
||||
`magic-note-storage`, `assistant-database`, `assistant-storage-upgrade` and
|
||||
`canvas-ipc.integration`, with full `npm run typecheck` and `npm run lint`
|
||||
passing after the review fixes. Filesystem rename/rm failure injection uses
|
||||
real SQLite and files. A 1 MiB PDF retained its binary mtime across 20 updates
|
||||
while SQLite growth stayed below 2 MiB; the migration regression verified
|
||||
more than 2 MiB of physical shrink and restored SQLite plus the notes tree.
|
||||
- Session validation reports the real Electron -> IPC -> SQLite/files path
|
||||
successfully created text, image and canvas/PDF entries, completed three
|
||||
consecutive updates and reopened persisted data after process restart.
|
||||
Migration also passed with an on-disk schema 37 fixture. This was a constructed
|
||||
fixture, not a user's historical database; no real user database was migrated.
|
||||
Actual model calls for this storage validation: 0.
|
||||
- Backups require coordinated SQLite and `notes/` copies while writes are paused
|
||||
or the app is closed. No product one-click backup was added.
|
||||
- Final full test run: 4,705 passed, 3 failed, 67 skipped (760.91 seconds).
|
||||
All three failures were in `acp-remote-runtime.test.ts`, which was being changed
|
||||
concurrently: a launch-response schema mismatch and two channel-close recovery
|
||||
assertions. That file subsequently passed all 100 tests on a targeted rerun.
|
||||
No note-storage test failed; this does not claim a second all-green full run.
|
||||
|
||||
## 2026-09-19: Canvas Wheel Navigation
|
||||
|
||||
- The shared canvas viewport explicitly handles horizontal and vertical wheel
|
||||
input in annotation, flow-text and read-only modes. Shift with vertical-only
|
||||
input scrolls horizontally. Ctrl input was initially left to the browser;
|
||||
the View Zoom update below assigns Ctrl/Command + wheel to canvas zoom.
|
||||
Default scrolling is prevented only when the viewport actually moves; page
|
||||
scrolling remains available at boundaries or without canvas overflow.
|
||||
- Removed scroll-chain containment. The Electron baseline reproduced boundary
|
||||
trapping, but not a universal failure of native horizontal input. The new
|
||||
Electron regression passed 30 scenarios; focused unit tests passed 13 tests.
|
||||
Run `node tests/magic-canvas-wheel.electron.mjs` to repeat the native-input
|
||||
fixture. It does not validate a physical mouse or the complete App shell.
|
||||
- Typecheck and lint passed. The full test run during this fix reported 4,682
|
||||
passed, 67 skipped and one unrelated Agent package inventory timeout.
|
||||
|
||||
## 2026-09-19: Canvas View Zoom
|
||||
|
||||
- Implemented editor/read-only zoom using the shared core. Controls and view-only
|
||||
invariants are documented in the
|
||||
[canvas reference](../../../src/renderer/src/magic-canvas/README.md#view-zoom)
|
||||
and root UI design. The persisted model and export dimensions are unchanged.
|
||||
- Real PDF plus flow validation reproduced an existing cleared-background defect:
|
||||
automatic pagination reassigned an unchanged canvas width/height, erasing its
|
||||
bitmap. Layout now retains the bitmap when dimensions are unchanged.
|
||||
- Focused validation passed 9 files / 140 tests: `canvas-wheel`, `canvas-flow`,
|
||||
`canvas-note`, canvas `model`, `MagicCanvasEditor`, `MagicCanvasThumbnail`,
|
||||
`MagicNoteContent`, `MagicNotesWorkspace`, and `tests/canvas-core.electron.test.ts`.
|
||||
- `node tests/magic-canvas-wheel.electron.mjs`: 41 checks passed in Electron 43.2.0.
|
||||
Native input covered pen coordinates and object move/scale at 50% and 200%,
|
||||
second-page Quill click/typing, Ctrl wheel, ordinary/Shift/diagonal wheel and
|
||||
boundary propagation. Real PDF background pixels, unchanged PNG captures and
|
||||
794-by-1123 output dimensions, stable flow pagination, zero view-only changes,
|
||||
readonly controls, displayed page heights and fit-width resize all passed.
|
||||
Dark narrow layout included a real 560px window resize across the responsive
|
||||
padding breakpoint while the host width remained 360px.
|
||||
- `npm run typecheck` and focused ESLint on all six changed JavaScript/test files
|
||||
passed. Final `npm run lint` encountered two `react-hooks/immutability` errors
|
||||
at lines 18/27 of concurrently added `src/renderer/src/FloatingPortal.tsx`;
|
||||
those parallel changes were left intact. `git diff --check` passed.
|
||||
- No full test suite, production build, physical-device test or macOS session was
|
||||
run in this change. Meta-wheel is covered by focused tests. The Electron fixture
|
||||
uses production canvas modules/styles with real Fabric, Quill and PDF.js; it
|
||||
does not claim complete App-shell or release-package validation. No model calls
|
||||
or commits were made. Deployed Agent paths are unaffected.
|
||||
|
||||
## 2026-09-19: Single Navigation Button and Unframed Note Detail
|
||||
|
||||
- Replaced the two header tabs with one destination-labelled button and icon,
|
||||
immediately before New note. Switching retains the same button and restores
|
||||
focus to it. The list section keeps its localized accessible name; tab roles
|
||||
and references are removed. Navigation still uses the existing draft guard.
|
||||
- Note detail removes the surrounding layout border/radius and stream padding.
|
||||
Composer and entry borders, page margins and the AI divider remain. Overview
|
||||
cards and the independent todo layout retain their frame and spacing.
|
||||
- Focused workspace, editor, shared-primitives and i18n suites passed 122 tests.
|
||||
`npm run typecheck`, `npm run lint` and `git diff --check` passed. The full suite
|
||||
was not rerun for this change, as requested.
|
||||
- Updated the existing `magic-notes-render-electron.cjs` probe in the session's
|
||||
`opencode` temporary directory and ran `magic-notes-render-run.cjs` against the
|
||||
current workspace component and real Quill in Electron. At 1280x820, 600x760,
|
||||
600x520 dark and 420x640, the switch measures 104x36px and precedes New note.
|
||||
Pointer and Space activation restore switch focus; no tab semantics remain.
|
||||
- Computed detail border/radius and stream padding are all zero. Composer and
|
||||
entry borders and the AI divider remain 1px. At 1280px, closing AI expands the
|
||||
stream from 927px to the full 1216px layout width. Narrow layouts keep only the
|
||||
native scrollbar width between layout and content, with no horizontal overflow.
|
||||
AI moves below the editor and closing it increases editor height.
|
||||
- The same probe passed card/scroll return, actual Quill draft cancellation and
|
||||
discard, todo split/narrow layouts, source-note return, creation and completion
|
||||
filters. Screenshots were reviewed; results are in
|
||||
`magic-notes-render-results.json`. Console errors: 0; model calls: 0. Data and
|
||||
AI output use fixtures. The esbuild probe emits an existing IIFE/import.meta
|
||||
warning for the canvas PDF module; this run does not exercise PDF import.
|
||||
|
||||
## 2026-09-18: Integrated Canvas and Validation
|
||||
|
||||
Historical snapshot before file storage: the embedded payload and schema 37
|
||||
statements below describe this stage only. Current persistence and its validation
|
||||
are recorded in the schema 38 entry above.
|
||||
|
||||
- Implementation is complete across the workspace, PeopleLib Fabric + Quill
|
||||
editor/viewer, shared v1/v2 contract, preload/IPC, SQLite and AI analysis.
|
||||
Available operations include paged flow text, pen/highlighter, selection and
|
||||
transforms, floating text, images, PDF import/native text extraction and raster
|
||||
PDF export. Saving is manual; undo/redo is local to the page or Quill mode.
|
||||
The toolbar has no PNG download action; PNG capture is an API used for analysis.
|
||||
- Assets are embedded in SQLite `content_json`, not a separate resource table.
|
||||
Schema 37 is a compatibility marker without conversion of existing records.
|
||||
Flow checklist identity survives object moves and completion writes back to
|
||||
flow. MCP exposes readable canvas metadata/text and rejects plain-text
|
||||
replacement. See the [technical contract](./technical-design.md).
|
||||
- Entry, draft and canvas-source todo analysis use the default model's
|
||||
`supportsImageInput`: complete page captures plus extracted text, or labelled
|
||||
text-only fallback. Purely visual entries/drafts fail clearly with a text-only
|
||||
model. Canvas `immediate` mode explicitly offers manual analysis;
|
||||
`after-save-auto` catches capture/settings errors without blocking storage.
|
||||
Text-only entry comments survive equal `plainText`; layout changes invalidate
|
||||
visual comments and trigger renewed analysis in after-save-auto mode.
|
||||
- Confirmed interactive validation used the real workspace, preload, registered
|
||||
IPC and SQLite to save an image and a two-page PDF, reopen them, and complete
|
||||
three consecutive saves successfully. This covers persistence beyond the
|
||||
earlier renderer-only fixtures. The temporary probe uses
|
||||
`canvas-sqlite.tsx`, `canvas-sqlite-main.ts` and `run-canvas-sqlite.mjs` under
|
||||
the session's `opencode` temporary directory.
|
||||
- The isolated canvas core probe passed with a Vite production build loaded via
|
||||
`file://` and the unchanged application CSP, including PDF import, PNG capture
|
||||
and raster PDF download. The export review also verified the 20,000-character
|
||||
flow boundary and list markers in captured pages and re-rendered exported PDF.
|
||||
These are core rendering/export results, distinct from workspace persistence.
|
||||
- Regression coverage includes storage reopening, checklist writeback, comment
|
||||
retention/invalidation, revision conflicts, MCP protection, renderer integration
|
||||
and analysis input selection. The preload/IPC integration suite uses real
|
||||
SQLite but mocked Electron transport and model output; it is not evidence of
|
||||
a live model request.
|
||||
- Final `npm run typecheck`, `npm run lint` and `npm run build` passed. The full
|
||||
test rerun completed with 4,665 passed, 7 failed and 67 skipped. Failures were
|
||||
in Agent packaging (missing compound-package fixture and an inventory timeout)
|
||||
and activity record/DOM reuse assertions. Those files were under concurrent
|
||||
modification; this is not a fixed-snapshot all-green result. No Magic Notes or
|
||||
App regression failed in that run. The earlier two App failures from eager
|
||||
PDF.js loading were fixed with on-demand PDF imports and passed their targeted
|
||||
rerun; core/editor/workspace focused coverage passed 87 tests.
|
||||
- Live provider validation was blocked before any request: an isolated Electron
|
||||
process could not decrypt the configured default model credential
|
||||
(`runtime-model-credential-unreadable`). Settings were only read and remained
|
||||
unchanged. Actual model calls: 0. Runtime request tests verify image input and
|
||||
text fallback, but do not establish successful live provider generation.
|
||||
- This canvas change does not alter the deployed Agent runtime or the
|
||||
desktop-to-Agent production path, so it requires no separate remote Linux
|
||||
validation. Concurrent runtime work has its own validation scope.
|
||||
|
||||
## 2026-09-18: Stable Todo Split View and Header Navigation
|
||||
|
||||
- Supersedes the inline task expansion described in the earlier entry below.
|
||||
Wide containers now keep the grouped task list on the left and instructions,
|
||||
source and AI in one right detail pane. Narrow containers retain the mounted
|
||||
list and offer on-demand detail with an explicit return. Source-note return
|
||||
preserves task context. Header tabs precede New note; filters follow search.
|
||||
- Focused workspace, editor and shared-primitives suites passed all 93 tests.
|
||||
`npm run typecheck` and targeted ESLint passed. No full `npm test` was run.
|
||||
- Reused the temporary Electron renderer probe at 1280x820, 600x760,
|
||||
600x520 dark and 420x640. The 134x36 header switch precedes New note at all
|
||||
four sizes. Search/filter share a row at 1280 and 600; filters wrap at 420.
|
||||
The wide list's width, top, scroll and adjacent row coordinates remain equal
|
||||
before and after task selection/switching. Narrow returns preserve nonzero
|
||||
list scroll and restore focus. No horizontal overflow or console errors.
|
||||
- Also verified source-note round trips at all sizes, actual Quill draft
|
||||
confirmation, note-card return, AI visibility, task completion/status filters,
|
||||
AI fixture results, creation and Chromium keyboard activation.
|
||||
- Visible-window capture initially failed with Electron `UnknownVizError`.
|
||||
Final probe uses Electron offscreen rendering at scale 1, pointer input and
|
||||
Chromium DevTools keyboard input. It runs production React/Quill/CSS with
|
||||
in-memory API fixtures, not the full App/Main/SQLite/model path. Model calls: 0.
|
||||
Evidence remains under the temporary `opencode` directory in
|
||||
`magic-notes-render-results.json` and `magic-todos-*-expanded.png`.
|
||||
|
||||
## 2026-09-18: Independent Todo Task View
|
||||
|
||||
- Replaced todo full-page navigation with a grouped task list and one inline
|
||||
expansion for instructions, source content and AI comments. Completion works
|
||||
directly in each row. Search/status controls stay above the scrolling list;
|
||||
source-note navigation retains expansion, filters and initiating focus.
|
||||
- Kept the note-card overview and note editor/AI layout. Notes/To-dos PageTabs
|
||||
now size to their contents at desktop and narrow widths. Source failures offer
|
||||
an inline retry alongside the existing notification.
|
||||
- Focused command: `npx vitest run src/renderer/src/MagicNotesWorkspace.test.tsx src/renderer/src/MagicNoteEditor.test.tsx src/renderer/src/WorkspacePrimitives.test.tsx`:
|
||||
93 tests passed. `npm run typecheck` and ESLint on the workspace, its tests and
|
||||
both changed locale modules passed. Full `npm test` was not rerun, as requested.
|
||||
- Reused the isolated Electron renderer probe at requested content sizes
|
||||
1280x820, 600x760, 600x520 (dark) and 420x640. All four todo views retained
|
||||
18 tasks, inline expansion, independently scrolling lists and no horizontal
|
||||
overflow or detail/AI sidebar. Tabs measured approximately 133x35 CSS pixels.
|
||||
Note-card navigation, actual Quill draft confirmation, AI visibility, source
|
||||
navigation, direct completion, status filters and inline AI fixture output passed;
|
||||
the renderer reported no console errors.
|
||||
- The probe uses production React, Quill and CSS with in-memory API fixtures and
|
||||
isolated Electron sessions; it does not validate Main/SQLite or a live model.
|
||||
Model calls: 0. Scripts, screenshots and JSON results remain in the existing
|
||||
temporary `opencode/magic-notes-render-*` probe location.
|
||||
|
||||
## 2026-09-18: Card Overview and Detail Navigation
|
||||
|
||||
- Replaced the persistent inner list with a responsive card overview and explicit
|
||||
detail navigation. Search, todo filters, overview scroll and initiating focus
|
||||
survive a round trip. Creation opens the new note; todo source navigation returns
|
||||
to the todo overview. Back navigation protects entry and title drafts.
|
||||
- Kept AI visibility and resizing preferences, removed obsolete list resizing,
|
||||
and capped stacked AI height so short windows retain usable editor space.
|
||||
- Focused workspace, editor and shared UI suites passed 90 tests. Typecheck and
|
||||
full lint passed. The first full test invocation exceeded its 200-second limit.
|
||||
- Isolated Electron rendering exercised the actual workspace, Quill and CSS at
|
||||
1280x820, 600x760, 600x520 (dark), and 420x640. Navigation, creation, todo-source
|
||||
jumps, draft confirmation, AI visibility, scroll and focus restoration passed.
|
||||
These checks used in-memory API fixtures, not Main/SQLite integration; no real
|
||||
model calls were made. Canvas functionality is outside this layout change.
|
||||
|
||||
## 2026-09-17: Note List Action Menu
|
||||
|
||||
- Moved pin/unpin and whole-note deletion from the editor header to each note's
|
||||
action menu, following the [UI design system](../../../UI-DESIGN.md#137-魔法笔记).
|
||||
The menu uses existing update/delete IPC and shared destructive confirmation.
|
||||
- Added regression coverage for menu keyboard behavior, portal placement,
|
||||
dismissal, pin ordering, revision failures, deletion and retained drafts.
|
||||
- The focused Magic Notes suite passed 42 tests. The combined Magic Notes,
|
||||
conversation and shared-primitives suites passed 332 tests. Typecheck and
|
||||
targeted ESLint passed.
|
||||
- Full `npm test`: 370 files and 4,440 tests passed, 2 files and 3 tests failed,
|
||||
9 files and 67 tests skipped. Two heartbeat migration fixtures relabel a current
|
||||
database as an old version and fail on the existing `pinned` column. One
|
||||
OpenCode runtime-reuse request timed out; its two tests passed on isolated rerun.
|
||||
- Full lint reported 22 `document` no-undef errors in the unrelated, untracked
|
||||
`docs/features/story-graph/demo.js`. No unrelated files were changed to resolve
|
||||
these validation failures. Interactive Electron and touch verification remain
|
||||
unperformed.
|
||||
|
||||
## 2026-09-15: Automatic External Refresh
|
||||
|
||||
- Implemented the shared-database notification and Main/preload subscription
|
||||
|
||||
@@ -1,5 +1,323 @@
|
||||
# Magic Notes Technical Design
|
||||
|
||||
## Continuous Record Workspace
|
||||
|
||||
The detail mounts every entry in a continuous newest-first stream. The left index
|
||||
and AI source buttons scroll to an entry ID without changing the active editor,
|
||||
clearing a composer, flushing content or invoking the draft-switch guard. Opening
|
||||
a note shows a blank composer below the title and above the stream, without a New
|
||||
record button. Editing another entry or
|
||||
leaving the note still checks unsaved content after flushing canvas changes.
|
||||
`MagicNotesWorkspace.onBeforeLeave` registers this same check with the App navigation
|
||||
gate, including sidebar, application launcher and conversation jumps. The gate waits
|
||||
for canvas flush, then offers Continue editing or Discard draft and switch for dirty
|
||||
body or title content. Workspace cache eviction does not serve as draft protection.
|
||||
Non-title detail responses retain a dirty title. A title-save response acknowledges
|
||||
only the submitted input; text entered while that request runs remains a draft.
|
||||
The shared store returns `MagicNoteEntryCreateResult`, the existing detail shape
|
||||
plus a required `createdEntryId`. Production IPC and preload preserve that ID.
|
||||
Creating an entry selects this exact ID and resets the composer through its key;
|
||||
it does not enter editing, including after automatic analysis. Text/canvas selection
|
||||
is retained. Canvas capture for automatic analysis completes before the reset.
|
||||
the renderer never infers it from a list difference or timestamp. Agent/MCP
|
||||
creation delegates to the same store; its existing public note projection is
|
||||
unchanged.
|
||||
Explicit Edit opens a saved record. Update and automatic-analysis responses advance its revision, so subsequent
|
||||
saves use `updateEntry` and the returned revision. Failed writes retain the draft.
|
||||
|
||||
The dirty baseline belongs to the mounted editor. `MagicNoteEditor.onReady` reports
|
||||
Quill's normalized contents after loading; `MagicCanvasEditor.onReady` reports the
|
||||
first completed core flush, after Fabric restoration and flow pagination. Canvas
|
||||
input stays disabled until this baseline exists, and imperative flush waits for
|
||||
the same initialization promise. Raw stored JSON is not a valid comparison baseline
|
||||
for an initialized editor: Quill can add its terminal newline, and Fabric/flow
|
||||
serialization can add defaults or change field order. Later changes and navigation
|
||||
flushes compare with the initialized baseline without resetting it. A successful
|
||||
save remounts at the returned revision and establishes a new baseline; an unchanged
|
||||
background revision retains the existing editor and baseline. Aborted canvas
|
||||
initialization and file reads for an unmounted text editor cannot write back.
|
||||
|
||||
The real saved formatted-text probe reproduced identical values with different
|
||||
Delta key order: storage returned `{insert, attributes}` and Quill emitted
|
||||
`{attributes, insert}`. The old `JSON.stringify` comparison therefore reported
|
||||
dirty after a successful save. The saved PDF probe's initialized content and
|
||||
subsequent flush were byte-for-byte equal; readiness is still awaited for canvas
|
||||
restoration rather than assumed from the save response.
|
||||
|
||||
Saving or leaving an editing context clears the immediate-analysis timer, queued
|
||||
content and streamed draft comments through the shared draft-analysis cleanup.
|
||||
The context counter rejects late results and prevents dispatch after abandoned
|
||||
settings preparation. This cleanup does not clear the new-entry composer's content.
|
||||
|
||||
The left index defaults to 168px and resizes from its right edge between 140px
|
||||
and 320px. It shares the AI pane's pointer-capture handlers, separator styling
|
||||
and keyboard semantics (16px arrow steps, Home/End limits). Both widths are
|
||||
stored in `goodbuddy.magic-notes-layout.v1`; missing index width defaults to
|
||||
168px. Displayed widths clamp together to reserve at least 300px for the stream,
|
||||
without overwriting saved widths when the window shrinks. The page header places
|
||||
the index toggle immediately after the back button, followed by the AI toggle.
|
||||
All three use icons and visible localized text; pane toggle labels reflect their
|
||||
current show/hide action. Collapsing hides the entire index and removes its grid column,
|
||||
border, resize separator and occupied width. AI has a separate
|
||||
right pane with its existing resize separator and header toggle. Both panes scroll
|
||||
independently and persist independent desktop visibility. At container widths of
|
||||
800px or less, the index defaults to hidden and the same header toggle opens or
|
||||
closes a fixed 168px drawer below the header, without reserving a rail or showing
|
||||
an index resize separator; choosing
|
||||
an item or pressing Escape closes it. AI moves below the stream, limited to 40%
|
||||
of layout height and 280px. Overview search, scroll, task selection and return
|
||||
focus stay mounted.
|
||||
|
||||
`MagicCanvasThumbnail` lazily queues visible records through a temporary core
|
||||
renderer and caches results by content identity in a WeakMap. Core `capturePages`
|
||||
accepts `firstPageOnly` and `thumbnailWidth`; thumbnails capture page zero at 240px
|
||||
width, including paper/PDF background, Quill flow, images and Fabric objects.
|
||||
They do not capture all pages or leave an editor mounted for every record.
|
||||
An abort listener calls core `destroy()` immediately: PDF render cancellation and
|
||||
the Fabric abort happen synchronously, before waiting for capture to settle.
|
||||
The `finally` path awaits the same destruction promise and removes the temporary
|
||||
surface, without destroying twice or waiting for capture before cancellation.
|
||||
AI capture uses the configured page prefix described under Canvas Analysis and
|
||||
Comments. Main canvas page height, wheel handling and file storage are unchanged.
|
||||
|
||||
Background refresh preserves dirty editing content and its original optimistic
|
||||
revision, including externally deleted entries. Clean editors adopt refreshed
|
||||
entries; deleting a clean selection updates `selectedEntryId` to a remaining
|
||||
entry or the empty state. Later appends preserve that actual selection, rather
|
||||
than recalculating a render-only fallback. Dirty deleted entries remain open
|
||||
until explicitly discarded. No background refresh saves a draft.
|
||||
|
||||
## Canvas Storage Contract
|
||||
|
||||
`MagicNoteContent` is the union of unchanged v1 Quill rich content and v2
|
||||
`paged-canvas`. The shared schema is authoritative. Canvas pages carry dimensions,
|
||||
template/PDF backgrounds, opaque Fabric object records and optional `flowAuto`.
|
||||
The optional Quill flow accepts native PeopleLib `version: 1` and page-break
|
||||
embeds. Template names are `blank`, `ruled`, `grid`, `dot`; PDF backgrounds use
|
||||
one-based `pageNumber` and optional extracted `text`.
|
||||
|
||||
API assets are document-local `{id,name,mimeType,dataUrl}` records. Backgrounds and
|
||||
image objects reference stable asset IDs; image objects can also embed raster
|
||||
data directly. Main validates data encoding, image/PDF signatures and references.
|
||||
Canvas has no v1 resource-size budget. `image_bytes` counts decoded embedded
|
||||
resource bytes for new entries. Bodies and binary assets use the file storage
|
||||
contract below; there is no separate resource table, resource staging or extra
|
||||
resource IPC. Renderer, preload and IPC continue to exchange hydrated v1/v2
|
||||
content with data URLs.
|
||||
|
||||
Schema 38 replaces SQLite body payloads with file markers. Schema 37 was the
|
||||
earlier canvas compatibility marker; its embedded-payload layout is historical.
|
||||
Plain text combines flow text, PDF extracted text and object text for search.
|
||||
Checklist indexes depend only on flow checklist lines; moving objects does not
|
||||
change todo identity. Completion updates flow without replacing pages or assets.
|
||||
Entry edits retain optimistic revisions. Canvas comment retention depends on the
|
||||
actual analysis input, as described below.
|
||||
|
||||
`note_get` reports content kind/version and `plainTextEditable`. The text update
|
||||
tool rejects canvas entries before mutation. These tools execute in the desktop
|
||||
gateway; remote gbagent's injected image MCP server is unaffected. Browser page
|
||||
renders can be sent as `analysisOptions.canvasImages`; analysis comments and draft
|
||||
results can retain the actual `inputMode` used.
|
||||
|
||||
## File Storage and Writes
|
||||
|
||||
`MagicNoteStorage` owns files under `dirname(assistantDatabasePath)/notes`:
|
||||
|
||||
```text
|
||||
notes/<noteId>/
|
||||
note.json # Derived {id, entries} membership manifest
|
||||
entries/<entryId>.json # Authoritative body envelope
|
||||
assets/<sha256>.<ext> # Binary assets deduplicated within this note
|
||||
```
|
||||
|
||||
Each body envelope contains `id`, `noteId`, `revision`, `updatedAt` and `content`.
|
||||
SQLite owns note metadata, entry membership, search/plain-text and byte indexes,
|
||||
optimistic revisions, todos and comments. Entry `content_json` contains only
|
||||
`{storage: "file", version: <content version>, revision: <body revision>}`.
|
||||
Comment-only revision increments do not change the indexed body revision.
|
||||
|
||||
Known media fields become `{$asset: "assets/<sha256>.<ext>", mimeType: "..."}`
|
||||
on disk and hydrate back to data URLs on API reads. These fields are v1
|
||||
`insert.image`, `localVideo.dataUrl` and `attachment.dataUrl`; canvas
|
||||
`assets[].dataUrl`, `preview`, flow embeds and image-object `src`, including
|
||||
nested objects. Ordinary text, insert strings, names and arbitrary `$asset`
|
||||
objects remain unchanged. Canvas asset IDs and PDF references are preserved.
|
||||
Write, read and garbage collection use the same scoped field traversal.
|
||||
|
||||
Revision checks run under `BEGIN IMMEDIATE` before file writes. Assets are
|
||||
written before the temporary body file is atomically renamed over the entry
|
||||
file; the database then commits its marker and indexes. This is not a single
|
||||
atomic transaction spanning SQLite and the filesystem. If a replacement body
|
||||
was renamed but indexing failed, the next database access or reopen reads it
|
||||
back, repairs plain text, byte counts and todos, advances revisions and
|
||||
invalidates old analysis. Missing bodies raise an error instead of becoming
|
||||
empty content. Failed creates leave no accepted database membership; their
|
||||
unaccepted files are removed on the next access or reopen.
|
||||
|
||||
After commit, reconciliation derives `note.json` from database membership and
|
||||
scans all retained entry files before removing unreferenced assets. Manifest or
|
||||
GC failures log `console.warn` with the note ID and error and remain in an
|
||||
in-memory retry set. Later database access retries; reopening derives cleanup
|
||||
again from membership. These cleanup failures do not turn a committed save,
|
||||
delete or reset into a failed operation or suppress its notifications. Body
|
||||
persistence and index-repair failures still propagate. Entry deletion, note
|
||||
deletion and assistant-data reset share this cleanup path; deferred whole-tree
|
||||
cleanup checks current membership so it preserves notes created after reset.
|
||||
There is no persistent retry journal, and renderer-only drafts create no files.
|
||||
|
||||
## Schema 38 Migration
|
||||
|
||||
The existing startup storage worker runs before the business connection opens.
|
||||
It upgrades the schema and converts legacy payloads one entry per SQLite
|
||||
transaction: write assets and body, read back hydrated content, compare JSON
|
||||
equality, then replace the old `content_json` payload with its file marker.
|
||||
Already converted entries are reconciled on retry. Matching body revisions do
|
||||
not require rehydrating binary payloads. Direct `AssistantDatabase.initialize`
|
||||
callers and tests also run storage repair.
|
||||
|
||||
Space reclamation is separate from conversion. Every worker attempt checks
|
||||
`PRAGMA freelist_count` and runs checkpoint/VACUUM when free pages remain.
|
||||
Cancellation after the last converted entry or a VACUUM failure therefore
|
||||
allows reclamation to retry on the next startup, even with no legacy rows left.
|
||||
The schema marker alone does not establish that conversion and reclamation
|
||||
finished. General migration requirements are in the
|
||||
[database migration guide](../../development/database-migrations.md).
|
||||
|
||||
## Backup and Restore
|
||||
|
||||
A restorable backup must pair a consistent SQLite backup with the adjacent
|
||||
`notes/` tree from the same period with note writes paused, or with the app
|
||||
closed. Handle active WAL through SQLite's consistent backup mechanism; copying
|
||||
only the database main file while it is active is insufficient. Restore both
|
||||
parts together. SQLite-only backups of file-backed entries cannot recover
|
||||
bodies or assets, and `note.json` alone cannot recover database metadata,
|
||||
revisions, todos or comments.
|
||||
|
||||
The product has no one-click assistant backup action. This is an operational
|
||||
backup boundary, not a new UI capability. The tested backup/restore and migration
|
||||
scope, including the lack of a real user historical database run, is recorded
|
||||
in [progress](./progress.md).
|
||||
|
||||
## Canvas Editor and Export
|
||||
|
||||
`MagicNotesWorkspace` selects rich-text or canvas content for new entries and
|
||||
opens saved canvases in `MagicCanvasEditor`; `MagicNoteContent` delegates saved
|
||||
canvas rendering to the paginated `MagicCanvasContent` viewer. The React adapter
|
||||
wraps the retained PeopleLib Fabric + Quill core. Fabric owns page annotations;
|
||||
Quill owns the continuous body flow. Saving awaits `flush()` so queued imports,
|
||||
text edits and pagination reach the existing preload/IPC create/update handlers.
|
||||
There is no autosave or separate canvas persistence path.
|
||||
|
||||
Editor and viewer heights follow the current paper element's displayed height
|
||||
through natural CSS layout, including wrapped toolbars and viewport padding.
|
||||
No fixed preview height or viewport-height cap clips the page. The outer note
|
||||
stream scrolls vertically; the paper viewport retains horizontal scrolling.
|
||||
View zoom is local to each mounted core instance. A displayed-size outer box and
|
||||
one transformed logical-size surface keep PDF, flow and Fabric aligned; Quill
|
||||
screen measurements are converted back to logical coordinates. Neither zoom nor
|
||||
fit-width resize calls `onChange`, changes the persisted model, invalidates AI
|
||||
comments or alters PNG/PDF export dimensions. See the
|
||||
[view zoom implementation](../../../src/renderer/src/magic-canvas/README.md#view-zoom)
|
||||
for controls, bounds, page-switch policy and observer cleanup.
|
||||
|
||||
Paper templates, pen, highlighter, whole-object eraser, selection/transforms,
|
||||
floating text, images and PDF page backgrounds are integrated. Native PDF text
|
||||
is stored on its background for search and analysis; scanned pages remain visual
|
||||
without automatic OCR. The toolbar exports a raster PDF through jsPDF, composing
|
||||
background, flow text and annotations per page. It does not retain a searchable
|
||||
PDF text layer. `capturePages()` provides PNG data URLs for AI input, not a PNG
|
||||
download button. Undo/redo is per annotation page or Quill mode; page operations
|
||||
and PDF imports are not global undo steps.
|
||||
|
||||
Flow page-break removal and rejected-input history handling follow the
|
||||
[canvas editing and limit rules](../../../src/renderer/src/magic-canvas/README.md#editing-and-assets).
|
||||
`canvas-core.electron.test.ts` feeds the real Electron flow's before/after content
|
||||
into production `AssistantDatabase` create/update and reopen, checking that the
|
||||
derived checklist todo and its identity survive page-break removal. History
|
||||
coverage exercises accepted typing, rejected overflow, selection and redo.
|
||||
|
||||
The [canvas integration reference](../../../src/renderer/src/magic-canvas/README.md)
|
||||
owns core limits, import behavior and lifecycle details. Vite bundles Fabric,
|
||||
Quill, jsPDF, PDF.js workers and binary assets; PDF workers use explicit module
|
||||
Workers and local assets under the production `file://` CSP. The renderer does
|
||||
not need Node integration or remote asset services.
|
||||
|
||||
## Canvas Analysis and Comments
|
||||
|
||||
Draft, saved-entry and todo analysis use the existing typed preload/IPC methods
|
||||
and `createDefaultModelRuntime`. Each canvas request reads the current default
|
||||
model profile's `supportsImageInput` (or the resolved default setting when no
|
||||
profile is selected). Main resolves settings again for the actual request. This
|
||||
does not change an Agent runtime or desktop-to-Agent protocol.
|
||||
|
||||
`magicNoteCanvasPageCount` is an application preference: an integer from 1 to 8,
|
||||
defaulting to 1 when absent in historical settings. The application center and
|
||||
`goodbuddy_config` application.update/get share the settings schema, store and
|
||||
change event. It counts pages within the canvas being analyzed, never entries.
|
||||
The first N pages follow the current `content.pages` array order, independent of
|
||||
the viewed page, page IDs or PDF source page numbers. Fewer than N pages selects
|
||||
the whole canvas. Saving and PDF export retain the 50-page canvas limit.
|
||||
|
||||
Renderer and Main share `selectMagicNotePages`. The core selects the prefix before
|
||||
compositing, so it never captures 50 pages just to discard 49. Analysis capture
|
||||
returns the selected pages' Quill text measured from the actual CSS columns;
|
||||
binary searches locate the character boundaries for explicit and automatic page
|
||||
breaks. Text-only requests skip image compositing but still read page text.
|
||||
The transient `canvasPageText` IPC field carries these page IDs and text, without
|
||||
changing saved canvas content. Main reads the persisted page count for all three
|
||||
handlers, selects page text, PDF text, objects and images in the same order, and
|
||||
ignores captures outside the prefix. A partial canvas with flow requires matching
|
||||
page text; missing data fails instead of including the full cross-page flow.
|
||||
Prompts state the selected and total page counts. A changed setting applies to
|
||||
the next analysis; existing comments are not automatically regenerated.
|
||||
|
||||
| Default model capability | Analysis input and result |
|
||||
| --- | --- |
|
||||
| Image input supported | Renderer captures the selected prefix; Main requires each selected page's PNG/JPEG capture and sends it alongside that page's flow, PDF and object text. Comments carry `inputMode: canvas-images`. |
|
||||
| No image input | Only text from the selected prefix is sent, with an explicit instruction that handwriting, images and layout were not seen. Comments carry `inputMode: text-fallback`. |
|
||||
| No image input and no extracted text in the selected entry/draft pages | Analysis fails with guidance to switch to an image-capable default model; no visual understanding is claimed. |
|
||||
|
||||
Todo analysis includes its title and instructions plus the source canvas context,
|
||||
and uses the same capability and capture rules. Non-canvas content keeps the text
|
||||
analysis path. All analysis remains read-only with no tool calls.
|
||||
|
||||
Saved-entry requests accept `expectedRevision`; todo requests accept
|
||||
`sourceEntryRevision`. Nonempty `canvasImages` or `canvasPageText` requires the corresponding revision,
|
||||
even when the resolved model uses text fallback. Main loads the source after
|
||||
resolving settings and checks the supplied revision before model invocation.
|
||||
Existing rich-text callers may omit these fields. Entry writeback retains its
|
||||
revision check; canvas todo writeback also requires the source entry revision,
|
||||
including when no comments existed at request time. No additional stored snapshot
|
||||
or schema migration is needed.
|
||||
|
||||
Canvas `immediate` mode provides an Analyze canvas draft action without a persistent
|
||||
instruction banner; pen strokes do not trigger automatic requests. `after-save-auto`
|
||||
prepares analysis input and then saves manually submitted content. Settings or
|
||||
capture errors are caught so storage still proceeds; after a successful save,
|
||||
the UI reports that automatic analysis failed. A later model error likewise
|
||||
does not roll back the save. Failure to flush the document itself still prevents
|
||||
saving incomplete content.
|
||||
|
||||
For v2-to-v2 entry updates, identical content retains saved comments. If all
|
||||
comments have `text` or `text-fallback` input mode, equal analysis text from shared
|
||||
`magicNoteCanvasAnalysisText(content)` (including page numbers)
|
||||
also retains comments across layout-only changes. Other changed canvas content,
|
||||
including layout changes with `canvas-images` comments, clears saved entry
|
||||
comments and `analyzed_at`. Source canvas changes apply the same rule to saved
|
||||
todo comments and increment the affected todo revision without replacing its ID.
|
||||
In `after-save-auto`, visual comments trigger renewed
|
||||
analysis when content changes, while existing text-only comments trigger it when
|
||||
analysis text changes. A changed draft clears its prior canvas draft analysis.
|
||||
The UI labels the actual input mode so a text fallback is visible to the user.
|
||||
|
||||
Workspace captures the saved entry and its revision before awaiting image capture;
|
||||
an external refresh cannot pair an old capture with a newer revision. Automatic
|
||||
post-save analysis takes the revision from the save response using `createdEntryId`
|
||||
or the edited entry ID. Todo capture uses the selected source entry's actual page
|
||||
IDs and revision. Renderer text-only change detection uses the same shared
|
||||
analysis-text helper as persistence, so inserting a blank page before text triggers
|
||||
renewed analysis when its cited page number changes. Draft analysis remains unversioned.
|
||||
|
||||
## Change Notifications
|
||||
|
||||
`AssistantDatabase` accepts `onMagicNotesChanged: () => void`. Every successful
|
||||
@@ -22,6 +340,15 @@ objects and returns a function removing that listener. The existing
|
||||
|
||||
## Refresh Lifecycle
|
||||
|
||||
The overview's `libraryView` and the optional note-only `detailView` are independent:
|
||||
opening a todo's source note does not lose the originating task view.
|
||||
The overview remains mounted but hidden while detail is open, retaining scroll
|
||||
position and filters. Returning restores the initiating control's focus and
|
||||
invalidates outstanding detail requests. Initial loading fetches summaries and
|
||||
todos without selecting the first note or task. Navigation away from unsaved content or
|
||||
an unsaved title uses the existing draft confirmation; active writes finish
|
||||
before navigation. No new persistence or IPC contract is required.
|
||||
|
||||
`MagicNotesWorkspace` subscribes on mount and coalesces notifications with a
|
||||
100 ms debounce. Pending notifications wait for an active local save or analysis
|
||||
to finish. Cleanup removes the listener, clears its timer and invalidates pending
|
||||
@@ -35,26 +362,116 @@ state are not reset.
|
||||
|
||||
Background application of a detail reads the current draft state after the
|
||||
asynchronous fetch. A dirty title is retained, and the composer is not remounted.
|
||||
An entry editor uses its original editing content and revision for both its React
|
||||
key and save request; external revisions cannot remount it or bypass optimistic
|
||||
concurrency checks. Deleted entries remain displayed only while their editor is
|
||||
open. If an externally deleted note has a draft, its editor remains available
|
||||
A dirty entry editor uses its original editing content and revision for both its
|
||||
React key and save request; external revisions cannot remount that draft or bypass
|
||||
optimistic concurrency checks. Clean editors adopt newer revisions. Deleted entries
|
||||
remain displayed only while their editor has a draft. If an externally deleted note has a draft, its editor remains available
|
||||
with a deletion explanation instead of moving the draft to another note.
|
||||
|
||||
Read failures use the existing retryable refresh error and retain successful
|
||||
data and drafts. Retry uses the same draft-preserving loader. Interaction rules
|
||||
are owned by [the UI design system](../../../UI-DESIGN.md#137-魔法笔记).
|
||||
|
||||
## Todo Task View
|
||||
|
||||
The To-dos panel has its own search/status toolbar and independently scrolling
|
||||
task list, grouped by source note. `selectedTodoId` controls one sibling detail pane;
|
||||
the selected item is derived from the filtered list. Completion updates the
|
||||
existing item and source cache without selecting or opening another task. Items
|
||||
that no longer match the status filter leave the list. A removed focused control
|
||||
returns focus to an available list/filter control.
|
||||
|
||||
Detail content includes instructions, the source entry, its existing note
|
||||
navigation action, and AI controls/comments. AI requests still use the existing
|
||||
analysis options and streaming subscription; failed analysis retains saved
|
||||
comments. Source reads are guarded against superseded selections, report failures
|
||||
through notifications and offer inline retry. The grid reserves both columns even
|
||||
without a selection, so selecting or switching tasks cannot move list rows. The
|
||||
detail is keyed by task ID to start each newly selected task at its top.
|
||||
At a page container width of 700px or less, CSS hides the mounted list while a
|
||||
task is selected and shows an explicit return button in the detail area. Returning
|
||||
clears selection and restores row focus without scrolling; source-note return
|
||||
retains selection and focuses this visible return button on narrow layouts.
|
||||
There is no todo detail route or separate AI sidebar. A shared accessible resize
|
||||
separator displays a 1px line with a transparent 9px hit area and 8px adjacent
|
||||
content padding. It adjusts the list width, initially 320px, with a 240px minimum and at
|
||||
least 300px reserved for detail. Pointer dragging and 16px arrow steps/Home/End
|
||||
persist `todoPaneWidth` in `goodbuddy.magic-notes-layout.v1`. Narrow layouts hide
|
||||
the separator; window constraints do not overwrite the saved desktop width.
|
||||
Each source-note heading is a disclosure button with its matching task count.
|
||||
Groups start expanded and can independently hide their mounted rows without
|
||||
clearing the selected detail. Collapse state lasts while the group is mounted.
|
||||
Note detail retains its existing AI pane preferences.
|
||||
|
||||
A single secondary button lives in `PageHeader.actions` before New note. Its
|
||||
label and icon describe the destination, and `requestDraftSwitch` retains the
|
||||
existing draft guard. Focus returns to the same `magic-library-switch` button
|
||||
after switching. The overview section keeps its localized list `aria-label`;
|
||||
there are no tab, tablist or tabpanel roles or tab-label references. The button
|
||||
follows content width at all sizes. The shared status `SegmentedControl` follows
|
||||
search in one wrapping toolbar.
|
||||
|
||||
Only note detail applies `magic-notes-layout--detail` to remove the layout frame
|
||||
and corner radius. Its stream pane has no extra padding; editor borders and the
|
||||
AI divider remain. Overview cards and the independent todo layout keep their
|
||||
existing frame and spacing. These changes do not affect Main, preload or storage.
|
||||
|
||||
## Note List Actions
|
||||
|
||||
Each overview card has an open-detail button and a sibling action-menu button. The menu
|
||||
uses the conversation action styles and shared `DestructiveConfirmActions`, and
|
||||
is portalled to `document.body`. Its position follows the trigger on scrolling,
|
||||
window resizing and confirmation-size changes. Entering detail, filtering out the
|
||||
target or leaving the notes panel closes the menu.
|
||||
|
||||
Pinning uses the target summary's ID and revision through the existing update
|
||||
IPC. It updates the sorted summary and matching selected detail without resetting
|
||||
title, composer or entry-edit drafts. Deleting uses the explicitly confirmed
|
||||
target ID and refreshes the overview after success, without opening another note.
|
||||
These actions are available only in the overview. Database and Agent contracts
|
||||
are unchanged.
|
||||
|
||||
## Agent Search Contract
|
||||
|
||||
`note_search` accepts an integer `limit` from 1 to 100, defaulting to 8.
|
||||
The shared schema supplies both model tool definitions and MCP validation;
|
||||
the existing 128 KiB output budget can reduce the returned result count.
|
||||
The built-in MCP gateway returns invalid scoped-tool arguments as an
|
||||
`isError: true` tool result with the field and validation message, allowing
|
||||
the model to correct its arguments without an MCP internal-error response.
|
||||
Validation failures do not execute the tool.
|
||||
|
||||
These note tools run in the desktop gateway. Remote gbagent ACP sessions
|
||||
currently inject only the image MCP server, so this change requires no
|
||||
daemon implementation update.
|
||||
|
||||
## Validation
|
||||
|
||||
- `magic-note-storage.test.ts` uses real files and SQLite for migration equality,
|
||||
reopen/repair, binary deduplication, coordinated backup/restore and physical
|
||||
database shrink. Failure injection covers body rename, manifest/GC cleanup,
|
||||
delete/reset retries, post-conversion cancellation and VACUUM retry.
|
||||
- Canvas integration coverage includes `MagicCanvasEditor.test.tsx`,
|
||||
`MagicNoteContent.test.tsx`, `rich-content.test.ts`,
|
||||
`magic-note-analyzer.test.ts` and `tests/magic-note-canvas-text.test.ts`.
|
||||
`canvas-ipc.integration.test.ts` runs production preload and registered IPC
|
||||
handlers against real SQLite with mocked Electron transport and model output.
|
||||
Interactive workspace/SQLite and production core export evidence is recorded
|
||||
separately in [progress](./progress.md).
|
||||
- `assistant-database.test.ts` checks committed writes with a second SQLite
|
||||
connection, successful mutation coverage, failures, no-op completion and reset.
|
||||
- `knowledge-mcp-gateway.test.ts` exercises real database CRUD through Agent/MCP
|
||||
access, including revision failure without an extra notification.
|
||||
access, including revision failure without an extra notification. A real HTTP
|
||||
MCP client also checks searches above ten results, the published limit schema,
|
||||
invalid arguments and successful retry against SQLite.
|
||||
- `magic-notes-events.test.ts` executes the production preload, checking the
|
||||
channel, payload-free callback and listener removal.
|
||||
- `MagicNotesWorkspace.test.tsx` covers external updates and deletion, selection,
|
||||
source reload, editor identity and draft saves, coalescing, deferred writes,
|
||||
asynchronous selection races, error retry and unmount cleanup.
|
||||
asynchronous selection races, error retry and unmount cleanup. List-action
|
||||
coverage includes portal placement, keyboard navigation, dismissal, delete
|
||||
confirmation, target selection, revision failures and draft preservation.
|
||||
|
||||
No schema migration, dependency, refresh control or network service is added.
|
||||
Schema 38 and the file layout above govern current persistence; refresh
|
||||
notifications need no separate storage or network service. Dated validation
|
||||
results and remaining repository checks are owned by [progress](./progress.md).
|
||||
|
||||
@@ -42,6 +42,7 @@ User Stories 和逻辑设计;本页定义产品边界,技术设计定义唯
|
||||
认证信息。
|
||||
- 自定义值作为普通连接设置保存。API Key 和其他密钥必须使用专用凭据字段,不应写入
|
||||
自定义请求头或请求体。
|
||||
- LLM 连接可以复制,已保存的 API Key 由 Main 进程安全复制到新连接。
|
||||
|
||||
## 相关功能
|
||||
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# 模型连接请求定制进度
|
||||
|
||||
## 2026-09-20:模型设置可用性修复
|
||||
|
||||
- 模型接口 URL 保存失败时按连接名称显示错误,URL 字段同步提供 `aria-invalid` 和就地
|
||||
错误;共享契约同时拒绝空值、非 HTTP/HTTPS 地址和超长地址。
|
||||
- 模型连接分类标题与 LLM、向量、重排、语音分段页签在模型内容滚动时共同固定;自定义
|
||||
Header/Body 默认收纳在折叠的“高级设置”中,无效时自动展开。
|
||||
- LLM 连接新增复制入口。复制保留当前草稿、请求定制和认证设置;已保存 API Key 由 Main
|
||||
进程从加密凭据复制,不进入 Renderer 或错误信息。
|
||||
- 已新增 SettingsPanel 与 RuntimeSettingsStore 回归覆盖;完整验证结果见后续条目。
|
||||
|
||||
## 2026-09-11:发布前固定模型说明修复
|
||||
|
||||
- 项目已保存固定 Runtime 模型时,恢复显示实际固定模型的帮助文字,保留简化后的菜单;
|
||||
|
||||
@@ -76,6 +76,15 @@ API Key 继续由现有 Main 加密设置和 Prompt-scoped Agent 凭据路径管
|
||||
Body 是普通可见连接设置,不提供第二套通用秘密存储;UI 必须提示不要把密钥放入这两个
|
||||
JSON 对象,标准认证 Header 也由 schema 拒绝。
|
||||
|
||||
模型连接输入使用 `modelConnectionUrlSchema`,先在 Renderer 以连接名称标识无效 URL,
|
||||
再由共享契约拒绝空值、超过 2048 个字符或非 HTTP/HTTPS 地址。页头错误保留完整错误上下文,
|
||||
字段错误通过 `aria-describedby` 关联到 URL 控件。
|
||||
|
||||
复制连接不在 Renderer 读取或暴露已保存 API Key。复制草稿提交
|
||||
`{ action: 'copy', sourceProfileId }`,Main 在同一设置更新事务中从当前加密凭据或默认连接
|
||||
的环境凭据创建目标凭据。源连接不存在时整个更新失败;目标连接的其他字段仍由普通输入
|
||||
校验。正在编辑但尚未保存的 Key 仍以一次性 `replace` 值提交,因而不与旧凭据混合。
|
||||
|
||||
## 7. 验证
|
||||
|
||||
- 共享 schema:JSON 类型、边界、保留字段和优先级。
|
||||
@@ -84,7 +93,8 @@ JSON 对象,标准认证 Header 也由 schema 拒绝。
|
||||
- Runtime:真实 bundled OpenCode/Continue 对 loopback Provider 的请求探针;DeepSeek
|
||||
Utility 启动配置和 Pi-AI Provider 配置。
|
||||
- 远程:Main/Agent 网关合并、认证优先级、摘要绑定、大小限制和不重放。
|
||||
- UI:默认值、保存、无效 JSON 保留和 Runtime 支持说明。
|
||||
- UI:默认值、保存、无效 JSON 保留、连接级 URL 错误、粘附标题/页签、折叠高级设置和
|
||||
包含凭据的连接复制。
|
||||
|
||||
## 8. 通道直连模型选择修复
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
## 信息架构
|
||||
|
||||
“自定义请求头”和“自定义请求体”位于当前 LLM 连接详情的最底部、连接兼容性摘要之前。
|
||||
它们不新增页签、弹窗或高级模式。每个编辑器使用可纵向调整大小的等宽文本域,默认值为
|
||||
它们收纳在默认折叠的“高级设置”中,不新增页签或弹窗。只要其中一项无效,保存时自动
|
||||
展开该区域并保留对应字段错误。每个编辑器使用可纵向调整大小的等宽文本域,默认值为
|
||||
`{}`。
|
||||
|
||||
## 编辑与保存
|
||||
@@ -17,6 +18,13 @@
|
||||
`aria-invalid` 和就地错误;点击保存时阻止写入,并在现有设置错误区域显示对应连接。
|
||||
- 保存成功后,编辑器使用两个空格缩进重新格式化已保存对象。
|
||||
- 新连接从两个空对象开始。
|
||||
- 保存前校验失败时,页头错误和对应字段错误都显示连接名称;模型接口 URL 同时标记
|
||||
`aria-invalid` 并关联就地错误,避免只显示无法定位的“Invalid URL”。
|
||||
- LLM 模型连接的分类标题和模型类型分段页签在内容滚动时共同粘附在内容区顶部;左侧连接
|
||||
列表仍可独立滚动。
|
||||
- 每个已保存或正在编辑的 LLM 连接提供“复制连接”。复制会生成新的连接名称和 ID,保留
|
||||
地址、模型、协议、请求定制、认证方式及当前草稿 API Key。已保存的 API Key 通过 Main
|
||||
进程的加密凭据复制路径保存,不回传到 Renderer;原连接和默认连接不改变。
|
||||
|
||||
## 兼容性说明
|
||||
|
||||
@@ -24,6 +32,8 @@
|
||||
该值。请求体说明明确列出直连、Continue 和受管远程 OpenCode 支持,并说明本地
|
||||
OpenCode 与 DeepSeek Harness 不使用该值。
|
||||
|
||||
两项说明使用标签同行的 `InlineHelp`,交互遵循[同行帮助规范](../../../UI-DESIGN.md#614-inlinehelp-同行帮助)。下列警告及字段错误保持常显。
|
||||
|
||||
同一区域还必须说明:
|
||||
|
||||
- Runtime、协议和认证生成的字段优先。
|
||||
@@ -32,7 +42,7 @@ OpenCode 与 DeepSeek Harness 不使用该值。
|
||||
|
||||
## 无障碍与键盘
|
||||
|
||||
- 每个文本域有稳定的可访问名称,并通过 `aria-describedby` 关联兼容性说明。
|
||||
- 每个文本域有稳定的可访问名称,并通过 `aria-describedby` 关联兼容性说明。帮助关闭时保留隐藏说明,展开时替换为同 ID 的浮层,避免关联丢失或 ID 重复。
|
||||
- 无效状态不能只依赖颜色,必须同时提供错误文字和 `aria-invalid`。
|
||||
- 文本域遵循标准键盘编辑、选择、复制和粘贴行为;保存继续使用设置页唯一的主保存操作。
|
||||
- 焦点边框、字体、间距、颜色和深浅主题全部复用全局语义令牌。
|
||||
@@ -49,7 +59,7 @@ Runtime 入口恢复跟随。该展示规则不改变模型解析、远程 Agent
|
||||
DeepSeek Harness 的 `platform` 选项命名为“管理员环境优先,回退 GoodBuddy 兼容连接”,
|
||||
不能沿用 OpenCode/Continue 的“Runtime 自有配置”标签。来源规则以
|
||||
[Harness 模型配置](../deepseek-harness/technical-design.md#121-配置来源)为准。
|
||||
帮助文字与选择器通过 `aria-describedby` 关联,并明确此选项不加载用户 profile 或
|
||||
帮助文字收进标签同行的 `InlineHelp`,与选择器通过 `aria-describedby` 持续关联,并明确此选项不加载用户 profile 或
|
||||
配置文件;无兼容 GoodBuddy 连接时仍可选择管理员环境来源。
|
||||
|
||||
概览使用 Main 提供的[平台来源投影](./technical-design.md#9-harness-平台来源公开投影),
|
||||
|
||||
@@ -9,9 +9,13 @@
|
||||
| --- | --- |
|
||||
| [远程主机与 Agent 技术设计](./technical-design.md) | 当前产品语义、Agent、Runtime、Workspace、协议、生命周期和验证事实 |
|
||||
| [远程环境准备技术设计](./environment-provisioning-technical-design.md) | Host 下载、GoodBuddy 传输、安装事务、更新和恢复边界 |
|
||||
| [执行清单验证记录](./runtime-checklist-validation.md) | 远程 OC/CN 原生清单、CN 开发包安装、取消及断线重放的实测结果与剩余验收 |
|
||||
|
||||
现有文档包含产品、功能逻辑和进度事实,后续修改远程功能时再按标准职责拆分。
|
||||
|
||||
跨会话共享 OpenCode 进程、ACP 连接与模型桥的实现设计,见
|
||||
[Runtime 进程复用技术设计](../assistant-workbar/runtime-process-reuse-technical-design.md#6-ssh-opencode-与-agent)。
|
||||
当前实现尚未发布;验收状态见[工作栏进度](../assistant-workbar/progress.md#2026-09-14-runtime-进程复用实施中)。
|
||||
|
||||
更新退役、任务完成后 Runtime 回收和下一轮历史恢复的当前源码验证,见
|
||||
[2026-09-23 生命周期验证](./technical-design.md#生命周期验证2026-09-23)。
|
||||
|
||||
@@ -16,6 +16,10 @@ bootstrap 能力元数据。公开直连能力仍须完成本文末尾 GitHub/
|
||||
[SSH 远程主机与 GoodBuddy Agent 实现说明](./technical-design.md)
|
||||
和源码为准。
|
||||
|
||||
2026-09-18 补注:现有单 Runtime 复合包格式也支持 CN,安装器按包内 Runtime 身份
|
||||
选择并登记 payload。CN 开发包的 Linux 验证及联合分发的剩余工作见
|
||||
[执行清单验证记录](./runtime-checklist-validation.md#cn-部署与生命周期)。
|
||||
|
||||
## 设计结论
|
||||
|
||||
将安装和更新从项目切换移到 Host 管理是合理的,原因如下:
|
||||
@@ -157,8 +161,15 @@ daemon 参与。两种 acquisition 的差别只在于如何把同一个签名 co
|
||||
4. commit 后在同一条已固定身份的 SSH lease 上执行 Agent bootstrap/readiness 和 Runtime
|
||||
activate。新 Agent 最多完整验证一次,新 Runtime 最多完整验证一次;注册成功后的
|
||||
health、capabilities 和 prompt 启动只读取 registry、签名 manifest 与入口 metadata。
|
||||
5. Main 确认 adoption 后显式执行 cleanup。cleanup 失败不回滚已经健康的 Agent/Runtime,
|
||||
也不阻塞下一次更新。
|
||||
5. Agent adoption 和所有 Runtime 激活成功后,Main 通过新安装执行 `cleanup-obsolete`,
|
||||
尽力回收已证明停止且无待处理工作的旧 Agent payload。命令返回逐安装的延期原因,
|
||||
不支持该命令的旧包或清理失败不会回滚健康环境。随后显式清理本次 operation staging;
|
||||
staging cleanup 失败也不阻塞下一次更新。
|
||||
|
||||
新安装成为 current 后,包含退役逻辑的旧 Agent 排空已有任务再退出;当前 Agent 可以常驻。
|
||||
仍存活且缺少退役逻辑的 legacy daemon 不会被升级清理命令终止,不能用无连接推断其空闲。
|
||||
共享 Runtime digest 目录、历史和 Workspace 保留。清理条件、延期限制及验证记录见
|
||||
[远程主机技术设计](./technical-design.md#升级残留清理)。
|
||||
|
||||
这不是两个并行安装管理器,也不存在把 Agent 与 Runtime payload 逐文件走 SFTP 安装的
|
||||
第二条路径。两种 acquisition 共享相同的 prepare、commit、adoption、finalize 与 cleanup。
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
# 远程执行清单验证记录
|
||||
|
||||
日期:2026-09-18。对应 CL-P1、CL-P3、CL-P4;需求与统一契约见
|
||||
[执行清单设计](../assistant-workbar/runtime-checklist-technical-design.md)。
|
||||
本记录包含远程 Agent 到 Main Runtime 的事件验证,以及后续 Windows 完整桌面到
|
||||
Linux CN 的安装、清单展示、保存、重启和取消验证;各阶段覆盖范围分别列出。
|
||||
|
||||
## OC 实现与原生证据
|
||||
|
||||
共享 Linux x64 经 `192.168.0.23` 连接,使用已保存的 SSH 凭据和固定 Host Key。
|
||||
未使用 VPN 路由,不计作第二台 Host。Agent 由当前源码构建到专用测试目录,复用该机
|
||||
受管 Node 24.19.0 和签名 OC 包;manifest 与二进制 `--version` 均为 1.18.29。
|
||||
|
||||
真实模型按提示两次调用 `todowrite`:先写入一个 `in_progress/high` 条目,再写入
|
||||
`todos: []`。未适配时两次工具均成功,ACP 只有 `tool_call/tool_call_update`,
|
||||
没有 `plan`。上游同版本
|
||||
[acp/event.ts](https://github.com/anomalyco/opencode/blob/v1.18.29/packages/opencode/src/acp/event.ts)
|
||||
也未处理 `todo.updated`,因此不能把 ACP 类型定义当作部署证据。
|
||||
|
||||
现有 `opencode-subagent-plugin.ts` 将精确原生 `todo.updated` 放入既有 ACP metadata,
|
||||
由 Agent 原 Session 分派、operation 归属和 semantic transcript 传递。
|
||||
`acp-remote-runtime.ts` 在工具截断之前消费该结构并校验 `RuntimeChecklist`。
|
||||
原 Session 与当前 Prompt 不符、无效条目均不替换清单。工具活动保留;正文及泛化
|
||||
工具名不触发更新。`cancelled` 原样保留,避免 ACP plan 只允许三种状态造成信息损失。
|
||||
标准 OC `plan.entries` 的结构化转换也有测试,但本版本的真实成功路径使用插件原生事件。
|
||||
|
||||
## 共享 Host 结果
|
||||
|
||||
最终源码复验目录为 `/root/tmp/gb-acp-recovery-4bLjbp`,首次通过目录为
|
||||
`/root/tmp/gb-acp-recovery-SJbdZQ`。首次清单抵达桌面运行时后,在其
|
||||
semantic checkpoint 被消费前关闭 attach。随后恢复同一 controller、binding 和
|
||||
operation,设置 `remoteRecoveryOnly: true`,没有再次提交 Prompt。
|
||||
|
||||
| 验证项 | 结果 |
|
||||
| --- | --- |
|
||||
| 原生清单新增 | sequence 1,`todo.updated`,一个 `in_progress/high` 条目 |
|
||||
| 未 ACK 清单重放 | 重连后仍为 sequence 1,内容相同 |
|
||||
| 原生清空 | sequence 5,`todos: []`,映射为 `{source:'opencode',items:[]}` |
|
||||
| 重连后完成 | 两份清单、工具活动和终态继续到达 |
|
||||
| 额外模型调用 | 重连未重发 Prompt;该次总计 3 个真实 provider 请求 |
|
||||
| 清理 | Agent 停止后,测试目录所属残留进程 0;临时上传及运行目录已删除 |
|
||||
|
||||
更长条目、取消状态、非法输入、子 Session、标准空 plan、工具活动不重复生成清单、
|
||||
相同 provenance 重放,由单元测试覆盖。远程真实模型的长清单、取消及连续多轮隔离
|
||||
本次未实测,不能将单元测试记为真实 Host 验收。
|
||||
|
||||
一次较早的重放探测在 `done` 后才断开,之前已 ACK 的记录被正常裁剪,故不能从
|
||||
sequence 0 重新读取整段历史。该次断言失败后,改为验证未 ACK 边界;产品未增加
|
||||
历史快照或另一套重连机制。已 ACK 内容的恢复仍依赖 Main 已提交的消息。
|
||||
|
||||
另复现 OC 冷启动超过默认 15 秒控制超时:连续五次请求未进入 provider。
|
||||
将 `runtime/startPrompt` 控制等待提高到至少 30 秒后,原生初始化、建会话和模型选择
|
||||
完成。对应测试模拟 20 秒启动,确认仅提交一次 start;其他控制请求时限保持原设置。
|
||||
|
||||
## 模型调用口径
|
||||
|
||||
统计为测试 Agent 中实际向非 loopback provider 发出的 HTTP 请求,不将 ACP Prompt
|
||||
次数、工具调用、内部 `/session` 路由请求或重连计作模型调用。读取已有 OC 模型配置,
|
||||
测试未打印凭据或端点。
|
||||
|
||||
| 探测 | 实际模型请求 |
|
||||
| --- | ---: |
|
||||
| 五次冷启动定位 | 0 |
|
||||
| 提高启动等待后的原始 ACP 观察 | 3 |
|
||||
| 输出工具状态、确认两次 todowrite 成功 | 3 |
|
||||
| 插件接入后在 done 边界重放,断言失败 | 3 |
|
||||
| 最终未 ACK 边界断线重放通过 | 3 |
|
||||
| 最终源码再次复验未 ACK 边界 | 3 |
|
||||
| CN Linux 包与命令入口探测 | 0 |
|
||||
| 合计 | 15 |
|
||||
|
||||
五次进入 provider 的 OC 探测均返回三个 HTTP 200;无受控假 provider 调用。
|
||||
|
||||
## CN 部署与生命周期
|
||||
|
||||
已从 npm 获取固定 `@continuedev/cli@1.5.47` 工件,压缩包为 17,945,195 bytes,
|
||||
并在 Host 校验 SHA-512:
|
||||
|
||||
```text
|
||||
gtpewV3RoIOD9dyTtKIBi1SY0VOHRu3Ehe7C/mmnswm+j34MPyrcQhQaWj/m+jdfGO4fNIKdrgGIlLso1ULDFw==
|
||||
```
|
||||
|
||||
在 `/root/tmp/gb-checklist-upload-44f47e99-94e2-4e35-8efb-ef80b7a830ca` 解包,
|
||||
以受管 Node 24.19.0 执行 `dist/cn.js --version` 得到 1.5.47;`serve --help`
|
||||
确认 HTTP `/state`、`/message` 入口。`acp --help` 返回普通顶层帮助,并非 ACP
|
||||
子命令;工件 `src/index.ts` 有 serve 注册、没有 acp 注册。探测未启动模型会话,
|
||||
没有把包入口可运行记为远程 CN 功能完成。测试工件已从 Host 清理。
|
||||
日志留存复验目录为 `/root/tmp/gb-checklist-upload-424f412c-cf2a-4857-810f-48da03285821`,
|
||||
同样通过并清理,模型请求仍为 0。
|
||||
|
||||
以下为同日后续实施结果,取代最初调查时的未实现清单。
|
||||
|
||||
| 路径 | 当前实现 |
|
||||
| --- | --- |
|
||||
| 受管工件 | lock 固定 `@continuedev/cli@1.5.47`、SHA-512 和 `lib/continue/dist/cn.js`;组包保存 payload、manifest、签名和许可文本 |
|
||||
| 工件安装 | Runtime 与复合 Agent 包校验接受 CN;桌面按 descriptor 的 Runtime ID 选择 payload,Host 复用 `prepare/commit` 和 Runtime registry |
|
||||
| Agent 启动 | `continue-acp-helper.ts` 在 Host 上复用 `ContinueHostAdapter`,启动 Node 驱动的 CN HTTP 宿主,对既有 Agent 提供 ACP Session 接口 |
|
||||
| 模型桥 | Main 选择 CN 模型 profile,Agent gateway 保留逐 operation 的模型配置;CN 请求携带 Session/operation 路由头,配置中使用桥接占位凭据 |
|
||||
| 清单 | 宿主在原始参数层提取成功的 `Checklist.checklist`,经 `_meta.goodbuddyChecklist` 和原 semantic transcript 输出统一 `checklist` 事件 |
|
||||
| 项目及聊天 | 创建、保存、安装激活、Main Runtime 工厂和聊天菜单均接受 CN;项目默认值与历史 CN 选择保持;安装身份必须匹配选择 |
|
||||
| 生命周期 | ACP cancel 取消本轮宿主,已有 attach、transcript/ACK 负责断线恢复;后续 Prompt 可在同一对话继续 |
|
||||
|
||||
最终复验目录为 `/root/tmp/gb-cn-runtime-EALG99`。开发复合包为 188,609,399 bytes,
|
||||
使用临时签名身份和隔离 trust registry,没有更改应用或 Host 的正式信任配置。
|
||||
桌面验包函数、Host `prepare/commit`、安装后 Agent `bootstrap/health` 与 CN
|
||||
`runtime activate` 均通过。随后当前源码 Agent 使用该安装得到的 CN payload 执行下列场景:
|
||||
|
||||
| 场景 | 实测结果 |
|
||||
| --- | --- |
|
||||
| Execute 原生清单 | sequence 3 为一个 pending 条目,sequence 6 为显式空列表 |
|
||||
| 未 ACK 断线重连 | 同一 binding/operation 重放原 sequence 3,再收到空清单;不重发 Prompt |
|
||||
| 取消 | 新一轮产生清单后取消,Main 返回 `RemotePromptCancelledError`;清单不改写为完成 |
|
||||
| 同对话续发 | 切换 Ask 后返回 `RECOVERED`,没有旧清单事件 |
|
||||
| 回收 | Agent 停止后测试所属进程 0;隔离目录和上传目录已删除 |
|
||||
|
||||
本轮实际向外部 provider 发出的 HTTP 请求数为 **17**:首次取消探针 5 次,修正探针对
|
||||
预期取消异常的处理后 6 次,最终安装包复验 6 次。三轮分别取得 4、5、5 个 HTTP 200,
|
||||
各有 1 次请求在取得响应前取消。安装探针的四次排错运行均为 0 次;重连没有额外提交。
|
||||
此前已有 `cn-runtime-live.log` 记录过 3 次 CN 请求,属于接手前证据,不计入本轮 17 次,
|
||||
也不并入上方原 OC 阶段的 15 次。本轮全部 CN 进程均运行于 Linux Host。
|
||||
|
||||
安装排错包括复用旧缓存时缺少 Koffi CJS 文件,以及探针把安装器 CLI 一起内联后污染
|
||||
relay stdout。当前组包实现已包含 Koffi CJS 文件;探针改为单独导入安装器模块。
|
||||
这些运行未进入模型。最终日志为临时目录中的 `cn-runtime-live.log`。
|
||||
|
||||
本阶段验证从 Main 受管 Runtime 工厂进入。后续完整桌面与联合开发包结果见下节。
|
||||
CN 的 Linux arm64/macOS arm64 尚未实测;本轮没有提交或发布 Agent 包。
|
||||
|
||||
## 2026-09-19 联合包与完整桌面验收
|
||||
|
||||
默认 Agent 组包与 CI 构建已同时包含 OC 1.18.29、CN 1.5.47。描述符保留主 Runtime,
|
||||
通过 `additionalRuntimes` 描述随包安装的 CN;目录、桌面验包、Host 安装和资源加载
|
||||
均校验该列表。单 Runtime 包仍可读取,已有发布包无需迁移。联合包专项测试为
|
||||
31 passed、1 skipped,包含生产安装器对单包及联合包的安装回归。
|
||||
|
||||
完整桌面使用当前生产 Main、Preload 和 Renderer,隔离 profile 与开发签名 registry,
|
||||
保留 sandbox/context isolation。连接由既有凭据和固定 Host Key 到 `192.168.0.23`,
|
||||
再转发至同机专属 SSH 服务;该服务将 HOME 限定在 `/root/tmp/gb-joint-ui-2oQ87z/home`。
|
||||
本地 loopback 地址仅为 SSH 转发入口,CN 进程实际运行于 Linux。原生文件选择对话框
|
||||
由测试驱动返回开发包路径;其后的导入、验签、传输和安装均走生产 IPC。
|
||||
|
||||
桌面完成联合包导入和“由 GoodBuddy 传输”安装后,通过实际项目表单创建 CN 远程项目,
|
||||
选择 Execute,在真实输入框发送清单请求。两次 Checklist 更新后,顶部显示一项完成、
|
||||
一项待办,工具活动仍可见;保存和退出重启后,原项目保留这两项状态。
|
||||
|
||||
重启续发首次实测失败:CN 内存原生会话已退出,但旧 ready binding 仍引用其 Session;
|
||||
随后恢复同一个 start 请求又重复递增序号。修复后冷打开 CN ready binding 会从现有
|
||||
对话历史新建原生会话,未确认 start 的恢复保持原 operation 和序号。相邻 4 文件
|
||||
167 passed、1 skipped;新构建的真实桌面已验证重启续发成功、清单出现后点击停止、
|
||||
取消条目仍为待办,以及随后返回 `REMOTE_CN_RESUMED` 且不显示旧轮清单。
|
||||
|
||||
测试启动器曾遗漏托盘资源,以及误清除托管 Node 所需的 `ELECTRON_RUN_AS_NODE`,导致
|
||||
前一次构建未刷新产物;修复启动器后核对产物包含源码修复,再进行上述成功复验。
|
||||
这两项是临时驱动问题。最初测试计数钩子未覆盖远端模型出口,改从隔离 Agent 的
|
||||
`model-calls.sqlite` 读取 operation、dispatch 状态及取消结果,仅导出计数,不读取凭据。
|
||||
|
||||
首轮全量测试为 4707 passed、67 skipped、1 failed;失败为本地 OpenCode 压缩返回
|
||||
`compacted: false`。随后单独复验该文件及远程 ACP、项目选择、顶部清单,共 136 passed。
|
||||
不将专项复验写成一次全量全绿。类型检查和 lint 已通过。
|
||||
|
||||
完整桌面截图位于批准临时目录的 `joint-cn-real-before-restart.png` 和
|
||||
`joint-cn-cancelled.png`,均已打开审阅。窗口中状态与内容对齐,取消后保留待办,
|
||||
输入框仍可使用。UI 驱动为 `joint-desktop.cjs`、`joint-scenario.ps1`;共享 Host
|
||||
及隔离 profile 由 `joint-ui-host.ts` 管理。
|
||||
|
||||
补充真实长清单测试收到 55 项、5,601 字符的条目正文,数据库读取数量和字符数一致。
|
||||
随后成功调用空字符串清单,顶部隐藏;再次退出重启并打开原项目,保存的空数组仍有效,
|
||||
没有复活旧清单。自动断言摘要保存在批准临时目录的 `joint-final-evidence.json`,覆盖
|
||||
SSH 项目身份、CN 选择、长清单落库、取消保留待办、取消后续发和清空后的重启状态。
|
||||
|
||||
本轮新增模型请求以隔离 Agent gateway 调用账本为准,共 **16 次**,15 次 completed,
|
||||
1 次 outcome-unknown/cancelled。三轮创建并更新两项清单分别 3 次;取消场景 2 次;
|
||||
取消后续发 1 次;55 项长清单 2 次;清空 2 次。旧会话失败和启动排错未新增 gateway
|
||||
dispatch。本轮 16 次与上文 CN 17 次、早期 OC 15 次分别记录,不重复累计。
|
||||
|
||||
清理后已用原固定身份重新连接 Host,确认测试目录不存在、所属进程为 0;本地隔离
|
||||
profile 及其加密测试凭据已删除。正式 Host 安装、应用配置和业务文件未改动。最终
|
||||
typecheck、lint、差异检查通过。尚未执行 Linux arm64/macOS arm64 真机与多远程
|
||||
会话同时生成清单的完整桌面矩阵;隔离与去重仍有定向测试覆盖,不据此扩大真机结论。
|
||||
|
||||
## 测试与复查路径
|
||||
|
||||
源码相邻测试:7 个文件,185 passed、6 skipped。覆盖 ACP Runtime、协议通道、
|
||||
managed Runtime、原生插件、Agent-owned Prompt、ACP connection 和 model bridge。
|
||||
|
||||
```text
|
||||
npx vitest run src/main/agent/acp-remote-runtime.test.ts src/agent-daemon/opencode-subagent-plugin.test.ts src/main/remote-agent/protocol-remote-runtime-channel.test.ts src/main/remote-agent/managed-remote-acp-runtime.test.ts src/agent-daemon/agent-owned-acp-prompt.test.ts src/agent-daemon/agent-acp-connection.test.ts src/agent-daemon/model-bridge.test.ts
|
||||
npm run typecheck
|
||||
npm run lint
|
||||
```
|
||||
|
||||
Typecheck、lint 通过。`npm test` 在 240 秒时超时,未得到全量完成结果;此前
|
||||
`src/renderer/src/App.test.tsx` 两项 Magic Notes 测试因 `pdfjs` 的 `DOMMatrix is not defined`
|
||||
失败。本次未修改该 Renderer 路径。
|
||||
|
||||
本机临时探测目录为 `C:\Users\jiang\AppData\Local\Temp\opencode`,保留这些脚本供复查:
|
||||
|
||||
- `checklist-host-driver.ts`:读取既有凭据、SSH 固定身份、部署源码 Agent、清单及断线断言。
|
||||
- `checklist-host-build.cjs`、`checklist-host-run.cjs`:构建和 Electron 执行入口;`--verify` 跑 OC,`--cn-only` 跑 CN 无模型探测。
|
||||
- `checklist-host-daemon.ts`:只记录原生 ACP 初始化阶段名称的诊断包装。
|
||||
- `checklist-cn-probe.cjs`:CN 工件完整性与 Linux CLI 入口验证。
|
||||
- 复用 `acp-recovery-host-build.cjs`、`acp-recovery-host-daemon.ts` 和 `image-host-agent.ts`,没有复制凭据文件。
|
||||
|
||||
最终 stdout 证据分别为该目录的 `checklist-oc-final.log` 和 `checklist-cn-final.log`。
|
||||
文档审校扫描无阻断项;复核项为测试范围列举及必要的未验证边界,按事实保留。
|
||||
|
||||
构建顺序为先运行 `acp-recovery-host-build.cjs`,再运行 `checklist-host-build.cjs`,
|
||||
最后运行 `checklist-host-run.cjs --verify`。真实 OC 探测会调用模型;早期 `--cn-only`
|
||||
探测只检查 CN 入口,不调用模型。
|
||||
|
||||
后续 CN 全路径复验使用 `cn-runtime-build.cjs` 构建当前源码与临时签名复合包,再运行
|
||||
`checklist-host-run.cjs --continue --verify --lifecycle`。该命令会发出真实模型请求,
|
||||
与前述只运行 version/help 的 `--cn-only` 不同。`cn-runtime-daemon.ts` 在隔离 Home
|
||||
调用桌面验包及 Host 安装器,验证 CLI 激活,然后使用当前源码 Agent 持有模型轮次;
|
||||
provider HTTP 请求计数在 Agent gateway 的外部 fetch 层完成。
|
||||
|
||||
后续专项验证:远程相关 27 个测试文件、374 项通过;新增 CN 复合包测试经过桌面完整
|
||||
验包与生产安装器的 prepare/commit。全量运行完成,4665 passed、67 skipped、6 failed;
|
||||
其中一项是运行期间新增 CN 测试尚未修正的归档名,随后专项复验通过。其余五项为
|
||||
`ActivityPanel.test.tsx` 与 `activity-store.test.ts` 的引用复用断言,本轮未修改对应实现。
|
||||
这两个文件在并行工作区继续更新后重新运行,39 项全部通过;全量运行中的失败项均已
|
||||
经后续专项复验,但没有重新运行全部测试来取得同一最终快照的全绿报告。
|
||||
最终另复验项目选择、顶部清单和安装管理 3 文件、41 项通过;App 远程 Runtime 选择与
|
||||
继承 3 项通过。`npm run typecheck`、`npm run lint` 均通过。中文审校扫描阻断项为 0,
|
||||
复核项为测试列举、请求数分项与必要验收边界,按事实保留。
|
||||
|
||||
## 2026-09-19 Continue 会话 MCP 交付回归
|
||||
|
||||
模型桥 helper 原先把 Session MCP 写入原生配置,但独立模型 profile 生成分支只保留
|
||||
显式能力,导致 Execute 最终配置丢失 MCP。修复后通过 run options 传递会话 HTTP
|
||||
服务器;本机原生 MCP 隔离和 Ask 只读规则不变,具体机制见[Runtime](./technical-design.md#runtime)。
|
||||
`continue-acp-helper.test.ts` 联合运行实际 helper 与 adapter 配置生成器,覆盖初次交付、
|
||||
续接替换、Ask 排除、加载空列表以及本机 profile 不继承原生 MCP。
|
||||
|
||||
共享 Linux x64 Host 经 LAN 固定身份连接,使用保存的 GoodBuddy 凭据,当前源码 Agent、
|
||||
helper、adapter 与 CN 1.5.47 在专用目录运行。Main 受管 Runtime → Agent-owned ACP →
|
||||
Continue → Agent HTTP MCP → Main 工具桥往返通过;仅最末端图片服务替换为无计费
|
||||
测试接收端,校验输入并返回 `SAFE_MCP_ROUNDTRIP`,未连接图片 Provider。
|
||||
同一对话依次 Execute、Ask、Execute:两次 Execute 各实际调用工具一次,Ask 没有工具调用
|
||||
并回复 `ASK_NO_IMAGE_TOOL`;三次均正常终结。最终源码再次构建后完整重复该顺序。
|
||||
|
||||
首轮成功目录 `/root/tmp/gb-cn-runtime-2kQ7wc`,最终复验目录
|
||||
`/root/tmp/gb-cn-runtime-NNM89D`。每轮 5 次真实文本 Provider HTTP 请求,合计 **10 次**,
|
||||
全部 HTTP 200;每轮上限 6 次,单次最大输出 1,024 tokens,单 Prompt 验证超时 120 秒。
|
||||
图片 Provider 请求 **0 次**。此前一次测试目录权限错误在 Runtime 验证阶段退出,模型请求
|
||||
0 次;修正测试目录权限后通过,未放宽产品校验。每轮结束确认所属剩余进程 0,专用运行
|
||||
及上传目录已清理,原 Host 安装未修改。此验证不包含正式包安装、UI 点击或真实图片生成。
|
||||
|
||||
复查脚本位于上述本机临时目录:`mcp-host-build.cjs`、`mcp-host-run.cjs`,
|
||||
最终日志 `mcp-host-live.log`。专项 4 文件 38 项通过;改动文件 ESLint 和 Agent 类型检查
|
||||
通过。主任务随后完成合并后的 `npm test`:405 个文件通过、9 个跳过,4,826 项通过、
|
||||
67 项跳过,耗时 982.48 秒,无失败;最终完整类型检查与 lint 通过。
|
||||
@@ -49,8 +49,8 @@ Agent `0.11.23` 提供,显式请求期限与取消仍有效。当前问答源
|
||||
|
||||
## 产品语义
|
||||
|
||||
远程 Host 与项目能力是默认关闭的技术预览。用户在“设置 > 平台功能”的独立
|
||||
“远程项目(技术预览)”页签启用开关后,Renderer 才显示 SSH Host 管理、托管 SSH 项目创建和已保存
|
||||
远程 Host 与项目能力默认关闭。用户在“设置 > 平台功能”的独立
|
||||
“远程项目”页签启用开关后,Renderer 才显示 SSH Host 管理、托管 SSH 项目创建和已保存
|
||||
远程项目;Main 在 IPC 边界执行同一开关校验,避免隐藏界面被绕过。关闭开关不删除 Host、
|
||||
项目或凭据;当前远程项目切回第一个普通本地项目。应用启动仍固定
|
||||
选择普通本地项目,不会因为保存了远程项目而自动连接 Host。
|
||||
@@ -185,7 +185,36 @@ Detached GoodBuddy Agent
|
||||
解析新的 current registry 并建立当前连接,无需刷新项目记录;当前环境无效时要求用户
|
||||
显式修复,但不在项目切换中下载或安装。失败或取消保留 Host 配置、凭据、项目、
|
||||
Workspace 和旧组件。
|
||||
- 显式 stop、升级、身份冲突或进程退出时清理 GoodBuddy 自己的 socket、状态和子进程;不得删除或覆盖无关 Host 文件。
|
||||
- 当前 Agent 可以常驻。新安装提升为 current 后,已包含退役逻辑的旧 Agent 每 250 ms 检查 registry 并进入
|
||||
draining,不再接受新 Prompt;无活动任务时退出,有任务时等待完成。已连接 Desktop 的
|
||||
终态 transcript ACK 也计入排空条件;断线任务完成后不等待离线 Desktop。待答问题仍是
|
||||
活动任务。旧安装退出前可以重新 Attach 原任务,但不得重新 bootstrap 已退役安装。
|
||||
- Prompt 完成后释放空闲 Runtime;共享进程仍有其他活动任务时保留,最后一个任务完成后
|
||||
关闭并核对进程树为空。保留语义 transcript 与会话标识;下一轮重建 Runtime,OpenCode
|
||||
加载原生 session,Continue 通过已有冷启动路径从持久对话历史初始化。
|
||||
- 显式 stop/retire 可校验并停止已被 registry 替换的旧安装。退出只清理 GoodBuddy 自己的
|
||||
socket、状态和子进程,不删除会话历史,不删除或覆盖无关 Host 文件。
|
||||
|
||||
### 升级残留清理
|
||||
|
||||
Agent adoption 和所有 Runtime 激活成功后,Main 调用新安装的
|
||||
`cleanup-obsolete --installation-id <current-id>`。清理由当前 Agent 代码执行,不调用
|
||||
旧安装的二进制。命令扫描现存 Agent installation 目录,跳过 current 和 candidate,
|
||||
返回 `complete`、`removed` 与逐安装的 `deferred` 原因。清理不可用或存在延期项时记录
|
||||
警告,已成功的安装不回滚;下次更新或再次调用该命令时重试,没有后台清理服务。
|
||||
|
||||
删除旧 payload 前须完整验证安装包,在既有 bootstrap lock 内确认原 daemon 的生命周期
|
||||
记录已失效,并只读检查已有 `semantic-prompts.sqlite` 与 journal。活动、结果未知、
|
||||
未确认的终态、尚存的 ACP channel,以及缺失或无法读取的记录都会阻止清理。随后根据
|
||||
该安装的 Runtime owner 记录回收可确认归属的孤儿进程;身份冲突、无法检查或仍有子进程
|
||||
的 owner 记录保留,等待重试。删除前还检查进程是否使用该 payload,并再次核对 registry。
|
||||
只删除已确认不再使用的 Agent 可执行文件目录,保留 state、历史和 Workspace。
|
||||
|
||||
**仍存活的 legacy daemon 不会被此命令回收。** 旧实现没有原子排空并拒绝新工作的操作,
|
||||
持久记录也不足以证明存活 daemon 的内存任务为空。因此即使没有连接或没有持久操作,
|
||||
仍返回 `live-or-unproven-daemon-inactivity`,不能把升级后的旧版本自动退出规则追溯应用于
|
||||
缺少退役逻辑的进程。此轮不裁剪共享 Runtime digest 目录,也不扫描 payload 已丢失而
|
||||
仅剩 state 的安装。用户报告的六个 daemon、五个旧 helper 和约 2.67 GiB 占用未据此宣告解决。
|
||||
|
||||
### Host 级环境生命周期
|
||||
|
||||
@@ -225,12 +254,18 @@ Main 传入已规范化的绝对 POSIX root。Agent 返回 Workspace identity、
|
||||
|
||||
## Runtime
|
||||
|
||||
首个远程 Runtime 固定为签名 OpenCode ACP bundle。
|
||||
托管 SSH 项目的 Composer Runtime 菜单只显示当前配置的 OpenCode 和管理入口,不显示
|
||||
直连模型、Continue 或 DeepSeek Harness。激活旧远程会话时,若其保存了其他 Runtime
|
||||
selection,Renderer 会恢复为当前 OpenCode 配置;Main 的远程请求校验仍是最终边界。
|
||||
托管 SSH 项目支持签名 OpenCode ACP bundle 和 Continue 1.5.47 HTTP 宿主。
|
||||
Composer Runtime 菜单显示 OpenCode、Continue 和管理入口,不显示直连模型或
|
||||
DeepSeek Harness。项目与历史会话保留 OC/CN 选择;不支持的旧 selection 恢复为项目
|
||||
默认值。Main 校验已安装 Runtime 身份与选择一致,按相应 CN/OC 模型 profile 建立模型桥。
|
||||
本机配置的 SKILL 包和 stdio MCP Server 不上传或分配给远程 Runtime;远程 ACP Session
|
||||
继续使用空 MCP Server 列表。Node.js、Python 等工具执行环境同样不修改或同步到 Host,
|
||||
仅接收当前请求由 Agent 管理的 HTTP MCP 能力(例如 Execute 图片工具),不继承本机原生
|
||||
MCP 配置。Continue helper 将 ACP Session 的服务器作为显式 `sessionMcpServers` 交给
|
||||
共享 `ContinueHostAdapter`,与模型桥配置一起生成最终配置;不先写入会被独立模型分支
|
||||
丢弃的原生配置。新建、加载和续接 Session 使用当前列表,空列表清除旧能力;Ask 在 helper
|
||||
和 adapter 两层均不注入这些 Execute 能力。本机独立模型 profile 仍不继承原生 MCP,
|
||||
既有知识库和自定义 MCP 的显式分配规则不变。
|
||||
Node.js、Python 等工具执行环境同样不修改或同步到 Host,
|
||||
完整边界见[工具执行环境](../local-tool-environment/README.md)。
|
||||
|
||||
### Ask
|
||||
@@ -273,16 +308,34 @@ Execute 直接启动已签名 Runtime:
|
||||
|
||||
## 执行清单接入范围
|
||||
|
||||
OC `todowrite` / CN `Checklist` 顶部展示属于待实施功能,权威需求及接入设计见
|
||||
OC `todowrite` / CN `Checklist` 顶部展示的权威需求及接入设计见
|
||||
[Runtime 执行清单](../assistant-workbar/runtime-checklist-technical-design.md),
|
||||
当前证据见[工作栏进度](../assistant-workbar/progress.md#2026-09-15-runtime-执行清单设计未实现)。
|
||||
远程实现与验证范围见[执行清单验证记录](./runtime-checklist-validation.md)。
|
||||
目标范围包括远程 GoodBuddy Agent 运行 OC 和 CN;ACP 仅承担传输与远端协议。
|
||||
|
||||
当前托管安装与实时 Runtime 路径仍只接受 OC,不能把通用 ACP plan/tool 传递代码视作
|
||||
远程 CN 已可用。CN 的受管工件、安装/启动、项目选择与校验、模型桥及原生清单传递
|
||||
需要在该功能实施时补齐,并在共享 Linux 真实 Host 分别确认 OC/CN 的可用程度。
|
||||
本次只完善文档,未实测任一远程 Runtime 的清单;已有 OC Ask/Execute、问答或重连证据
|
||||
不作为本功能通过证据。清单恢复复用下节流程,不增加独立持久化或重连系统。
|
||||
CN 1.5.47 的 `serve` 提供 HTTP `/state`、`/message`,没有原生 ACP 命令。
|
||||
Agent 的 `continue-acp-helper.ts` 复用 `ContinueHostAdapter` 启动该宿主,转换 ACP
|
||||
Session、文字、工具、问答、清单与取消。Main 冻结的模型配置仍通过既有 Agent gateway
|
||||
执行,CN 配置仅持有 loopback 路由及占位凭据。成功的原生清单经
|
||||
`_meta.goodbuddyChecklist` 进入同一 operation 的 semantic transcript,Main 校验 CN
|
||||
来源和统一 schema;普通工具参数和正文不生成清单。
|
||||
|
||||
2026-09-18 Linux x64 已通过 CN 开发复合包验包、安装、Agent 启动及 CN 激活,随后
|
||||
通过真实模型清单新增/清空、未 ACK 断线重放、取消和同对话 Ask 续发。默认组包现同时
|
||||
携带 OC/CN,`additionalRuntimes` 随主 Runtime 一起验签、安装和加载,仍可读取已有单
|
||||
Runtime 包。2026-09-19 完整桌面验证覆盖联合包安装、CN 项目创建、清单更新、重启和
|
||||
取消后续发;实测范围及尚未覆盖的平台见上述验证记录。开发包尚未发布。
|
||||
|
||||
2026-09-18 实测 OC 1.18.29 的 ACP 仅转发 `todowrite` 工具活动,没有原生 `plan`
|
||||
通知。已有 OC 插件现将精确 `todo.updated` 事件经 `_meta.goodbuddyTodoEvent` 转发,
|
||||
保留原 Session ID、完整条目及空数组。Main 校验原生事件类型、Session 和清单 schema
|
||||
后输出 `checklist`,不从工具标题、截断字符串或正文推测清单。标准 `plan.entries`
|
||||
也可映射 OC 清单;尚未确认的 `plan_update`、`plan_removed` 只形成活动状态。
|
||||
清单恢复复用下节流程及既有 semantic transcript/ACK;已通过首份清单未 ACK 时断开、
|
||||
同操作重连、随后收到空清单的真实 Host 验证。顶部显示与数据库历史由对应模块单独验收。
|
||||
|
||||
同次验证复现原生 ACP 冷启动超过 15 秒控制超时。`runtime/startPrompt` 的控制等待
|
||||
现至少为 30 秒,其余控制请求沿用原时限;Runtime 外层仍保留启动超时和取消处理。
|
||||
|
||||
## ACP 与断线
|
||||
|
||||
@@ -335,11 +388,21 @@ OC `todowrite` / CN `Checklist` 顶部展示属于待实施功能,权威需求
|
||||
未发布进程复用源码补齐 Desktop 重启后的多任务并行附加、实时待答快照及 Main 答案
|
||||
持久化,详见[恢复与终态投影](../assistant-workbar/runtime-process-reuse-technical-design.md#63-恢复与终态投影);
|
||||
不把该待答投影当作 durable transcript 或旧版本已经支持的行为。
|
||||
Desktop Main 接收实时问题时保存其消息位置,回答后不移动到后续输出之后;实时待答
|
||||
快照不伪造语义来源。位置去重、旧会话边界见
|
||||
[Runtime 交互边界](../assistant-workbar/runtime-interactions.md#运行与失败),
|
||||
当前源码的真实 Host 证据见[问答位置持久化](../assistant-workbar/progress.md#2026-09-19-问答位置持久化)。
|
||||
- ACP 工具结果同时支持文本 `content` 和 `rawOutput.output`,移除 OpenCode 的
|
||||
`<task><task_result>` 外层包装后渲染正文。恢复已有子代理时使用 ACP 所带的所属
|
||||
`toolCallId` 继续路由,不等待新的父 Task metadata,也不重放模型请求。
|
||||
远端实时过程需要包含该插件的 Agent;旧 Agent 的最终结果仍可查看,历史未采集过程
|
||||
不会被补造。
|
||||
- 远端 ACP 普通工具失败后,只有最后一次工具失败之后收到非空白助手正文、Prompt 正常
|
||||
完成且没有 `pending` / `in_progress` 工具时,才在 `done` 前将仍失败的工具更新为
|
||||
`recoverable`,保留调用 ID、名称、输入、输出和错误。失败前的正文、推理和纯空白
|
||||
不作为恢复依据;后续再次失败会清除该依据。原生子代理失败不参与普通工具恢复,
|
||||
Prompt 失败、取消或 `outcome-unknown` 也不触发转换。Agent-owned 与直接 ACP 路径
|
||||
共用此规则;转换表示后续响应已处理失败,不表示工具重新执行成功。
|
||||
- 本机 OpenCode SDK 路径允许不同会话并行,同一会话仍按请求顺序执行。每个请求独立拥有
|
||||
SSE 事件订阅,并在正常完成、错误、取消或消费方结束迭代时主动关闭自己的响应流,
|
||||
不关闭其他会话的订阅或共享 Server。聊天与原生 Compact 共用 `consumeEventSubscription`,
|
||||
@@ -349,6 +412,10 @@ OC `todowrite` / CN `Checklist` 顶部展示属于待实施功能,权威需求
|
||||
的断线继续执行语义。
|
||||
- 同一 detached Agent 存活时,短暂 SSH 断线依次执行 `controller/resume`、`runtime/resumeAcpChannel` 和 `runtime/replayAcpChannel`,从 Main 已确认的 cursor 后只重放 Agent 到 Main 的已记录输出。重复 frame 会被确认但不会再次交给上层。若上一代连接只留下没有活动请求的 detached binding,完成精确 controller takeover 后会先有界停止并核对遗留 Runtime process,再用新 channel epoch 重新打开同一 binding 并恢复已有 ACP session;其他 controller、未证明 takeover 或仍有活动请求的 binding 仍被拒绝。
|
||||
- Main 到 Runtime 的 ACP 输入、模型请求、工具请求和 blob 不自动重放。
|
||||
- Continue helper 的会话与上下文仅保存在进程内。Desktop 冷打开已完成的 CN 对话时,
|
||||
关闭旧空闲 binding,以新 binding 和已有对话历史创建会话;同一存活会话内续发仍递增
|
||||
Prompt 序列。活动请求恢复沿用原 operation 和序列;start 未确认时通过既有 attach
|
||||
核对,不递增序列或重新发送 Prompt。
|
||||
- 无法确认外部 Provider 是否已处理的模型调用保持结果未知,避免重复计费或重复副作用。
|
||||
- 只有远端返回 identity 匹配且 `closed: true` 时,Main 才删除持久化 binding;传输失败或未确认 close 会保留 recovery identity。
|
||||
- 终态 close 由 Agent 在一个事务中为两个方向记录 sequence high-water tombstone,删除剩余 frame 和 active channel,并归零对应 journal quota。tombstone 用于拒绝迟到的旧 epoch frame,因此当前不会按时间自动裁剪。
|
||||
@@ -376,6 +443,14 @@ OC `todowrite` / CN `Checklist` 顶部展示属于待实施功能,权威需求
|
||||
时提交 Task 终态;事务成功后生成器才继续并向 Agent ACK。断电发生在同一 sequence
|
||||
中间时会重读该 sequence,已写事件按 `(binding, operation, sequence, eventIndex)` 去重,
|
||||
未写事件继续归并,不会重复 Provider 或工具执行。
|
||||
- 工具失败恢复所需的正文依据从 Desktop 已提交的 `task_events` 推导:按
|
||||
`remote_semantic_sequence` 数值及 `remote_event_index` 顺序判断最后一次失败后是否有
|
||||
非空白 `text`,重连时与已有工具状态一起交给 Runtime,保留断线前的有效依据。
|
||||
不从会原地更新或被截断的展示块推断顺序,不新增表、字段或持久快照。终态转换按
|
||||
`callId` 排序,并重发已为 `recoverable` 的工具,使部分终态落库后的重读仍使用稳定的
|
||||
`eventIndex`,沿用既有 provenance 去重和 checkpoint 提交。
|
||||
重连时,工具及原生子代理的状态也以已提交的远端活动事件为准,覆盖消息终态处理派生的
|
||||
失败状态,避免将未完成活动误判为可恢复错误,保证终态重放内容一致。
|
||||
- 远程 context metrics 与对话级压缩后的估算值按本次请求实际使用的 Runtime 选择写入
|
||||
`context_state_json`。实时接收与恢复入口都传入请求的选择;数据库在未传入时按对话、
|
||||
项目、auto 顺序解析。继承项目设置的对话保持 `runtime_selection_json = NULL`,
|
||||
@@ -616,6 +691,16 @@ bundle 构建通过;这组历史结果不替代完整 Host 支持改动后的
|
||||
|
||||
### Linux 历史验收
|
||||
|
||||
- 2026-09-16:当前源码通过本机 Linux x64/arm64 两份 `0.11.18` 旧包副本的生产签名、
|
||||
manifest、包内 lock 和 payload 校验(OpenCode `1.18.9`),原缓存元数据未变。
|
||||
共享 Linux x64 Host 的 LAN/VPN 两条路由均通过固定主机密钥验证;在 `/root/tmp`
|
||||
独立目录运行当前源码校验与 `preparePackage` / `commitPackage`,随后旧包 Agent
|
||||
bootstrap、health、stop 全部通过,未操作原有 Agent,模型请求为 0 次。
|
||||
相关扩大回归为 326 通过、1 跳过;全量 `npm test` 为 4146 通过、67 跳过、4 失败,
|
||||
另有 App 测试文件因缺少 `LocalInferencePage` 导入失败。大离线包超时用例复跑通过;
|
||||
其余失败为 heartbeat fixture 重复 `pinned` 列及导航设置默认字段导致的断言差异。
|
||||
Node/Agent 类型检查和本次改动文件 lint 通过;全量 typecheck 受上述缺失组件阻塞,
|
||||
全量 lint 受 story-graph demo 的 22 项 `document` 未定义错误阻塞。
|
||||
- 2026-09-06 使用当前源码隔离 Agent 验证原生子代理过程插件。最终场景收到 37 条 ACP
|
||||
插件事件,桌面侧从已保留活动重建跟踪器后仍接收后续文本与工具;子代理和父请求正常
|
||||
完成,最终模型调用账本为 4 次 completed。此前成功场景另 4 次,两轮真实 Host 共
|
||||
@@ -779,6 +864,13 @@ goodbuddy-agent diagnostics --installation-id <installationId>
|
||||
校验,但所有 production 层统一使用一组 GoodBuddy 通用发布身份并通过签名域区分用途,
|
||||
不要求内部 Runtime 单独配置密钥。`build/agent-catalog.cjs` 为双架构包生成签名累计目录,
|
||||
拒绝同一版本/架构改变字节。
|
||||
- `remote-runtime-launch-contracts.ts` 中的 OpenCode Runtime lock 版本使用现有
|
||||
`componentVersionSchema` 校验,不再硬编码为 `1.18.29`,避免 Desktop 升级后仅因版本
|
||||
不同拒绝已有完整签名旧包。包内 manifest、lock 与 descriptor 的版本一致性,以及签名、
|
||||
哈希和协议兼容性检查仍须通过;构建与发行的 OpenCode pin 保持 `1.18.29`。
|
||||
- Agent lock 的 Node/koffi 版本同样使用 `componentVersionSchema`,允许完整旧包携带
|
||||
不同于 Desktop 构建 pin 的依赖版本。验证时仍以包自身 lock 精确核对 manifest 的
|
||||
Node.js、koffi 和 koffi native 版本;Node `24.19.0`、koffi `3.1.4` 发行 pin 不变。
|
||||
- `.github/workflows/agents.yml` 只使用进程内临时测试 key 做分支/PR 原生验证,不发布。
|
||||
`.github/workflows/agent-release.yml` 才可在 annotated `agent-v<version>` 标签和受保护
|
||||
`agent-signing` Environment 中构建 production 包。Agent GitHub Release 必须
|
||||
@@ -792,6 +884,93 @@ goodbuddy-agent diagnostics --installation-id <installationId>
|
||||
信任条件。上传到 Host 前重新序列化为确定性 LF JSON。签名目录、描述符、manifest 和
|
||||
payload 仍按原始签名字节及 SHA-256 验证。
|
||||
|
||||
## 2026-09-20 冗余代码清理验证
|
||||
|
||||
- 删除没有生产调用的结构化命令 runner、两个进程身份辅助函数、占位 peer provider、
|
||||
平台查询辅助函数和五个旧 Runtime 请求构造/摘要辅助函数。保留实际 CLI、进程检查器、
|
||||
请求 schema、原生 peer 校验及包签名验证,不改变 Ask/Execute 或协议行为。
|
||||
- 清理后的当前源码 Agent、Main attach transport 和 protocol client 在共享 Linux x64
|
||||
Host 上通过固定 Host Key 连接,完成启动、状态/能力读取、只读工作区打开及真实文件读取,
|
||||
断开后再次连接并重复读取。验证没有使用模拟 Agent 或旧安装包的 Agent 实现。
|
||||
- 本轮测试进程剩余 0;专属 Host 目录、上传文件及本地隔离 profile 已删除。
|
||||
文本和图片模型请求均为 **0 次**。这是未使用代码删除后的启动/连接验证,不是新增的
|
||||
Runtime 模型调用、安装更新或其他架构验收。
|
||||
- 临时证据为 `redundancy-cleanup-host-{build,run}.cjs` 与
|
||||
`redundancy-cleanup-host-live.log`;定向回归 230 项通过,typecheck、lint 通过。
|
||||
- 同期全量 `npm test` 为 4,844 通过、67 跳过、11 失败;失败来自并行修改中的五个
|
||||
Renderer 帮助提示/焦点测试套件。更新后的五个套件连同更新设置大小展示回归共 64 项
|
||||
定向复跑全部通过;未把这次复跑记为又一轮全量通过。
|
||||
|
||||
## 生命周期验证(2026-09-23)
|
||||
|
||||
通过已有 `lifecycle-host` harness 重建当前源码的 Agent、daemon 和桌面驱动,使用已保存
|
||||
凭据与固定 Host Key 经 LAN 连接共享 Linux x64 Host。运行文件均位于独立
|
||||
`/root/tmp/gb-lifecycle-*` 目录,Runtime 使用 Host 已安装的 OpenCode。两次 Ask/Execute
|
||||
运行合计 8 次真实 Provider 请求,另一次空闲更新验证没有模型请求。
|
||||
|
||||
- Ask 完成后测试所属子进程为 0;下一轮重建 Runtime、加载同一原生 session,并准确回忆
|
||||
上轮标记。第二轮完成后子进程再次为 0。
|
||||
- Execute 启动等待 8 秒后写文件的工具任务,Desktop 断开,再把新安装提升为 current。
|
||||
旧 daemon 进入 draining,活动进程仍在;重连并恢复原 operation 后正常收尾,旧 daemon
|
||||
退出,测试所属子进程为 0。
|
||||
- 空闲更新中,旧 daemon 在提升后 3 秒断言窗口内退出,新 current daemon 仍为 ready。
|
||||
各次运行清理时所属子进程均为 0,未修改共享安装 registry。
|
||||
|
||||
8 个定向套件共 213 项通过,覆盖 CLI 退役安装 stop/retire、daemon 常驻与排空、共享及
|
||||
独占 Runtime 回收、待答问题仍属活动任务、断线恢复、原生历史加载和 Desktop 冷启动。
|
||||
远程 UI/IPC 定向复跑 28 项通过,SettingsPanel 完整套件通过;IPC/App 扩展运行共
|
||||
517 项通过、4 项失败,失败位于并行修改中的
|
||||
心跳、设置读取次数和监督者导航断言。全量 `npm test` 运行到 600 秒工具时限仍无最终汇总,
|
||||
期间报告数据库迁移、监督者和导航测试失败,不能记为全量通过。`npm run lint` 通过。
|
||||
类型检查早期通过,最终复跑被并行新增的 `tests/incremental-review-live.electron.test.ts:23`
|
||||
中环境变量对象类型错误阻塞。
|
||||
|
||||
本次实机覆盖当前源码的 daemon、registry 提升、托管 ACP、真实 Runtime 与模型请求。
|
||||
未重复完整签名包安装和 UI 更新点击,也未在实机重跑 Continue 或问题答复;这些场景
|
||||
不能以本次 OpenCode 结果替代。历史发布说明中的技术预览记录保留,当前远程项目设置与
|
||||
说明文档已去除该标签;Linux 桌面控制等其他预览功能不受影响。
|
||||
|
||||
## 升级残留清理验证(2026-09-24)
|
||||
|
||||
本轮在产品源码改动完成后,重新从工作区构建 cleanup CLI 和隔离验证脚本,并在共享
|
||||
Linux x64 Host 重跑。使用既有 GoodBuddy 加密凭据存储和固定 Host Key;没有输出凭据。
|
||||
本机临时 harness 位于 `C:/Users/jiang/AppData/Local/Temp/opencode/`,执行命令为:
|
||||
|
||||
```powershell
|
||||
node "C:\Users\jiang\AppData\Local\Temp\opencode\upgrade-cleanup-build.cjs"
|
||||
node "C:\Users\jiang\AppData\Local\Temp\opencode\upgrade-cleanup-run.cjs"
|
||||
```
|
||||
|
||||
`upgrade-cleanup-build.cjs` 打包 `upgrade-cleanup-cli.ts`、`upgrade-cleanup-linux.ts`
|
||||
和 `upgrade-cleanup-host.ts`;run 脚本通过 Electron 启动 host 驱动。Host 驱动读取共享安装
|
||||
registry 以定位 Node 和 koffi 依赖,仅在 `/root/tmp/gb-upgrade-upload-*` 上传测试脚本。
|
||||
远端执行形式如下,其中 `<current-id>` 来自共享 registry,`<upload-root>` 是本轮唯一目录:
|
||||
|
||||
```sh
|
||||
NODE_PATH='/root/.goodbuddy/agent/installations/<current-id>/lib/node_modules' TMPDIR='<upload-root>' '<installed-node>' '<upload-root>/linux.cjs' '<upload-root>/cli.cjs'
|
||||
```
|
||||
|
||||
场景在独立 `/root/tmp/gb-upgrade-cleanup-*` HOME 中生成临时签名测试安装和既有格式的
|
||||
生命周期、语义、journal、owner 记录,通过该 HOME 的 current 安装执行真实 CLI:
|
||||
`goodbuddy-agent cleanup-obsolete --installation-id current`。测试用实际 Node 进程表示
|
||||
不含退役逻辑的旧 daemon/helper,不是部署历史发布包或运行真实模型任务。
|
||||
|
||||
本次构建与远端运行退出码均为 0。远端断言确认:孤儿 Runtime 被回收,所属旧安装 payload
|
||||
删除;存活 legacy daemon 延期;另一个带 `starting` 操作记录的进程存活;历史数据库字节
|
||||
不变;current/candidate payload 保留;第二次调用没有重复删除。运行输出为:
|
||||
|
||||
```json
|
||||
{"passed":true,"productionCli":true,"realOrphanReclaimed":true,"liveLegacyDeferred":true,"activeWorkPreserved":true,"historyPreserved":true,"idempotent":true,"providerCalls":0}
|
||||
```
|
||||
|
||||
测试结束清除自身进程和隔离目录,没有清理共享用户安装或修改其 registry。早先隔离实测
|
||||
发生在最终补充两个本地回归用例之前;本节记录的是之后重新构建源码的复跑。此前最终本地
|
||||
9 个定向套件 99 项通过,typecheck 和改动文件 ESLint 通过。
|
||||
|
||||
本次三分钟时限内未找到可直接运行 backend/bridge Vitest 套件的 Linux harness,未安装
|
||||
额外测试依赖,故这两类 Linux 套件未运行。上述 cleanup 场景不替代这些套件,不证明完整
|
||||
UI 更新或签名复合包安装流程通过;真实模型调用为 0 次。
|
||||
|
||||
## 发布前完整回归(不能替代开发验证)
|
||||
|
||||
以下清单只用于复核开发期间已经在真实 Host 上通过的链路,不得作为 Agent 改动的首次
|
||||
@@ -810,3 +989,72 @@ goodbuddy-agent diagnostics --installation-id <installationId>
|
||||
9. 运行一次有界的真实模型调用,确认凭据不进入 Renderer、SSH 参数、远端环境或磁盘,
|
||||
只在当前 accepted operation 生命周期内进入 Agent 内存。
|
||||
10. 中断并恢复 SSH,确认活动 Runtime 不被网络抖动终止。
|
||||
|
||||
## Prompt 完成与空闲回收验证(2026-09-24)
|
||||
|
||||
Runtime 可以先收到完整模型正文并返回 ACP `end_turn`,随后 helper 才完成 delivery ACK。
|
||||
`RuntimeAcpBackend` 的正常完成路径现在要求 broker 等待已接收的 exchange 完成,最多等待
|
||||
2 秒,再关闭 socket;取消等强制关闭路径不等待这一步。ACK 超时或交付失败仍记录
|
||||
`outcome-unknown`,语义 transcript 与 reconciliation 保持一致。
|
||||
|
||||
正常完成时,Agent 在串行 control 操作内先持久化语义终态,再将 binding 标为空闲并回收
|
||||
Runtime。回收失败记录 `daemon.stop.failed`,reason 为 `idle-runtime-cleanup`,不把已提交的
|
||||
`completed` 改成未知结果。确定性回归覆盖共享与独占 Runtime 的 stop/reconcile 失败,以及
|
||||
等待 ACK 期间不得提交终态或停止 Runtime。
|
||||
|
||||
Desktop 的下一次显式发送会淘汰缓存中的不可用 binding,再通过 cold-open 创建新请求并
|
||||
传入已有对话历史。OpenCode 和 Continue 的未知终态均有回归;不会重放原来的未知请求。
|
||||
`remoteRecoveryOnly` 只允许附加同一活动 operation,已完成或结果未知的缓存不能借恢复
|
||||
入口发起新请求。缓存仍有活动 operation 时,不同 request ID 的发送被拒绝;普通失败后
|
||||
已回到 `ready` 的会话仍可继续使用。
|
||||
|
||||
### 本机模拟范围
|
||||
|
||||
`agent-owned-acp-prompt.local.test.ts` 使用本机已安装的 OpenCode 1.18.29、真实 ACP 和
|
||||
loopback fake model。测试暂缓 delivery ACK,确认正文 `ACK_OK` 和 ACP 完成先到达,释放
|
||||
ACK 后提交 `completed`,随后注入清理异常并确认终态不变。该用例注入了
|
||||
`completePrompt` 回调,没有经过完整 `RuntimeAcpBackend` 完成路径;backend 的提交顺序、
|
||||
清理诊断和 reconciliation 由 `runtime-acp-backend.test.ts` 的独立回归检查。
|
||||
|
||||
本轮本机定向复跑 7 个套件,199 项通过、10 项 Unix-only 用例在 Windows 跳过,耗时
|
||||
5.60 秒。套件为 `runtime-acp-backend`、`agent-owned-acp-prompt`、
|
||||
`agent-owned-acp-prompt.local`、`model-bridge`、`model-bridge-client`、
|
||||
`runtime-composition` 和 `acp-remote-runtime`。本轮没有运行完整测试套件。
|
||||
|
||||
### 最终源码 Linux 场景
|
||||
|
||||
源码修正完成后,重新构建临时 `lifecycle-host-agent`、`lifecycle-host-daemon` 和
|
||||
`lifecycle-host-driver`,通过既有 GoodBuddy 加密凭据存储与固定 Host Key 连接共享
|
||||
Linux x64 Host。执行命令为:
|
||||
|
||||
```powershell
|
||||
node "C:\Users\jiang\AppData\Local\Temp\opencode\lifecycle-host-build.cjs" agent daemon driver
|
||||
node "C:\Users\jiang\AppData\Local\Temp\opencode\lifecycle-host-run.cjs" driver --fake-model --completion-only
|
||||
```
|
||||
|
||||
测试在独立 `/root/tmp/gb-lifecycle-*` HOME 启动当前源码的 Agent daemon,通过 Desktop
|
||||
`createManagedRemoteAcpRuntime`、SSH attach、Agent protocol、
|
||||
`createProductionRuntimeProtocol` 和 `RuntimeAcpBackend` 运行 Host 上已安装的 OpenCode。
|
||||
该路径使用真实 Runtime process owner、model bridge、ACP 和语义存储,没有注入 backend
|
||||
的 completion 回调。测试只替换 daemon 的外部模型 HTTP 响应,返回确定的 SSE 文本。
|
||||
|
||||
两轮 Ask 均收到完成事件,每轮结束后测试目录所属的 Runtime/helper 进程数为 0。第二轮
|
||||
在回收后重新启动 Runtime,保留同一原生 session ID,模型请求中带有第一轮保存的标记。
|
||||
运行输出的关键记录为:
|
||||
|
||||
```json
|
||||
{"inspected":true,"oldState":"ready","draining":false,"remainingOwnedProcesses":0,"realProviderCalls":0,"fakeModelCalls":1}
|
||||
{"inspected":true,"oldState":"ready","draining":false,"remainingOwnedProcesses":0,"realProviderCalls":0,"fakeModelCalls":2}
|
||||
{"currentSourceAsk":true,"runtimeRecreated":true,"nativeHistoryPreserved":true}
|
||||
{"stopped":true,"realProviderCalls":0,"fakeModelCalls":2,"remainingOwnedProcesses":0}
|
||||
{"exit":0,"stderrBytes":0}
|
||||
```
|
||||
|
||||
构建和运行退出码均为 0,fake model exchange 共 2 次,真实 Provider 请求为 0 次。清理
|
||||
只停止测试 daemon、其 Runtime 进程并删除隔离目录;没有更改共享用户安装或 registry,
|
||||
没有停止无关 Host 进程。
|
||||
|
||||
此次 Linux 场景验证正常完成、空闲回收和下一轮恢复原生历史;没有在 Host 上注入 ACK
|
||||
延迟或清理失败,也没有覆盖未知终态后的新 binding 场景、Continue、Execute、断线恢复、
|
||||
完整 UI 操作或签名包安装。故障时序与未知终态的下一次发送以本机定向回归为证据,不能用
|
||||
这两轮成功请求替代。
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
# Smart Heartbeat
|
||||
|
||||
Smart Heartbeat 负责按计划或条件主动检查范围内信息,并生成可监督的建议或任务。
|
||||
Smart Heartbeat 已提供按计划或手动执行的有界回顾,生成报告及后续建议。应用默认关闭;启用及计划条件见[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)。条件或事件唤醒仍需后续设计。
|
||||
|
||||
现有入口已升级为“监督者”,智能心跳作为其定期唤醒机制,驱动检查与整理;应用提供工作回顾、故事线图谱、自动监督、活动记录和设置五个页签。计划在自动监督管理,创建与编辑使用 Modal;设置只保留算法参数。定义见[监督者应用产品设计](../conversation-supervision/supervisor-prd.md),实施方案见[监督者技术设计](../conversation-supervision/technical-design.md)。现有计划和范围保留,监督调用继续禁止工具执行。
|
||||
|
||||
自动报告及监督回顾分别按来源版本和已读位置处理增量;没有待处理输入时记录无变化,跳过模型及新报告。手动历史重看和删除报告的边界见[自动增量规则](../conversation-supervision/logic-design.md#自动增量与手动重看)。
|
||||
|
||||
## 文档导航
|
||||
|
||||
当前监督者设置已接入报告与监督整理两项模型阶段超时(30 至 600 秒,默认 240)及共享监督并发数(1 至 4,默认 1),模型超时不包含排队、采集、保存或整次执行期限。报告先获槽再领取,实际请求开始前校验并刷新租约,报告提交或无变化后释放槽位再进入监督。普通聊天不入监督池,尚无跨聊天与监督的提供商全局并发或 RPM/TPM 限制。租约、固定传输时限及重试与用户超时分开,见[控制清单](../conversation-supervision/review-scheduling-design.md#当前超时与调度控制清单已实现)。完整分页与持久化续跑仍为设计,不能把现有增量检查称为完整调度器。
|
||||
|
||||
监督者“活动记录”页签同时显示手动回顾和心跳执行,将新心跳报告与下游监督按运行 ID 合并,保留各阶段真实状态。心跳审计、运行概览、指标、趋势、自动监督报告阅读和建议处理与计划列表同在自动监督页;工作回顾只读取统一监督结果,设置只保留配置。当前布局见[监督者 UI 设计](../conversation-supervision/ui-design.md#应用入口)。心跳审计继续读取报告自身状态,完整执行状态以[活动规则](../conversation-supervision/logic-design.md#活动记录)为准。
|
||||
|
||||
| 文档 | 权威职责 |
|
||||
| --- | --- |
|
||||
| [智能心跳 PRD](./prd.md) | 用户目标、范围、计划、运行和产品验收 |
|
||||
|
||||
@@ -21,11 +21,13 @@
|
||||
|
||||
### 1.1 智能心跳是独立能力
|
||||
|
||||
- “智能心跳”菜单入口是心跳配置、报告、建议和运行历史的权威位置。
|
||||
- “监督者 > 自动监督”是计划管理、自动监督报告阅读和建议处理的权威位置,创建与编辑使用 Modal;工作回顾只阅读统一监督结果,运行活动位于活动记录,设置只管理算法参数。内部继续沿用 heartbeat 标识。
|
||||
- 任务中心和设置中心不复制完整心跳配置表单。
|
||||
- 智能心跳不并入通用自动化中心,也不作为任务中心条目。
|
||||
- 现有心跳报告、记忆建议、行动建议和审计历史在本轮继续保留。
|
||||
|
||||
心跳与监督者共用应用开关;默认关闭、计划自动运行条件、手动请求及在途运行边界统一见[应用启停与执行](../conversation-supervision/logic-design.md#应用启停与执行)。单条计划的启停与应用启用分别保存,普通任务定时调度不受该应用开关影响。
|
||||
|
||||
### 1.2 长期方向:分区记忆协同
|
||||
|
||||
已确认的方向只有:
|
||||
@@ -48,7 +50,7 @@
|
||||
|
||||
### 2.1 目标
|
||||
|
||||
1. 完整心跳配置统一到“智能心跳”菜单入口。
|
||||
1. 完整自动监督配置统一到“监督者 > 自动监督”,创建与编辑复用同一 Modal 表单。
|
||||
2. 心跳配置支持 `Global` 或指定一个、多个 Project。
|
||||
3. 保留现有概览、待处理建议、运行历史和报告能力。
|
||||
4. 新入口完整可用后,再移除任务中心和设置中心中的重复心跳配置。
|
||||
@@ -95,7 +97,7 @@
|
||||
|
||||
### 3.2 多项目执行
|
||||
|
||||
多项目配置的一次心跳仍然是一次运行和一份报告:
|
||||
多项目配置的一次心跳仍然是一次运行;有待处理输入时生成一份报告,无变化时只保存运行状态:
|
||||
|
||||
- 将选中项目中允许读取的会话、任务和记忆汇总后,再执行现有全局输入上限。
|
||||
- 不为每个 Project 分别创建 Run、报告、建议或 Task 副本。
|
||||
@@ -112,15 +114,21 @@
|
||||
- 迁移不修改启停状态、重复规则、时间、回顾窗口、保留期限、下次运行或历史。
|
||||
- 项目删除时,只有该项目的配置按现有规则删除;多项目配置移除被删除项目并保留其他范围。
|
||||
|
||||
### 3.4 自动增量
|
||||
|
||||
定时报告与下游监督分别跳过已成功处理且未修改的来源,均无新输入时不调用模型,活动显示无变化。首次运行受计划范围和回顾窗口限制;预算遗漏、修订和失败重试规则由[监督者逻辑设计](../conversation-supervision/logic-design.md#自动增量与手动重看)统一定义。已确认记忆只提供背景。
|
||||
|
||||
“立即运行”保留显式重新生成心跳报告的行为,下游监督仍使用自动进度。工作回顾中的手动操作重新整理所选历史区间。删除计划、报告或到期清理历史不重置来源进度,也不重建图谱。
|
||||
|
||||
## 4. 权威入口
|
||||
|
||||
| 操作 | 权威入口 | 其他位置 |
|
||||
| --- | --- | --- |
|
||||
| 创建或编辑心跳配置 | 智能心跳 > 心跳计划 | 不复制表单 |
|
||||
| 选择 Global / Project 范围 | 智能心跳 > 心跳计划 | 不依赖当前项目推断 |
|
||||
| 暂停、恢复、立即心跳或删除 | 智能心跳 > 心跳计划 | 不复制操作 |
|
||||
| 查看报告与待处理建议 | 智能心跳 | 主导航徽标和通知可触达 |
|
||||
| 查看运行历史和失败 | 智能心跳 > 报告与记录 | 活动记录仅作审计补充 |
|
||||
| 创建或编辑自动监督配置 | 监督者 > 自动监督 > 创建或编辑 Modal | 不复制表单 |
|
||||
| 选择 Global / Project 范围 | 监督者 > 自动监督 > 创建或编辑 Modal | 不依赖当前项目推断 |
|
||||
| 暂停、恢复、立即运行或删除 | 监督者 > 自动监督 | 不复制操作 |
|
||||
| 查看自动监督报告与待处理建议 | 监督者 > 自动监督 | 保留既有结果及处理操作 |
|
||||
| 查看运行历史和失败 | 监督者 > 活动记录;自动监督 | 活动按同一次执行展示心跳报告和下游监督状态;自动监督页保留报告本身的运行审计 |
|
||||
| 管理平台级默认值 | 设置中心 | 不显示单条计划 CRUD |
|
||||
|
||||
移除重复入口的顺序:
|
||||
@@ -133,12 +141,7 @@
|
||||
|
||||
## 5. 智能心跳页面
|
||||
|
||||
保留现有四个页面:
|
||||
|
||||
- **运行概览**:状态、成功率、记忆、洞察和行动统计。
|
||||
- **待处理建议**:记忆建议和行动建议。
|
||||
- **报告与记录**:心跳报告与 Run 审计。
|
||||
- **心跳计划**:唯一完整配置入口。
|
||||
五页签归属以[监督者 UI 设计](../conversation-supervision/ui-design.md#应用入口)为准:自动监督管理计划并显示运行概览、统计、趋势、自动监督报告、建议和 Run 审计,设置只展示算法参数,工作回顾阅读统一监督结果及历史,活动记录只展示统一执行、进度和详情,并支持按计划 ID 筛选。原有 CRUD、建议处理、报告展开及分页能力保留,不在设置内部再建页签。
|
||||
|
||||
### 5.1 心跳计划
|
||||
|
||||
@@ -146,7 +149,7 @@
|
||||
|
||||
- 名称。
|
||||
- `Global` 或指定 Project 范围。
|
||||
- 每日或每周重复规则。
|
||||
- 每日或每周重复规则;当前不支持分钟间隔。
|
||||
- 本地时间、星期与 IANA 时区。
|
||||
- 回顾窗口。
|
||||
- 保留期限。
|
||||
@@ -172,7 +175,7 @@
|
||||
|
||||
### 5.3 响应式与无障碍
|
||||
|
||||
- 宽窗口使用 `dashboard` 壳层,创建和编辑区域保持单列。
|
||||
- 复用监督者的 `supervisor` 壳层,创建和编辑区域保持单列。
|
||||
- 窄窗口下计划列表和表单转为单列卡片。
|
||||
- `Global / 指定项目` 使用共享分段选择控件,Project 多选使用 Checkbox。
|
||||
- 范围、状态和失败不能只依赖颜色。
|
||||
@@ -193,7 +196,7 @@ type HeartbeatScope =
|
||||
- Main 在创建、编辑、列出和执行时验证 Project 归属。
|
||||
- Global 读取所有 Project 的有界会话和任务,但长期记忆仍只读取 Global。
|
||||
- 指定 Project 读取选中项目的有界会话和任务,以及 Global 与选中项目记忆。
|
||||
- 现有输入条数、字符预算、输出大小、超时、重试、租约和工具禁用边界继续生效。
|
||||
- 现有输入条数、字符预算、输出大小、超时、重试、租约和工具禁用边界继续生效。报告与监督整理共用监督专用并发池,普通聊天不入池;具体设置、租约和未实现的限流边界见[当前控制清单](../conversation-supervision/review-scheduling-design.md#当前超时与调度控制清单已实现)。
|
||||
- 多项目汇总后统一应用上限,不能按项目倍增预算。
|
||||
- 心跳结果默认不在系统通知中暴露私人正文。
|
||||
- 数据迁移必须使用 SQLite 事务,保留外键、级联删除和现有历史。
|
||||
@@ -207,7 +210,7 @@ type HeartbeatScope =
|
||||
- 扩展共享 Schema、Preload 与 Main 数据合同。
|
||||
- 增加 Global / 多 Project 持久化与旧数据迁移。
|
||||
- 让心跳执行按冻结范围构建有界输入。
|
||||
- 在“心跳计划”中完成创建、编辑和全部计划操作。
|
||||
- 在“监督者 > 自动监督”中完成全部计划操作;创建和编辑使用同一 Modal 表单。
|
||||
- 补齐范围、迁移、权限和界面测试。
|
||||
|
||||
### 已完成:移除重复配置
|
||||
@@ -228,7 +231,7 @@ type HeartbeatScope =
|
||||
|
||||
## 8. 验收状态
|
||||
|
||||
- [x] 智能心跳菜单是完整配置的唯一权威入口。
|
||||
- [x] 监督者的自动监督页签是完整计划配置的唯一权威入口。
|
||||
- [x] 用户可以创建和编辑 `Global` 心跳。
|
||||
- [x] 用户可以创建和编辑绑定一个或多个 Project 的心跳。
|
||||
- [x] 切换当前 Project 不会改变已保存范围。
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# 故事线图谱(Story Graph)
|
||||
|
||||
这是[监督者应用](../conversation-supervision/supervisor-prd.md)的核心视图。目标对象与交互由[产品设计](./prd.md)定义,用户验收见[User Stories](../conversation-supervision/user-stories.md)。时间事件、知识实体与阶段总结在目标设计中分别建模;下述演示仍使用阶段节点。
|
||||
|
||||
[打开交互演示](./demo.html)。这是监督者故事图谱的独立设计原型,使用模拟数据,尚未接入产品或笔记存储。
|
||||
|
||||
`demo.html`、`demo.css`、`demo.js` 共同负责可交互的界面草案:时间线选择、阶段回放、跨时间线关联、节点总结和来源展开。直接在浏览器中打开 HTML 即可使用,无需安装依赖。
|
||||
|
||||
当前设计假设:每个圆代表一条时间线,实心节点代表阶段总结,实线表示同一时间线的先后关系,虚线表示不同时间线间的知识关联。节点位置为示意布局,尚未定义集中度算法,不代表效率或产出。聚合知识以预置示例演示,不调用模型。
|
||||
|
||||
新增平铺视图(默认):时间轴沿画布外围逆时针环绕,从顶部 09.01 经左侧 09.07、底部到右侧 09.14,以箭头表示继续推进。知识节点平铺在环内,外圈时间点与对应阶段节点直接连接,选择节点时高亮其时间连线。工具栏可切回圆形视图,保留所选阶段、时间线与节点。当前仍使用阶段节点示例,同一知识实体在多个时间点发生修订的合并展示尚未实现。
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,27 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Story Graph · GoodBuddy 监督者</title>
|
||||
<link rel="stylesheet" href="demo.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="appbar"><div class="brand"><span class="brandmark">G</span> GoodBuddy <span class="separator">/</span> 监督者</div><div class="top-actions"><span class="badge">交互原型 · 模拟数据</span><button id="theme" aria-label="切换深浅主题">切换主题</button></div></header>
|
||||
<main>
|
||||
<header class="page-heading"><div><div class="eyebrow">STORY GRAPH</div><h1>故事图谱</h1><p>沿着工作的来路,看见知识之间的联系。</p></div><div class="scope">范围:GoodBuddy 项目<br><span>2026.09.01 — 09.14</span></div></header>
|
||||
<div class="workspace">
|
||||
<aside class="timelines"><div class="section-heading"><h2>时间线</h2><span>03</span></div><p class="muted">选择一条故事,查看它的演变。</p><div id="timeline-list"></div><div class="sidebar-note"><span class="eyebrow">如何读图</span><p>一个圆,一条时间线。<br>圆内节点是阶段总结,<br>圆间虚线是知识关联。</p><p class="muted">分布仅作示意。集中度不等于效率或产出。</p></div></aside>
|
||||
<section class="graph-section" aria-label="故事图谱画布"><div class="graph-toolbar"><div><strong id="graph-title">全部故事</strong><span id="node-count" class="muted"></span></div><label class="view-picker">视图 <select id="view" aria-label="图谱视图"><option value="flat">平铺 · 环绕时间轴</option><option value="circles">圆形 · 独立故事</option></select></label><label class="check"><input id="relations" type="checkbox" checked> 跨线关联</label></div>
|
||||
<div class="canvas"><svg id="graph" viewBox="0 0 860 620" role="group" aria-label="可交互故事图谱,使用 Tab 选择阶段节点"></svg><div class="canvas-caption"><span class="live-dot"></span> 阶段总结构成故事,关联让知识相遇</div></div>
|
||||
<div class="graph-legend"><span><i class="dot"></i>阶段总结</span><span><i class="line"></i>时间推进</span><span><i class="line dashed"></i>知识关联</span><button id="reset">显示全部时间线</button></div>
|
||||
<div class="playback"><div class="playback-heading"><strong>故事如何长出来</strong><output id="date-label" for="stage">09.14 · 知识汇聚</output></div><input id="stage" type="range" min="0" max="2" value="2" step="1" aria-label="回放阶段"><div class="range-labels"><span>09.01 · 起点</span><span>09.07 · 探索</span><span>09.14 · 汇聚</span></div></div>
|
||||
</section>
|
||||
<aside class="inspector" aria-label="阶段详情"><div class="section-heading"><h2>故事详情</h2><span class="badge">可追溯</span></div><div id="detail" aria-live="polite"></div></aside>
|
||||
</div>
|
||||
<section class="insights"><div><div class="eyebrow">ACROSS STORIES</div><h2>从不同故事中,找到同一个答案</h2><p>3 条时间线中,有一条共同的知识线索。</p></div><button id="insight" class="insight-card"><span class="insight-icon">↗</span><span><strong>知识需要保留来路</strong><small>故事图谱 × 个人知识库 × 记忆架构</small></span><span>查看聚合 →</span></button></section>
|
||||
<footer>设计讨论稿 · 所有总结、关联和来源均为模拟示例;未读取或修改真实会话。</footer>
|
||||
</main>
|
||||
<script src="demo.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,119 @@
|
||||
/* global document */
|
||||
const stories = [
|
||||
{ id: 'graph', title: '监督者 · 故事图谱', color: 'var(--accent)', cx: 270, cy: 230, r: 172, description: '从工作过程到可追溯的故事' },
|
||||
{ id: 'knowledge', title: '个人知识库', color: 'var(--success)', cx: 625, cy: 260, r: 146, description: '从学习记录到可复用的知识' },
|
||||
{ id: 'memory', title: '记忆架构', color: 'var(--warning)', cx: 451, cy: 463, r: 118, description: '从原始数据到分层理解' }
|
||||
];
|
||||
const nodes = [
|
||||
{ id: 'g0', story: 'graph', stage: 0, x: 160, y: 205, label: '观察工作过程', title: '让工作过程可以回看', copy: '监督者按时间记录工作的推进。我们希望在记录之上保留阶段性的理解,帮助用户重新进入一段未完成的工作。', source: '监督者功能讨论', quote: '过一周再回来,我需要知道之前为什么这样做,而不只是看到最后的结果。' },
|
||||
{ id: 'g1', story: 'graph', stage: 1, x: 261, y: 260, label: '阶段总结', title: '把连续记录整理成阶段', copy: '每个阶段保留讨论的问题、形成的判断和仍未解决的事项。阶段节点沿时间连接,组成一条能够继续展开的故事。', source: '阶段总结交互草案', quote: '以一个阶段为单位总结。保留关键决定,也保留没有走通的尝试和原始记录。' },
|
||||
{ id: 'g2', story: 'graph', stage: 2, x: 326, y: 215, label: '保留知识来路', title: '总结之后,仍能找到来路', copy: '故事图谱把阶段总结和原始记录连接起来。跨时间线聚合出的知识,也应保留每条贡献来源,方便核对结论、理解语境。', source: 'Story Graph 方案讨论', quote: '阶段总结与聚合知识应保留来源,支持追溯原始记录。', related: 'k2', reason: '共同要求:聚合知识仍可回到原始记录' },
|
||||
{ id: 'k0', story: 'knowledge', stage: 0, x: 706, y: 213, label: '学习记录', title: '记录学习中的问题', copy: '学习时产生的笔记与问答散落在不同会话中。先保留它们的上下文,再考虑如何归入个人知识库。', source: '学习知识图谱讨论', quote: '学习时的提问和笔记可以放在一起,之后能够继续补充。' },
|
||||
{ id: 'k1', story: 'knowledge', stage: 1, x: 635, y: 304, label: '知识关联', title: '把同一个问题的线索连起来', copy: '不同笔记可能围绕同一个问题。关联需要说明它们共有的主题,以及各自提供了什么补充。', source: '个人知识库设计记录', quote: '关联不只是两段内容相似,还要说明这条联系为什么有用。' },
|
||||
{ id: 'k2', story: 'knowledge', stage: 2, x: 562, y: 240, label: '来源追溯', title: '知识条目携带出处', copy: '知识库中的归纳附带原始笔记和讨论位置。用户可以从结论返回上下文,判断它是否适用于当前问题。', source: '知识条目来源设计', quote: '每条归纳都应该能够展开出处,看到原话和产生结论时的上下文。', related: 'g2', reason: '共同要求:聚合知识仍可回到原始记录' },
|
||||
{ id: 'm0', story: 'memory', stage: 0, x: 380, y: 475, label: '原始数据', title: '从真实记录开始', copy: '原始会话、笔记和执行结果构成记忆的基础。上层归纳需要能指向这些记录。', source: '记忆基础讨论', quote: '先有记录,再有总结;总结不能取代原始记录。' },
|
||||
{ id: 'm1', story: 'memory', stage: 1, x: 465, y: 504, label: 'DIKW 分层', title: '区分记录与理解的层次', copy: '尝试按数据、信息、知识、智慧组织记忆。图谱中的阶段总结与知识聚合,可能成为这些层次之间的连接。', source: 'DIKW 设计草案', quote: '数据、信息、知识、智慧应当分层呈现,让用户知道当前看到的是什么。' },
|
||||
{ id: 'm2', story: 'memory', stage: 2, x: 478, y: 438, label: '可解释的归纳', title: '让每层归纳都能被解释', copy: '从数据到知识的每一步,都应说明归纳依据。阶段总结可以连接过程记录,聚合知识可以连接多个阶段。', source: '记忆分层讨论', quote: '一条知识来自哪些信息、信息又来自哪些数据,需要能够顺着关系找回去。', related: 'g2', reason: '共同要求:上层归纳保留底层依据' }
|
||||
];
|
||||
const dates = ['09.01 · 故事起点', '09.07 · 持续探索', '09.14 · 知识汇聚'];
|
||||
let activeStory = null;
|
||||
let selected = 'g2';
|
||||
let stage = 2;
|
||||
const graph = document.querySelector('#graph');
|
||||
const detail = document.querySelector('#detail');
|
||||
|
||||
function showDetail() {
|
||||
const node = nodes.find(item => item.id === selected);
|
||||
const story = stories.find(item => item.id === node.story);
|
||||
const related = node.related && nodes.find(item => item.id === node.related);
|
||||
detail.innerHTML = `<div class="detail-intro"><div class="detail-kicker">${story.title} / 阶段 ${node.stage + 1}</div><h2 class="detail-title">${node.title}</h2><p class="detail-copy">${node.copy}</p></div>
|
||||
<div class="detail-block"><h3>跨时间线关联</h3>${related && related.stage <= stage ? `<button class="relation-link" data-target="${related.id}">${related.title} ↗<small>${node.reason}</small></button>` : '<p class="muted">此阶段尚未建立跨线关联。</p>'}</div>
|
||||
<div class="detail-block"><h3>来源记录 · 1 条</h3><details class="source"><summary>${node.source}</summary><small>模拟会话 / 2026.${dates[node.stage].slice(0, 5)} / 消息 #${node.stage + 1}</small><p>“${node.quote}”</p></details><small>演示来源,可展开查看模拟原文。</small></div>`;
|
||||
}
|
||||
|
||||
function render() {
|
||||
document.querySelector('#timeline-list').innerHTML = stories.map(story => `<button class="timeline" data-story="${story.id}" aria-pressed="${activeStory === story.id}" style="--story-color:${story.color}"><span class="timeline-top"><i class="timeline-color"></i><span>0${stage + 1} 个阶段</span></span><strong>${story.title}</strong><small>${story.description}</small></button>`).join('');
|
||||
document.querySelector('#graph-title').textContent = activeStory ? stories.find(item => item.id === activeStory).title : '全部故事';
|
||||
document.querySelector('#node-count').textContent = ` / ${(stage + 1) * (activeStory ? 1 : 3)} 个阶段`;
|
||||
document.querySelector('#date-label').textContent = dates[stage];
|
||||
const showRelations = document.querySelector('#relations').checked;
|
||||
const flat = document.querySelector('#view').value === 'flat';
|
||||
document.querySelector('.sidebar-note p').innerHTML = flat ? '时间沿外圈逆时针推进。<br>内部平铺知识节点,<br>时间点直接连接对应节点。' : '一个圆,一条时间线。<br>圆内节点是阶段总结,<br>圆间虚线是知识关联。';
|
||||
document.querySelector('.canvas-caption').innerHTML = `<span class="live-dot"></span> ${flat ? '外圈时间逆时针推进 · 点击节点高亮时间连线' : '阶段总结构成故事,关联让知识相遇'}`;
|
||||
const plotted = nodes.map(node => flat ? { ...node, x: [280, 430, 580][stories.findIndex(story => story.id === node.story)], y: [210, 330, 450][node.stage] } : node);
|
||||
let markup = stories.map(story => `<g style="--story-color:${story.color}" class="${activeStory && activeStory !== story.id ? 'dim' : ''}"><circle class="orbit" cx="${story.cx}" cy="${story.cy}" r="${story.r}"/><circle class="orbit-inner" cx="${story.cx}" cy="${story.cy}" r="${story.r * .62}"/><text class="orbit-label" x="${story.cx}" y="${story.cy - story.r + 36}" text-anchor="middle">${story.title}</text><text class="orbit-caption" x="${story.cx}" y="${story.cy - story.r + 55}" text-anchor="middle">${stage + 1} 个阶段 · ${story.id === 'graph' ? '持续收敛' : story.id === 'knowledge' ? '多向探索' : '逐步归纳'}</text><path class="story-path" d="${nodes.filter(node => node.story === story.id && node.stage <= stage).map((node, index) => `${index ? 'L' : 'M'}${node.x},${node.y}`).join(' ')}"/></g>`).join('');
|
||||
if (showRelations && stage === 2) markup += '<path class="cross-path" d="M326 215 Q439 153 562 240 M326 215 Q366 362 478 438 M562 240 Q565 365 478 438"/><text class="bridge-label" x="413" y="188">来源追溯</text><text class="bridge-label" x="372" y="367">归纳依据</text>';
|
||||
if (flat) {
|
||||
const anchors = [{ x: 430, y: 65 }, { x: 95, y: 310 }, { x: 765, y: 310 }];
|
||||
markup = `<defs><marker id="time-arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0 L10 5 L0 10 Z" fill="var(--accent)"/></marker></defs>
|
||||
<ellipse class="time-boundary" cx="430" cy="310" rx="335" ry="245"/>
|
||||
<path class="time-ring" marker-end="url(#time-arrow)" d="M430 65 A335 245 0 0 0 95 310 A335 245 0 0 0 430 555 A335 245 0 0 0 765 310 A335 245 0 0 0 560 84"/>
|
||||
<text class="orbit-caption" x="430" y="30" text-anchor="middle">起点 · 逆时针沿外圈阅读 ↶</text>`;
|
||||
markup += plotted.filter(node => node.stage <= stage).map(node => {
|
||||
const anchor = anchors[node.stage];
|
||||
return `<path class="time-link ${node.id === selected ? 'active' : ''} ${activeStory && activeStory !== node.story ? 'dim' : ''}" d="M${anchor.x} ${anchor.y} Q${anchor.x} ${node.y} ${node.x} ${node.y}"/>`;
|
||||
}).join('');
|
||||
markup += stories.map(story => `<g style="--story-color:${story.color}" class="${activeStory && activeStory !== story.id ? 'dim' : ''}"><path class="story-path" d="${plotted.filter(node => node.story === story.id && node.stage <= stage).map((node, index) => `${index ? 'L' : 'M'}${node.x},${node.y}`).join(' ')}"/><text class="orbit-caption" text-anchor="middle" x="${plotted.find(node => node.story === story.id).x}" y="165">${story.title}</text></g>`).join('');
|
||||
if (showRelations && stage === 2) markup += '<path class="cross-path" d="M280 450 Q355 407 430 450 M280 450 Q430 515 580 450 M430 450 Q505 407 580 450"/>';
|
||||
markup += anchors.map((anchor, index) => `<g class="time-point ${index > stage ? 'dim' : ''}"><circle cx="${anchor.x}" cy="${anchor.y}" r="7"/><text x="${anchor.x}" y="${anchor.y - 20}" text-anchor="middle">${dates[index].slice(0, 5)}</text><text class="orbit-caption" x="${anchor.x}" y="${anchor.y + 28}" text-anchor="middle">${['首次提出', '持续补充', '形成归纳'][index]}</text></g>`).join('');
|
||||
}
|
||||
markup += plotted.filter(node => node.stage <= stage).map(node => {
|
||||
const story = stories.find(item => item.id === node.story);
|
||||
return `<g class="node ${node.id === selected ? 'selected' : ''} ${activeStory && activeStory !== node.story ? 'dim' : ''}" style="--story-color:${story.color}" transform="translate(${node.x} ${node.y})" tabindex="0" role="button" aria-label="${story.title}:${node.title}" aria-pressed="${node.id === selected}" data-node="${node.id}"><circle class="halo" r="22"/><circle class="core" r="${node.stage === 2 ? 10 : 7}"/><text text-anchor="middle" y="34">${node.label}</text></g>`;
|
||||
}).join('');
|
||||
graph.innerHTML = markup;
|
||||
showDetail();
|
||||
}
|
||||
|
||||
function selectNode(id) {
|
||||
selected = id;
|
||||
render();
|
||||
}
|
||||
document.querySelector('#timeline-list').addEventListener('click', event => {
|
||||
const button = event.target.closest('[data-story]');
|
||||
if (!button) return;
|
||||
activeStory = activeStory === button.dataset.story ? null : button.dataset.story;
|
||||
if (activeStory) selected = nodes.find(node => node.story === activeStory && node.stage === stage).id;
|
||||
render();
|
||||
});
|
||||
graph.addEventListener('click', event => {
|
||||
const node = event.target.closest('[data-node]');
|
||||
if (node) selectNode(node.dataset.node);
|
||||
});
|
||||
graph.addEventListener('keydown', event => {
|
||||
const node = event.target.closest('[data-node]');
|
||||
if (node && (event.key === 'Enter' || event.key === ' ')) {
|
||||
event.preventDefault();
|
||||
const id = node.dataset.node;
|
||||
selectNode(id);
|
||||
graph.querySelector(`[data-node="${id}"]`).focus();
|
||||
}
|
||||
});
|
||||
detail.addEventListener('click', event => {
|
||||
const button = event.target.closest('[data-target]');
|
||||
if (!button) return;
|
||||
activeStory = null;
|
||||
selectNode(button.dataset.target);
|
||||
});
|
||||
document.querySelector('#relations').addEventListener('change', render);
|
||||
document.querySelector('#view').addEventListener('change', render);
|
||||
document.querySelector('#reset').addEventListener('click', () => { activeStory = null; render(); });
|
||||
document.querySelector('#stage').addEventListener('input', event => {
|
||||
stage = Number(event.target.value);
|
||||
const story = activeStory || nodes.find(node => node.id === selected).story;
|
||||
selected = nodes.find(node => node.story === story && node.stage === stage).id;
|
||||
render();
|
||||
});
|
||||
document.querySelector('#theme').addEventListener('click', () => {
|
||||
document.documentElement.dataset.theme = document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark';
|
||||
});
|
||||
document.querySelector('#insight').addEventListener('click', () => {
|
||||
stage = 2;
|
||||
activeStory = null;
|
||||
document.querySelector('#stage').value = '2';
|
||||
document.querySelector('#relations').checked = true;
|
||||
render();
|
||||
detail.innerHTML = `<div class="detail-intro"><div class="detail-kicker">跨时间线聚合 / 示例</div><h2 class="detail-title">知识需要保留来路</h2><p class="detail-copy">三个故事指向同一个设计要求:不论是阶段总结、知识条目,还是分层记忆,归纳结果都需要连接原始依据。</p></div><div class="detail-block"><h3>来自 3 条时间线</h3>${['g2', 'k2', 'm2'].map(id => { const node = nodes.find(item => item.id === id); return `<button class="relation-link" data-target="${id}">${node.title} ↗<small>${stories.find(item => item.id === node.story).title} · 点击查看阶段与来源</small></button>`; }).join('')}</div><div class="detail-block"><h3>聚合说明</h3><p class="muted">以上为预置的聚合示例。实际功能需要结合来源生成关联依据,并允许用户核对。</p></div>`;
|
||||
document.querySelector('.inspector').scrollIntoView({ block: 'nearest' });
|
||||
});
|
||||
render();
|
||||
@@ -0,0 +1,39 @@
|
||||
# 故事线图谱产品与交互设计
|
||||
|
||||
日期:2026-09-16。状态:目标设计;当前 Demo 仅验证部分布局与交互。
|
||||
|
||||
图谱属于[监督者应用](../conversation-supervision/supervisor-prd.md),负责展示工作过程与知识演变。应用入口、心跳和知识库写入边界由该产品设计定义。
|
||||
|
||||
## 对象
|
||||
|
||||
对象之间的关系与监督者数据流见[逻辑设计](../conversation-supervision/logic-design.md),本文负责对象含义及用户操作要求。
|
||||
|
||||
FR-G1:外圈时间轴承载事件,内部节点表示持续积累的知识实体,阶段总结解释一个时间区间。同一知识实体可连接多个时间事件,分别表示提出、补充、验证或修订。新增方案不自动意味着旧方案已失效。
|
||||
|
||||
| 对象 | 内容 |
|
||||
| --- | --- |
|
||||
| 故事线 | 围绕某个工作主题形成的事件与知识集合 |
|
||||
| 时间事件 | 发生时间、所属故事、来源及影响的知识实体 |
|
||||
| 知识实体 | 当前描述、关联来源、演变记录与确认状态 |
|
||||
| 阶段总结 | 指定范围内的决定、变化和未解决事项 |
|
||||
| 关系 | 两端对象、关系类型、依据与确认状态 |
|
||||
|
||||
## 环绕时间轴
|
||||
|
||||
FR-G2:默认平铺视图将时间轴放在外围,逆时针围绕内部知识实体。起点、终点、方向和当前时间范围持续可见,外圈保留缺口,避免暗示时间循环。日期密集时聚合事件数量,展开后选择具体事件;时间位置采用真实比例还是等距事件布局需在下一轮原型中确定并明确标注。
|
||||
|
||||
选择事件后,高亮其影响的知识实体,右侧显示事件摘要和原始记录入口。外围时间点与内部实体使用实际连线;未选中的关系降低显著度。支持缩放、平移和聚焦,新增内容与回放尽量保留实体位置。
|
||||
|
||||
FR-G3:选择知识实体时,高亮对应的多个时间点与连线,标明提出、补充、验证或修订。右侧优先显示当前理解,再列演变和来源。历史回放下显示截至所选时刻的理解。
|
||||
|
||||
FR-G4:选择关系后,展示类型、关联理由及两端依据。跨故事关系与时间到实体的关系分别说明。用户可以确认、修改类型或移除关系;自动发现的关联标注待核对。
|
||||
|
||||
FR-G5:支持选择时间区间查看阶段总结,通过底部控制回放已发生事件及当时已知内容。圆形视图保留,用于观察独立故事聚合;视图切换保留范围、选择与回放时刻。集中度只描述关联分布,不等同于效率或产出,具体计算方式尚待设计。
|
||||
|
||||
FR-G6:默认展示主要实体及阶段事件,选中后展开一层关联,进一步展开由用户触发。提供文字列表完成与图形等价的对象选择、关系查看和来源跳转,遵循[监督者 UI 设计](../conversation-supervision/ui-design.md)的键盘与窄屏要求。
|
||||
|
||||
## 原型边界与下一轮验证
|
||||
|
||||
当前 Demo 使用阶段节点、固定坐标和预置关联,支持环绕时间轴、视图切换、回放、来源展开及模拟聚合;未实现真实实体合并、生成、历史版本、知识库接入或继续讨论。
|
||||
|
||||
下一轮场景:用户查看近期总结,选择“故事图谱布局”实体,看到三次讨论分别提出圆形、增加平铺、改为外圈逆时针时间轴,再查看来源并预览继续讨论的上下文。该场景应验证同一实体的多时点连接、事件和关系详情、阶段选择及上下文预览。验收映射见[User Stories](../conversation-supervision/user-stories.md) US-S04 至 US-S13。
|
||||
@@ -9,13 +9,24 @@
|
||||
3. [Scheduled Task PRD](./scheduled-task-prd.md):时间或事件触发的 Task。
|
||||
4. [Goal Task PRD](./goal-task-prd.md):围绕可验证结果有界推进的 Task。
|
||||
5. [Job 与 Subjob PRD](./job-and-subjob-prd.md):Task 内部串行、并行和委派执行。
|
||||
6. [技术设计](./technical-design.md):统计查询契约、请求证据与不完整数据边界,以及立即创建的事务和入队规则。
|
||||
|
||||
统计读取与共享存储、worker 生命周期和 Renderer 调度的通用约束见[运行时存储架构](../../architecture/runtime-storage.md)。
|
||||
|
||||
正式功能范围固定为当前项目:顶部显示当前会话消息总数和 Agent 累计回复时间,下方列出本项目全部会话的任务。创建默认立即执行并关联当前会话,可选择本项目其他会话、新会话或定时周期;产品与验收规则见 Task Center PRD 和 Scheduled Task PRD。
|
||||
|
||||
## 阅读顺序
|
||||
|
||||
[当前会话统计 Demo](./hierarchy-demo.html) 用于评审任务中心中的紧凑统计区:上方显示当前会话的消息数与 Agent 累计回复时长,下方管理当前项目全部会话的任务及用时。切换会话只更新会话统计,任务范围固定为当前项目,没有全局或所有项目入口。支持状态筛选、定位所属会话,以及暂停、继续和取消任务的模拟操作。新建任务支持填写内容、关联本项目已有或新建会话、立即或定时执行;仅在页面内模拟,刷新后恢复样例。
|
||||
配套 `hierarchy-demo.css` 与 `hierarchy-demo.js` 仅服务于该静态原型;使用演示数据,未接入产品。
|
||||
应用中的项目任务标题右侧仅保留 `+` 新建按钮;展开列表后直接显示状态选项,默认选中“进行中”,收起后保留所选状态。静态 Demo 的筛选入口仍为旧交互。
|
||||
时间为固定样例,Demo 从每次回复的起止秒数计算。正式功能按请求有效区间累计,包含轮内工具和审批等待、不计轮间空闲;任务用时按 `schedule_runs` 关联请求汇总,不按父任务生命周期计算。缺失历史有提示,远程缺少可靠证据时不推算用时,数据来源与边界以技术设计为准。
|
||||
|
||||
先阅读统一领域模型。其他功能文档不得重新定义 Task、Conversation、Job、Run 或 Subagent。
|
||||
若实现与文档出现冲突,应先修正统一模型,再同步功能 PRD。
|
||||
|
||||
## 相关功能
|
||||
|
||||
- [本机专用推理架构与任务控制评估](../../architecture/local-inference-task-control.md)记录 ASR、OCR、embedding 的任务与服务状态、执行所有权、取消、超时、清理及观测待办;推理请求不自动等同于本目录的产品 Task,候选迁移尚未采用。
|
||||
- [直连模型 Agent 能力](../direct-model-agent/README.md) 使用本目录定义的 Subagent 与
|
||||
Job/Subjob 语义,为直连模型提供单层编程委派,但不重新定义产品对象层级。
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
:root {
|
||||
color-scheme: light;
|
||||
--surface-canvas: #e8f0f5; --surface-raised: #fff; --surface-subtle: #f4f8fb;
|
||||
--text-primary: #17263a; --text-secondary: #4f6278; --border-default: #cedce7;
|
||||
--accent: #086fc7; --accent-subtle: #e8f4ff; --success: #15725d; --warning: #865200;
|
||||
--font-family-ui: "Inter Variable", "PingFang SC", "Microsoft YaHei UI", "Noto Sans SC Variable", system-ui, sans-serif;
|
||||
--radius-control: 10px; --radius-card: 14px;
|
||||
}
|
||||
[data-theme="dark"] { color-scheme: dark; --surface-canvas: #07131f; --surface-raised: #102335; --surface-subtle: #162c40; --text-primary: #edf7ff; --text-secondary: #b5c6d5; --border-default: #29445a; --accent: #72c5ff; --accent-subtle: #123b5b; --success: #73d9bb; --warning: #f2bd66; }
|
||||
* { box-sizing: border-box; }
|
||||
body { margin: 0; padding: 24px; background: var(--surface-subtle); color: var(--text-primary); font: 13px/1.65 var(--font-family-ui); }
|
||||
button, select { font: inherit; color: inherit; }
|
||||
button { cursor: pointer; border: 1px solid transparent; border-radius: var(--radius-control); padding: 8px 12px; background: transparent; }
|
||||
button:hover { background: var(--accent-subtle); }
|
||||
button:focus-visible, select:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
|
||||
select { width: 100%; min-width: 0; padding: 8px; border: 1px solid var(--border-default); border-radius: var(--radius-control); background: var(--surface-raised); }
|
||||
h1, h2, h3, p { margin: 0; } h1 { font-size: 24px; font-weight: 600; line-height: 1.4; } h2 { font-size: 14px; } h3 { font-size: 13px; }
|
||||
.demo-bar, .demo-footer { max-width: 1500px; margin: auto; display: flex; align-items: center; justify-content: space-between; gap: 16px; }
|
||||
.demo-bar { margin-bottom: 16px; }.demo-bar strong { font-weight: 600; }.demo-bar span { margin-left: 16px; color: var(--text-secondary); font-size: 12px; }
|
||||
#theme { border-color: var(--border-default); }
|
||||
.workspace { max-width: 1500px; margin: auto; display: grid; grid-template-columns: 240px minmax(0, 1fr) 328px; height: calc(100dvh - 132px); min-height: 640px; border: 1px solid var(--border-default); border-radius: var(--radius-card); overflow: hidden; background: var(--surface-raised); }
|
||||
.sidebar { display: flex; flex-direction: column; padding: 24px 16px; background: var(--surface-canvas); border-right: 1px solid var(--border-default); overflow: auto; }
|
||||
.brand { display: flex; align-items: center; gap: 12px; margin-bottom: 32px; font-size: 16px; }.brand-mark { display: grid; place-items: center; width: 32px; height: 32px; border-radius: var(--radius-control); color: var(--surface-raised); background: var(--text-primary); font-weight: 700; }
|
||||
label, .eyebrow { font-size: 11px; color: var(--text-secondary); }label { display: block; margin-bottom: 8px; }
|
||||
.muted, .metadata, .sidebar-note, .task-footnote { color: var(--text-secondary); font-size: 12px; }
|
||||
#project-meta { margin: 8px 0 32px; }.section-heading { display: flex; justify-content: space-between; align-items: center; gap: 8px; }.section-heading > span { color: var(--text-secondary); font-size: 11px; }
|
||||
#sessions { margin-top: 12px; display: grid; gap: 8px; }#sessions button { text-align: left; width: 100%; padding: 12px; }#sessions button[aria-current="true"] { color: var(--accent); background: var(--surface-raised); border-color: var(--border-default); }
|
||||
#sessions small { display: block; color: var(--text-secondary); margin-top: 4px; font-size: 11px; }.sidebar-note { margin-top: auto; padding-top: 32px; line-height: 1.9; }
|
||||
main { display: flex; flex-direction: column; min-width: 0; min-height: 0; }.conversation-header { padding: 24px 32px; border-bottom: 1px solid var(--border-default); }.eyebrow { margin-bottom: 8px; }.metadata { display: flex; flex-wrap: wrap; gap: 4px 16px; margin-top: 12px; }
|
||||
.messages { flex: 1; min-height: 0; overflow: auto; padding: 32px; }.message { max-width: 720px; margin: 0 auto 32px; }.message-author { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 12px; font-weight: 600; }.message-author time { margin-left: auto; color: var(--text-secondary); font-size: 11px; font-weight: 400; }.message p { font-size: 14px; line-height: 1.9; white-space: pre-line; }.message.user p { background: var(--surface-subtle); padding: 16px; border-radius: var(--radius-card); }
|
||||
.linked-task { display: block; margin-top: 16px; text-align: left; width: 100%; border-color: var(--border-default); color: var(--accent); }.linked-task small { display: block; color: var(--text-secondary); margin-top: 4px; }
|
||||
.composer { padding: 16px 32px 24px; }.composer-box { padding: 16px; border: 1px solid var(--border-default); border-radius: var(--radius-card); color: var(--text-secondary); }.composer-box div { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px; margin-top: 24px; font-size: 11px; }.composer > p { margin-top: 8px; font-size: 11px; color: var(--text-secondary); }
|
||||
.tasks { border-left: 1px solid var(--border-default); background: var(--surface-subtle); padding: 24px 16px; overflow: auto; }.tasks header { margin-bottom: 24px; }.tasks header p { margin-top: 4px; }.badge { border: 1px solid var(--border-default); border-radius: var(--radius-control); padding: 0 8px; }.scope-label { margin: 8px 0 16px; color: var(--text-secondary); font-size: 11px; overflow-wrap: anywhere; }
|
||||
.filters { display: flex; gap: 4px; border-bottom: 1px solid var(--border-default); padding-bottom: 12px; margin-bottom: 16px; }.filters button { padding: 4px 12px; font-size: 12px; }.filters [aria-pressed="true"] { color: var(--accent); background: var(--accent-subtle); border-color: var(--accent); }
|
||||
.task-card { background: var(--surface-raised); border: 1px solid var(--border-default); border-radius: var(--radius-card); padding: 16px; margin-bottom: 12px; }.task-card .status { flex-shrink: 0; font-size: 11px; color: var(--accent); }.task-card .status.done { color: var(--success); }.task-card p { color: var(--text-secondary); font-size: 11px; margin-top: 8px; }.empty { padding: 24px 8px; color: var(--text-secondary); text-align: center; }.demo-footer { color: var(--text-secondary); margin-top: 12px; font-size: 11px; }
|
||||
.session-stats { padding-bottom: 24px; border-bottom: 1px solid var(--border-default); }.session-stats h3 { font-weight: 500; }.stat-grid { display: grid; grid-template-columns: 1fr 1fr; margin: 24px 0 12px; gap: 16px; }.stat-grid dt { font-size: 12px; color: var(--text-secondary); }.stat-grid dd { margin: 4px 0 0; font-size: 20px; font-weight: 600; font-variant-numeric: tabular-nums; }.session-stats details { margin-top: 16px; font-size: 11px; color: var(--text-secondary); }.session-stats summary { cursor: pointer; }.session-stats details p { margin-top: 8px; }.task-heading { margin: 24px 0 12px; }.task-duration { display: flex; justify-content: space-between; margin-top: 16px; font-size: 12px; }.task-duration span { color: var(--text-secondary); }.task-duration strong { font-weight: 500; }
|
||||
.task-session { padding: 4px 0; margin-top: 4px; text-align: left; color: var(--accent); font-size: 11px; }.task-actions { display: flex; gap: 8px; margin-top: 12px; }.task-actions button { border-color: var(--border-default); padding: 4px 12px; font-size: 12px; }
|
||||
.task-toolbar { display: flex; gap: 4px; }.task-toolbar button { display: grid; place-items: center; width: 32px; height: 32px; padding: 4px; color: var(--text-secondary); }#new-task { font-size: 22px; line-height: 1; }.task-toolbar svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linejoin: round; }.task-toolbar .is-active { background: var(--accent-subtle); color: var(--accent); border-color: var(--accent); }#task-filter { margin-bottom: 16px; }#task-count { font-size: 11px; font-weight: 400; color: var(--text-secondary); }
|
||||
#task-form { padding: 16px; margin-bottom: 24px; border: 1px solid var(--border-default); border-radius: var(--radius-card); background: var(--surface-raised); }#task-form label { margin-top: 12px; }#task-form textarea, #task-form input { width: 100%; min-width: 0; padding: 8px; border: 1px solid var(--border-default); border-radius: var(--radius-control); background: var(--surface-raised); color: var(--text-primary); font: inherit; }#task-form textarea { resize: vertical; }#task-form p { margin-top: 12px; }#task-form button[type="submit"] { color: var(--accent); border-color: var(--accent); }textarea:focus-visible, input:focus-visible, summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }.task-card h3, .task-session, #sessions button { overflow-wrap: anywhere; }
|
||||
@media (max-width: 1100px) { .workspace { grid-template-columns: 200px minmax(0, 1fr) 280px; }.conversation-header, .messages { padding: 24px; }.composer { padding: 16px 24px; } }
|
||||
@media (max-width: 850px) { body { padding: 16px; }.workspace { height: auto; grid-template-columns: 200px minmax(0, 1fr); }.tasks { grid-column: 1 / -1; border-left: 0; border-top: 1px solid var(--border-default); }#task-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }.task-card { margin: 0; }.messages { min-height: 280px; }.demo-footer > span:last-child { display: none; } }
|
||||
@media (max-width: 560px) { body { padding: 8px; }.demo-bar { align-items: start; }.demo-bar span { display: block; margin-left: 0; }.workspace { display: flex; flex-direction: column; }.sidebar { border-right: 0; border-bottom: 1px solid var(--border-default); padding: 16px; }.brand, .sidebar-note { display: none; }#project-meta { margin: 8px 0 16px; }#sessions { grid-template-columns: repeat(2, minmax(0, 1fr)); }#sessions button { padding: 8px; }.conversation-header, .messages { padding: 24px 16px; }.composer { padding: 16px; }#task-list { grid-template-columns: 1fr; }.demo-footer { padding: 0 8px; } }
|
||||
@@ -0,0 +1,52 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>GoodBuddy · 当前会话统计</title>
|
||||
<link rel="stylesheet" href="hierarchy-demo.css">
|
||||
<script src="hierarchy-demo.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="demo-bar"><div><strong>GoodBuddy / 会话统计 Demo</strong><span>演示数据 · 时间为固定样例</span></div><button id="theme" aria-pressed="false">深色模式</button></header>
|
||||
<div class="workspace">
|
||||
<aside class="sidebar" aria-label="项目与会话">
|
||||
<div class="brand"><span class="brand-mark">G</span><strong>GoodBuddy</strong></div>
|
||||
<div class="eyebrow">当前项目</div>
|
||||
<strong>GoodBuddy 产品设计</strong>
|
||||
<p id="project-meta" class="muted">工作目录 · goodbuddy</p>
|
||||
<div class="section-heading"><h2>会话</h2><span id="session-count"></span></div>
|
||||
<nav id="sessions" aria-label="当前项目的会话"></nav>
|
||||
</aside>
|
||||
<main>
|
||||
<header class="conversation-header"><div class="eyebrow">GoodBuddy 产品设计</div><h1 id="conversation-title"></h1></header>
|
||||
<section class="messages" id="messages" aria-label="会话内容"></section>
|
||||
<div class="composer"><div class="composer-box">继续交流,或交代下一件事…<div><span>Execute · OpenCode</span><span>输入区仅作布局展示</span></div></div></div>
|
||||
</main>
|
||||
<aside class="tasks" aria-label="任务中心">
|
||||
<header><h2>任务中心</h2><p class="muted">当前项目 · GoodBuddy 产品设计</p></header>
|
||||
<section class="session-stats" aria-label="当前会话统计" aria-live="polite">
|
||||
<div class="eyebrow">当前会话</div><h3 id="stats-title"></h3>
|
||||
<dl class="stat-grid"><div><dt>Agent 累计回复时长</dt><dd id="duration"></dd></div><div><dt>消息数</dt><dd id="message-count"></dd></div></dl>
|
||||
<p id="message-breakdown" class="muted"></p>
|
||||
<details><summary>时间说明</summary><p id="time-description"></p><p>累计每次 Agent 回复的结束时间减去开始时间,不包含回复之间的空闲时间。任务用时单独展示,包含任务中的等待时间。</p></details>
|
||||
</section>
|
||||
<div class="section-heading task-heading"><h2>项目任务 <span id="task-count"></span></h2><div class="task-toolbar"><button id="new-task" aria-label="新建任务" title="新建任务" aria-expanded="false" aria-controls="task-form">+</button><button id="filter-toggle" aria-label="筛选任务" title="筛选任务" aria-expanded="false" aria-controls="task-filter"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 5h16l-6 7v6l-4 2v-8z"/></svg></button></div></div>
|
||||
<form id="task-form" hidden>
|
||||
<h3>新建任务</h3>
|
||||
<label for="task-content">任务内容</label><textarea id="task-content" rows="3" required maxlength="500" placeholder="描述要完成的工作"></textarea>
|
||||
<label for="task-conversation">关联会话</label><select id="task-conversation"></select>
|
||||
<label for="task-timing">执行时间</label><select id="task-timing"><option value="now">立即执行</option><option value="scheduled">定时执行</option></select>
|
||||
<div id="schedule-field" hidden><label for="task-date">计划时间</label><input id="task-date" type="datetime-local"></div>
|
||||
<p class="muted">仅在此页面模拟创建,刷新后恢复样例。</p>
|
||||
<div class="task-actions"><button type="submit">创建任务</button><button id="close-form" type="button">取消</button></div>
|
||||
</form>
|
||||
<div id="task-filter" hidden><label for="task-status">任务状态</label>
|
||||
<select id="task-status"><option value="all">全部状态</option><option value="进行中">进行中</option><option value="待执行">待执行</option><option value="已暂停">已暂停</option><option value="ended">已结束</option></select></div>
|
||||
<p id="active-filter" class="scope-label" hidden></p>
|
||||
<div id="task-list" aria-live="polite"></div>
|
||||
</aside>
|
||||
</div>
|
||||
<footer class="demo-footer"><span>切换会话更新统计;项目任务保持可见,可暂停、继续或取消。</span><span>范围固定为当前项目</span></footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,140 @@
|
||||
const conversations = [
|
||||
{ id: 'layout', title: '会话统计信息设计', userCount: 12, assistantCount: 12, start: '14:05', end: '14:32', replies: [[0, 12], [50, 74], [120, 136], [220, 240], [300, 332], [440, 458], [590, 615], [720, 750], [890, 930], [1100, 1145], [1350, 1378], [1580, 1620]], user: '任务中心可以增加当前会话、任务的时长和消息数,信息可能没多少。', reply: '右侧累计每次 Agent 从开始回复到结束回复的时间,不计两次回复之间的空闲。下方管理当前项目的全部任务,也可以直接新建任务。\n\n切换会话时只更新会话统计,项目任务列表保留。', tasks: [
|
||||
{ title: '制作统计布局 Demo', status: '进行中', duration: '6 分钟', time: '14:26 开始', active: true },
|
||||
{ title: '核对统计字段', status: '已完成', duration: '3 分钟', time: '14:21 至 14:24', active: false }
|
||||
] },
|
||||
{ id: 'review', title: '导航方案评审', userCount: 5, assistantCount: 7, start: '10:18', end: '11:46', replies: [[0, 30], [300, 350], [700, 740], [1400, 1470], [2200, 2260], [3500, 3580], [5230, 5280]], user: '整理一下导航方案中的问题,形成一份评审清单。', reply: '评审清单已整理完成,包括项目入口、会话导航和任务状态展示。', tasks: [
|
||||
{ title: '整理导航评审清单', status: '已完成', duration: '8 分钟', time: '11:38 至 11:46', active: false },
|
||||
{ title: '补充导航交互说明', status: '已暂停', duration: '2 分钟', time: '11:48 开始', active: true }
|
||||
] },
|
||||
{ id: 'ideas', title: '随手聊聊产品想法', userCount: 3, assistantCount: 3, start: '16:20', end: '16:38', replies: [[0, 8], [400, 411], [1071, 1080]], user: '消息数量应该算条数,还是算对话轮数?', reply: '显示消息条数更直观。用户发言和助手回复分别计数,工具调用不单独算成聊天消息。', tasks: [] }
|
||||
]
|
||||
let conversationId = 'layout'
|
||||
const $ = (id) => document.getElementById(id)
|
||||
const escape = (value) => String(value).replace(/[&<>"']/g, (char) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[char])
|
||||
function render() {
|
||||
const conversation = conversations.find((item) => item.id === conversationId)
|
||||
$('session-count').textContent = `${conversations.length} 个`
|
||||
$('sessions').innerHTML = conversations.map((item) => `<button data-conversation="${item.id}" aria-current="${item.id === conversationId}">${escape(item.title)}<small>${item.userCount + item.assistantCount} 条消息</small></button>`).join('')
|
||||
$('conversation-title').textContent = conversation.title
|
||||
$('stats-title').textContent = conversation.title
|
||||
// Demo intervals are reply start/end offsets in seconds, including idle gaps.
|
||||
const replySeconds = conversation.replies.reduce((sum, [start, end]) => sum + end - start, 0)
|
||||
$('duration').textContent = replySeconds >= 60 ? `${Math.floor(replySeconds / 60)} 分 ${replySeconds % 60} 秒` : `${replySeconds} 秒`
|
||||
$('message-count').textContent = `${conversation.userCount + conversation.assistantCount} 条`
|
||||
$('message-breakdown').textContent = `你 ${conversation.userCount} 条 · 助手 ${conversation.assistantCount} 条`
|
||||
$('time-description').textContent = `本会话 ${conversation.replies.length} 次已结束回复,累计 ${replySeconds} 秒。演示按每次回复的起止时间计算。`
|
||||
$('messages').innerHTML = conversation.userCount ? `<article class="message user"><div class="message-author">你 <time>${conversation.start}</time></div><p>${escape(conversation.user)}</p></article><article class="message"><div class="message-author">GoodBuddy <time>${conversation.end}</time></div><p>${escape(conversation.reply)}</p></article>` : '<p class="empty">任务已关联此会话,尚无消息。Demo 不执行真实 Agent。</p>'
|
||||
const projectTasks = conversations.flatMap((session) => session.tasks.map((task, index) => ({ task, session, index })))
|
||||
const status = $('task-status').value
|
||||
const filtered = status !== 'all'
|
||||
const filterLabel = $('task-status').selectedOptions[0].textContent
|
||||
$('filter-toggle').classList.toggle('is-active', filtered)
|
||||
$('filter-toggle').setAttribute('aria-label', filtered ? `筛选任务:${filterLabel}` : '筛选任务')
|
||||
$('filter-toggle').title = filtered ? `筛选任务:${filterLabel}` : '筛选任务'
|
||||
$('active-filter').hidden = !filtered
|
||||
$('active-filter').textContent = `筛选:${filterLabel}`
|
||||
const visibleTasks = projectTasks.filter(({ task }) => status === 'all' || (status === 'ended' ? !task.active : task.status === status))
|
||||
$('task-count').textContent = `${visibleTasks.length} / ${projectTasks.length} 项`
|
||||
$('task-list').innerHTML = visibleTasks.length ? visibleTasks.map(({ task, session, index }) => `<article class="task-card" data-session="${session.id}" data-index="${index}"><div class="section-heading"><h3>${escape(task.title)}</h3><span class="status ${task.active ? '' : 'done'}">${task.status}</span></div><button class="task-session" data-open="${session.id}">${session.id === conversationId ? '当前会话' : '会话'} · ${escape(session.title)}</button><div class="task-duration"><span>${task.active ? '已用时' : '总用时'}</span><strong>${task.duration}</strong></div><p>${escape(task.time)}</p>${task.active ? `<div class="task-actions"><button data-action="toggle">${task.status === '已暂停' ? '继续' : '暂停'}</button><button data-action="cancel">取消任务</button></div>` : ''}</article>`).join('') : '<p class="empty">当前项目暂无此状态的任务</p>'
|
||||
}
|
||||
$('task-status').addEventListener('change', render)
|
||||
$('filter-toggle').addEventListener('click', () => {
|
||||
const expanded = $('task-filter').hidden
|
||||
$('task-filter').hidden = !expanded
|
||||
$('filter-toggle').setAttribute('aria-expanded', String(expanded))
|
||||
if (expanded) $('task-status').focus()
|
||||
})
|
||||
$('task-filter').addEventListener('keydown', (event) => {
|
||||
if (event.key !== 'Escape') return
|
||||
$('task-filter').hidden = true
|
||||
$('filter-toggle').setAttribute('aria-expanded', 'false')
|
||||
$('filter-toggle').focus()
|
||||
})
|
||||
$('task-list').addEventListener('click', (event) => {
|
||||
const button = event.target.closest('button')
|
||||
if (!button) return
|
||||
if (button.dataset.open) {
|
||||
conversationId = button.dataset.open
|
||||
render()
|
||||
$('conversation-title').tabIndex = -1
|
||||
$('conversation-title').focus()
|
||||
return
|
||||
}
|
||||
const card = button.closest('[data-session]')
|
||||
const task = conversations.find((session) => session.id === card.dataset.session).tasks[Number(card.dataset.index)]
|
||||
if (button.dataset.action === 'cancel') {
|
||||
task.status = '已取消'
|
||||
task.active = false
|
||||
} else {
|
||||
if (task.status === '已暂停') {
|
||||
task.status = task.resumeStatus || '进行中'
|
||||
} else {
|
||||
task.resumeStatus = task.status
|
||||
task.status = '已暂停'
|
||||
}
|
||||
}
|
||||
const sessionId = card.dataset.session
|
||||
const index = card.dataset.index
|
||||
render()
|
||||
const next = $('task-list').querySelector(`[data-session="${sessionId}"][data-index="${index}"] button`)
|
||||
;(next || ($('task-filter').hidden ? $('filter-toggle') : $('task-status'))).focus()
|
||||
})
|
||||
$('sessions').addEventListener('click', (event) => {
|
||||
const button = event.target.closest('[data-conversation]')
|
||||
if (!button) return
|
||||
conversationId = button.dataset.conversation
|
||||
render()
|
||||
$('sessions').querySelector(`[data-conversation="${conversationId}"]`).focus()
|
||||
})
|
||||
$('theme').addEventListener('click', () => {
|
||||
const dark = document.documentElement.dataset.theme !== 'dark'
|
||||
document.documentElement.dataset.theme = dark ? 'dark' : 'light'
|
||||
$('theme').setAttribute('aria-pressed', String(dark))
|
||||
$('theme').textContent = dark ? '浅色模式' : '深色模式'
|
||||
})
|
||||
$('new-task').addEventListener('click', () => {
|
||||
$('task-form').hidden = false
|
||||
$('new-task').setAttribute('aria-expanded', 'true')
|
||||
$('task-conversation').innerHTML = conversations.map((session) => `<option value="${session.id}">${escape(session.title)}</option>`).join('') + '<option value="new">新建会话</option>'
|
||||
$('task-conversation').value = conversationId
|
||||
$('task-content').focus()
|
||||
})
|
||||
$('close-form').addEventListener('click', () => {
|
||||
$('task-form').hidden = true
|
||||
$('new-task').setAttribute('aria-expanded', 'false')
|
||||
$('new-task').focus()
|
||||
})
|
||||
$('task-timing').addEventListener('change', () => {
|
||||
const scheduled = $('task-timing').value === 'scheduled'
|
||||
$('schedule-field').hidden = !scheduled
|
||||
$('task-date').required = scheduled
|
||||
$('task-date').disabled = !scheduled
|
||||
})
|
||||
$('task-content').addEventListener('input', () => $('task-content').setCustomValidity(''))
|
||||
$('task-date').addEventListener('input', () => $('task-date').setCustomValidity(''))
|
||||
$('task-form').addEventListener('submit', (event) => {
|
||||
event.preventDefault()
|
||||
const title = $('task-content').value.trim()
|
||||
$('task-content').setCustomValidity(title ? '' : '请输入任务内容')
|
||||
const scheduled = $('task-timing').value === 'scheduled'
|
||||
$('task-date').setCustomValidity(scheduled && !(new Date($('task-date').value).getTime() > Date.now()) ? '请选择未来的时间' : '')
|
||||
if (!$('task-form').reportValidity()) return
|
||||
let session = conversations.find((item) => item.id === $('task-conversation').value)
|
||||
if (!session) {
|
||||
session = { id: `new-${conversations.length}`, title: title.slice(0, 24), userCount: 0, assistantCount: 0, replies: [], tasks: [] }
|
||||
conversations.push(session)
|
||||
}
|
||||
session.tasks.unshift({ title, status: scheduled ? '待执行' : '进行中', duration: '0 秒', time: scheduled ? `计划于 ${$('task-date').value.replace('T', ' ')}` : '刚刚创建 · 模拟执行', active: true })
|
||||
$('task-status').value = 'all'
|
||||
$('task-form').reset()
|
||||
$('schedule-field').hidden = true
|
||||
$('task-date').required = false
|
||||
$('task-date').disabled = true
|
||||
$('task-form').hidden = true
|
||||
$('new-task').setAttribute('aria-expanded', 'false')
|
||||
render()
|
||||
const created = $('task-list').querySelector(`[data-session="${session.id}"][data-index="0"] .task-session`)
|
||||
created.focus()
|
||||
})
|
||||
render()
|
||||
@@ -5,8 +5,8 @@
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 状态 | 首期稳定 Task 生命周期、创建体验与 Conversation 输入仲裁已实现;高级触发和执行治理待实施 |
|
||||
| 版本 | 0.8 |
|
||||
| 日期 | 2026-09-10 |
|
||||
| 版本 | 0.9 |
|
||||
| 日期 | 2026-09-17 |
|
||||
| 依赖 | [Task 与 Job 统一领域模型](./task-and-job-model.md) |
|
||||
| 相关架构 | [自动化平台总体设计](../../architecture/automation-platform-architecture.md) |
|
||||
|
||||
@@ -21,7 +21,7 @@ Scheduled Task 是带时间或事件触发器的 Task。每个 Scheduled Task
|
||||
|
||||
创建 Scheduled Task 时:
|
||||
|
||||
1. 用户选择关联当前 Conversation 或创建新 Conversation。
|
||||
1. 默认关联当前 Conversation,也可选择本项目其他可用 Conversation 或创建新 Conversation。
|
||||
2. 系统创建一个 Task,并保存稳定 `conversationId` 和 Schedule/Trigger Binding。
|
||||
3. 每次触发在同一 Task 内创建新的 Job 和 Run。
|
||||
4. 面向用户的文本进展和结果写回关联 Conversation,并标明 Task 来源。
|
||||
@@ -34,11 +34,11 @@ Scheduled Task 是带时间或事件触发器的 Task。每个 Scheduled Task
|
||||
|
||||
GoodBuddy 当前已实现首期统一生命周期:
|
||||
|
||||
- 创建 Modal 可以关联当前 Conversation 或原子创建新 Conversation,不修改当前
|
||||
- 创建 Modal 可以关联当前或本项目其他可用 Conversation,或原子创建新 Conversation,不修改已有
|
||||
Conversation 的标题和既有消息。
|
||||
- 每个 Schedule 绑定一个稳定产品级 Task 和 Conversation;重复触发复用同一身份,不再
|
||||
为每次触发创建新的顶层 Task。
|
||||
- 创建表单只设置任务要求、关联会话和时间;工作模式在关联会话中设置,未设置时使用项目默认值。
|
||||
- 创建表单设置任务要求、可选名称、关联会话和执行时间,默认立即执行;工作模式在关联会话中设置,未设置时使用项目默认值。
|
||||
- 单次、每日和每周计划支持暂停、恢复、立即运行和应用重启恢复。
|
||||
- 到期和手动运行先进入关联 Conversation 的持久输入队列,与回复期间继续发送的普通消息
|
||||
顺序仲裁;默认不打断当前回复,也不与其并发写入时间线。
|
||||
@@ -58,7 +58,7 @@ Job/Subjob/Run 的统一持久化抽象。当前每日和每周按既有 UTC 间
|
||||
|
||||
- 支持单次、每日、每周、每月、工作日和受限 Cron。
|
||||
- 支持 Task 完成、失败、Conversation 完成等内部事件触发。
|
||||
- 创建时明确选择当前或新 Conversation。
|
||||
- 创建时明确选择当前、本项目其他可用或新 Conversation。
|
||||
- 使用关联会话当前的工作模式;新会话使用普通项目默认值。
|
||||
- 在实际发送时解析上下文和执行配置,与手动发送共用执行流程。
|
||||
- 提供时区、错过执行、幂等、租约、重试、恢复、取消、预算和审计。
|
||||
@@ -82,10 +82,10 @@ Task Center 和 Conversation 操作都可以提供“新建定制任务”,但
|
||||
|
||||
```text
|
||||
新建定制任务
|
||||
创建一个可以按计划自动运行,并持续记录在会话中的任务
|
||||
创建一个立即或按计划运行,并持续记录在会话中的任务
|
||||
|
||||
任务名称 *
|
||||
[ 每周项目总结 ]
|
||||
任务名称(可选)
|
||||
[ 留空时从任务内容生成 ]
|
||||
|
||||
任务要求 *
|
||||
[ 总结本周完成和失败的工作,并列出下周优先事项。 ]
|
||||
@@ -97,11 +97,13 @@ Task Center 和 Conversation 操作都可以提供“新建定制任务”,但
|
||||
○ 新建会话
|
||||
为任务创建一条新会话,默认标题为任务名称
|
||||
|
||||
运行频率
|
||||
[ 单次 ] [ 每日 ] [ 每周 ] [ 每月 ] [ 工作日 ] [ Cron ]
|
||||
○ 本项目其他会话
|
||||
[ 选择会话 ▾ ]
|
||||
|
||||
首次运行 [ 2026-08-21 ] [ 17:00 ]
|
||||
时区 [ Asia/Shanghai ▾ ]
|
||||
执行时间
|
||||
◉ 立即执行 ○ 定时执行
|
||||
定时选项 [ 单次 ] [ 每日 ] [ 每周 ]
|
||||
首次运行 [ 日期 ] [ 时间 ](仅定时执行时显示)
|
||||
|
||||
关联项目
|
||||
GoodBuddy Desktop · 项目工作目录
|
||||
@@ -112,14 +114,18 @@ GoodBuddy Desktop · 项目工作目录
|
||||
|
||||
### 5.1 Conversation 选择
|
||||
|
||||
- 从当前聊天发起时默认选择当前 Conversation。
|
||||
- 从 Task Center 发起时默认选择新 Conversation。
|
||||
- 从当前聊天或 Task Center 发起时均默认选择当前 Conversation。
|
||||
- 可选本项目其他可用 Conversation 或新 Conversation;当前会话不可用时明确提示用户选择,不隐藏目标。
|
||||
- 当前选择必须持续可见,不能根据入口静默决定后隐藏。
|
||||
- 关联当前 Conversation 不修改其标题、既有消息和普通聊天能力。
|
||||
- 当前 Conversation 已有关联 Task 时,显示 Task 数量和共享上下文说明。
|
||||
- 新 Conversation 默认使用 Task 名称作为标题,用户可以单独修改。
|
||||
- 远程通道、归档、正在删除或 Project 不匹配的 Conversation 不可选择,并显示原因。
|
||||
|
||||
任务内容必填。名称可留空,使用压缩连续空白后的内容前 120 个字符生成。默认立即执行;
|
||||
选择定时后支持单次、每日和每周,首次运行时间必须在未来。每月、工作日、Cron 和 IANA
|
||||
时区仍属于后续能力,不显示为本次已支持选项。
|
||||
|
||||
### 5.2 创建摘要
|
||||
|
||||
提交前显示确定性摘要:
|
||||
@@ -133,8 +139,13 @@ GoodBuddy Desktop · 项目工作目录
|
||||
✓ 独立交付物保存到成果
|
||||
```
|
||||
|
||||
摘要随目标会话和执行时间更新,默认立即执行时明确显示“创建后进入会话队列”。
|
||||
创建 Task、可选新 Conversation、关联关系和 Schedule Binding 必须在 Main 中原子提交。
|
||||
失败时保持 Modal 和用户输入,不只显示短暂通知。提交期间锁定重复操作。
|
||||
立即创建同时提交一次执行及其队列项,只入队一次,遵循同会话串行规则;自动定时触发不得
|
||||
再添加一次执行。事务失败保留 Modal 和输入;提交期间锁定重复操作。创建成功后的刷新失败
|
||||
只提示刷新错误,不重新创建;后续执行失败显示在已创建任务中。
|
||||
数据库事务与队列实现见[技术设计](./technical-design.md#custom-task-creation),其中 schedule run
|
||||
与 queue 和创建记录在同一事务内写入,前端创建后不再调用 `runNow`。
|
||||
|
||||
### 5.3 Modal 行为与无障碍
|
||||
|
||||
@@ -346,7 +357,8 @@ Task Center 显示 Scheduled Task 的范围、关联 Conversation、状态、最
|
||||
|
||||
## 16. 验收标准
|
||||
|
||||
- [x] 创建 Scheduled Task 可以选择当前或新 Conversation。
|
||||
- [ ] 创建默认立即执行并关联当前 Conversation,可选本项目其他可用会话或新会话;名称可留空从内容生成。
|
||||
- [ ] 立即创建原子提交且只入队一次,定时器不重复触发;入队失败回滚创建,刷新或执行失败不重复创建。
|
||||
- [x] 关联当前 Conversation 不修改其标题、类型或既有消息。
|
||||
- [x] 一条 Conversation 可以在左侧展开一个或多个 Task。
|
||||
- [x] 工作模式使用关联会话当前设置,未设置时使用项目默认值。
|
||||
@@ -369,3 +381,5 @@ Task Center 显示 Scheduled Task 的范围、关联 Conversation、状态、最
|
||||
- [x] 文本结果只写入 Conversation,独立交付物才进入成果。
|
||||
- [ ] Task Center 和桌面通知可以打开正确 Conversation 并定位 Task。
|
||||
- [ ] 应用重启不自动重放结果未知的副作用。
|
||||
|
||||
本次新增或变更的创建验收项未在本轮文档同步中执行验证,保留未勾选状态。
|
||||
|
||||
@@ -5,22 +5,20 @@
|
||||
| 项目 | 内容 |
|
||||
| --- | --- |
|
||||
| 状态 | Scheduled Task 与项目范围跟随已实现;Goal/Event Task 与完整操作待实施 |
|
||||
| 版本 | 0.4 |
|
||||
| 日期 | 2026-09-10 |
|
||||
| 版本 | 0.5 |
|
||||
| 日期 | 2026-09-17 |
|
||||
| 依赖 | [Task 与 Job 统一领域模型](./task-and-job-model.md) |
|
||||
| 界面归属 | [通用助手工作栏与执行空间](../assistant-workbar/prd.md) |
|
||||
|
||||
## 1. 产品定义
|
||||
|
||||
Task Center 是工作栏中的单实例 Task 索引,默认跟随当前项目。它不是 Automation Center,
|
||||
也不复制 Conversation 内容。点击条目打开其关联 Conversation,并定位或展开对应 Task。
|
||||
Task Center 是工作栏中的单实例 Task 索引,范围固定为当前项目。顶部显示当前会话统计,
|
||||
下方管理本项目全部会话的任务。点击条目打开其关联 Conversation,并定位或展开对应 Task。
|
||||
|
||||
每个 Task 只关联一条 Conversation;一条 Conversation 可以关联零个、一个或多个 Task。
|
||||
Conversation 不因为关联 Task 而改变对象类型。
|
||||
|
||||
“单实例”和“项目范围”是两个独立属性:应用内只有一个 Task Center Tab,但该 Tab 默认展示
|
||||
当前项目的 Task。用户可以在同一 Tab 内切换到“全局任务”或“全部项目”,不能为每个项目创建
|
||||
独立 Task Center 实例。
|
||||
应用内只有一个 Task Center Tab,不提供全局或所有项目入口,也不为每个项目创建独立实例。
|
||||
|
||||
## 2. 收录边界
|
||||
|
||||
@@ -45,7 +43,7 @@ Job/Subjob/Run 树、独立详情或路由。
|
||||
|
||||
每条 Task 至少显示:
|
||||
|
||||
- 名称和 Global / Project 范围。
|
||||
- 名称和所属项目。
|
||||
- 关联 Conversation 标题。
|
||||
- Task 类型和触发来源。
|
||||
- Ask / Execute 模式。
|
||||
@@ -54,27 +52,47 @@ Job/Subjob/Run 树、独立详情或路由。
|
||||
- 最近活动时间。
|
||||
- 等待审批、失败或需要关注状态。
|
||||
- 下次计划时间(如适用)。
|
||||
- 累计执行时间:汇总该任务历次执行请求的有效区间,不使用父 Task 从创建到结束的生命周期。
|
||||
|
||||
Task 行只显示聚合后的用户状态,不要求用户理解内部 Job/Run。
|
||||
|
||||
## 4. 范围
|
||||
|
||||
范围控件提供:
|
||||
FR-TC1:当前项目是唯一范围,列表只显示 `projectId` 匹配的 Task。切换项目刷新列表;
|
||||
同一项目内切换会话只更新顶部统计,不按当前会话筛掉项目任务。没有活动项目时提示选择项目。
|
||||
历史范围偏好不恢复全局或所有项目视图;范围收窄不删除历史任务或修改其项目绑定。
|
||||
|
||||
- `当前项目`:默认值,跟随活动项目变化,只显示 `projectId` 匹配的 Task。
|
||||
- `全局任务`:只显示没有 `projectId` 的 Task。
|
||||
- `全部项目`:显示当前用户可见的项目 Task 和全局 Task,并保留每行的项目或 Global 标识。
|
||||
### 4.1 当前会话统计
|
||||
|
||||
没有活动项目时,“当前项目”显示明确空状态,并允许切换到“全局任务”或“全部项目”;系统不
|
||||
自动改变用户已经选择的范围。范围模式作为工作栏公开偏好持久化,但“当前项目”模式不保存
|
||||
具体项目 ID;重启后继续跟随届时的活动项目。
|
||||
FR-TC2:顶部紧凑展示当前会话消息总数与 Agent 累计回复时间。累计时间按每轮请求的有效
|
||||
执行区间相加,包含轮内工具执行和审批等待,不计轮间空闲;不重复累加子专家用时。
|
||||
缺失历史或时间证据不完整时提示“部分历史用时不可用”,已知用时不能表示为完整总量。
|
||||
远程执行缺少可靠时间证据时显示不可用或不完整,不用事件接收时间虚构远程用时。
|
||||
没有当前会话时提示选择会话,项目任务列表仍保留。
|
||||
项目名称放在当前会话统计卡片内部的顶部。
|
||||
|
||||
FR-TC3:任务累计执行时间按其历次执行请求汇总,周期触发仍显示在同一任务内。
|
||||
未执行与缺失执行证据必须区分;具体关联和统计边界见[技术设计](./technical-design.md)。
|
||||
|
||||
## 5. 交互
|
||||
|
||||
- 点击条目打开关联 Conversation,并定位到该 Task。
|
||||
- 支持按需要关注、进行中、已暂停、已结束筛选。
|
||||
- FR-TC4:项目任务标题右侧放置“+”新建按钮,不再提供独立筛选图标。
|
||||
项目任务列表默认收起,标题与数量、新建入口保持可见;点击标题切换展开状态。
|
||||
标题旁同时显示当前项目顶层任务中处于 `running` 状态的数量(如“2 运行中”),不受筛选或收起影响;为零时省略。
|
||||
展开后直接显示全部、进行中、暂停、已结束状态选项,初始选中进行中;收起不清除已选条件。
|
||||
- 进行中包含 `idle`、`queued`、`running`、`waiting_approval`、`failed` 和 `interrupted`;
|
||||
暂停对应 `paused`,已结束包含 `completed` 和 `cancelled`。分类只影响列表筛选,
|
||||
不改变任务状态语义;任务行保留具体状态、错误信息和原有操作。
|
||||
- 关联当前范围内顶层任务的审批直接显示在任务卡内,可拒绝或仅此次允许;有待审批的任务
|
||||
归入“进行中”,审批随任务卡参与状态筛选,不再重复显示独立审批分类或区域。
|
||||
- 普通会话或无法关联到当前范围内任务的审批保留独立处理区域,不受任务状态筛选影响;
|
||||
该区域仅在存在此类审批时显示。没有审批时不显示审批标题、空提示或徽标。
|
||||
- 审批徽标统计当前项目范围内全部待审批请求,不受状态筛选影响。
|
||||
- 支持立即运行、暂停、恢复、取消、编辑和删除。
|
||||
- 后台变化更新状态和徽标,不自动抢占当前页面。
|
||||
- FR-TC5:创建默认立即执行并关联当前会话,也可选择本项目其他可用会话或新建会话,
|
||||
改用单次定时、每日或每周计划。内容必填,名称可留空并从内容生成,详见[创建规则](./scheduled-task-prd.md#5-创建入口与-modal)。
|
||||
- 立即运行、重试和恢复在 UI 上都是 Task 操作,内部 Job/Run 不单独显示。
|
||||
- 完整消息留在 Conversation;工具、审批和错误可以在活动或 Runtime 中按 Task 查看;
|
||||
独立交付物在成果中查看。
|
||||
@@ -137,6 +155,11 @@ Task 行只显示聚合后的用户状态,不要求用户理解内部 Job/Run
|
||||
- [x] 普通模型请求和工具调用不会误显示为 Task。
|
||||
- [ ] 删除 Task 默认保留 Conversation 和既有消息。
|
||||
- [x] Smart Heartbeat 不进入 Task Center。
|
||||
- [x] Task Center 默认只显示当前项目的 Task,切换项目后在同一 Tab 更新列表。
|
||||
- [x] “全局任务”只显示无项目 Task,“全部项目”显示所有可见 Task 并标注所属范围。
|
||||
- [x] 范围切换不创建第二个 Task Center Tab;任务中心保持默认存在且不可关闭。
|
||||
- [ ] FR-TC1:仅显示当前项目任务,切换会话不缩小列表;无项目时显示选择项目提示。
|
||||
- [ ] FR-TC2:消息总数与累计回复时间随当前会话更新;轮内工具和审批计时,轮间空闲不计时,历史缺失及远程证据不足有提示。
|
||||
- [ ] FR-TC3:多次执行汇总到同一任务,不按父 Task 生命周期计算用时。
|
||||
- [x] FR-TC4:标题右侧仅保留新建;展开列表直接显示状态选项,默认进行中,收起后保留所选条件。
|
||||
- [ ] FR-TC5:创建默认立即执行和当前会话,可选择本项目其他会话、新会话及定时周期,空名称从内容生成。
|
||||
- [x] 任务中心保持单实例、默认存在且不可关闭。
|
||||
|
||||
本次新增或变更的验收项未在本轮文档同步中执行验证,保留未勾选状态。
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
# Execution Statistics Query
|
||||
|
||||
## Runtime Read Path
|
||||
|
||||
WAL、连接所有权、内存缓存和后台读取的通用规则见
|
||||
[运行时存储架构](../../architecture/runtime-storage.md)。
|
||||
|
||||
`tasks:execution-stats` 在 Main 校验来源、范围和当前回复租约,然后经
|
||||
`AssistantDatabase.getExecutionStatsAsync` 调用独立的 `execution-stats-worker`。
|
||||
worker 使用只读连接和短读事务执行下文查询,不运行数据库初始化或任务恢复。
|
||||
查询错误返回调用方,关闭数据库时终止 worker 并拒绝未完成请求。
|
||||
|
||||
worker 内按项目或会话范围缓存结果,最多 8 项、30 秒;每次读取先检查 SQLite 连接版本。
|
||||
活动回复绕过缓存,事务内读取清空并绕过缓存,关闭连接清空缓存。写入提交后的下一次读取
|
||||
会重新计算,覆盖本地消息、远程事件、任务删除和恢复。统计数据仍以查询快照时刻为准。
|
||||
|
||||
Renderer 按会话及所属项目、项目分别保留内存统计快照,切回已查询范围时同步显示缓存,
|
||||
同时后台刷新;首次打开未缓存范围仍等待查询,缓存不跨应用重启保留,也不在前端推算时长。
|
||||
保留可见时 5 秒刷新以及 revision、focus、visibility 触发刷新;失败的范围清除缓存并显示
|
||||
不可用,旧范围的迟到结果丢弃。重复缓存结果保留对象引用,更新通过 `startTransition`
|
||||
提交,避免无变化的轮询反复更新 App 的任务时长映射。任务面板使用 React `Activity`
|
||||
保留状态并降低隐藏时的渲染优先级;面板工厂在该边界内部调用。浏览器和终端沿用原有挂载
|
||||
与 effect 生命周期。
|
||||
|
||||
## Contract
|
||||
|
||||
`window.goodbuddy.tasks.getExecutionStats(input)` invokes `tasks:execution-stats`.
|
||||
`ExecutionStatsInput` accepts exactly one UUID scope: `{ conversationId }` or
|
||||
`{ projectId }`. Main validates the scope and trusted sender. The database returns:
|
||||
|
||||
```ts
|
||||
interface ExecutionStats {
|
||||
durationMs: number
|
||||
requestCount: number
|
||||
incompleteRequestCount: number
|
||||
activeRequestCount: number
|
||||
asOf: number // epoch milliseconds at query time
|
||||
taskDurations: Array<{
|
||||
id: string
|
||||
durationMs: number
|
||||
incompleteRequestCount: number
|
||||
}>
|
||||
}
|
||||
```
|
||||
|
||||
The project scope sums request evidence across its conversations and returns card
|
||||
totals in `taskDurations`. A scheduled execution uses `requestId = schedule_runs.id`;
|
||||
join its `schedule_id` to the stable task's `schedule_id` to aggregate repeated runs.
|
||||
Request creation also saves that proven relation in `parent_task_id`. Before plan
|
||||
deletion cascades its run records, bind older requests that still lack this link.
|
||||
Statistics can then use the retained link to a visible `origin = schedule` parent,
|
||||
even after the parent's `schedule_id` has been cleared. A shared conversation alone
|
||||
does not establish ownership. Other
|
||||
identified requests retain their own task ID. Visible tasks with no executions
|
||||
have a zero entry. Conversation queries return an empty `taskDurations` array.
|
||||
Message counts used to detect missing history stay in Main; no bodies are returned.
|
||||
|
||||
## Evidence And Boundaries
|
||||
|
||||
- Query all retained scoped task records, including hidden chat requests, without
|
||||
the visible-task list's pagination. Require a persisted message `request_id`
|
||||
link, a reply event (`text`, `reasoning`, `tool`, `done`, `error`, `subagent`,
|
||||
`generated-image`, or `question`) associated by `task_events.task_id`, a schedule
|
||||
run association, or a current reply lease. Local message snapshots do not persist
|
||||
request links, so local replies normally use event membership and kind. Do not
|
||||
parse each stream/tool payload merely to repeat its owning request ID.
|
||||
- Runtime status events also prove a reply when `requestId` matches the owning
|
||||
task, `type` is `status`, and `message` is a string. Maintenance status events
|
||||
such as `{ status: 'running' }` do not. Status-only interrupted replies retain
|
||||
their last evidenced interval just like text/tool replies.
|
||||
- Exclude stable schedule parents (`schedule_id` is set) and expert tasks
|
||||
(`origin = subagent`). Scheduled reply requests remain eligible. Nested expert
|
||||
execution is already inside the parent's elapsed interval and is not added again.
|
||||
- Maintenance-only records do not establish reply identity. This excludes manual
|
||||
context compression, heartbeat summaries, and unexecuted suggestions. A record
|
||||
with no reply identity evidence cannot be classified; it is not counted.
|
||||
- Sum intervals from `started_at` or later `running` status events to terminal
|
||||
events/statuses. Pause/queue closes an interval; a later run starts another.
|
||||
Repeated approval/running transitions do not reset the start. Tools and approval
|
||||
waits inside a reply are included; idle gaps between replies/runs are excluded.
|
||||
- `interrupted` may be written at application restart. Count only through the
|
||||
preceding evidence, mark the request incomplete, and never use its recovery
|
||||
`completed_at` as an execution endpoint. Missing or invalid timing also marks
|
||||
the request incomplete; unknown time is not replaced with zero-length certainty.
|
||||
- A non-aborted reply lease confirms current execution: an open local interval
|
||||
with a reliable start contributes through `asOf` and remains complete while active.
|
||||
Manual context compression leases are excluded.
|
||||
Without a live lease, stop at the latest evidence and mark an open interval
|
||||
incomplete. Renderer must query again after `asOf`; it does not extrapolate.
|
||||
- Remote-recoverable records, tasks in persisted SSH project execution spaces,
|
||||
and events with remote operation provenance are
|
||||
incomplete and contribute no duration: their receipt timestamps cannot establish
|
||||
remote execution intervals, particularly after replay. No Runtime, Agent, or
|
||||
remote persistence behavior changes are required by this read-only query.
|
||||
The SSH project check applies before the first provenance event, including
|
||||
channel/delegation requests with `remote_recoverable = false`, so an unknown
|
||||
remote duration is not first reported as complete local execution time.
|
||||
- Completed, failed, or running schedule runs with no request history count as
|
||||
incomplete, including in their stable task card. Pending runs do not count.
|
||||
- Former schedule parents are never execution clocks, including after deletion.
|
||||
A removed plan without retained execution ownership is reported as incomplete,
|
||||
not a complete zero; its former lifetime cannot reconstruct missing runs.
|
||||
- Retained top-level assistant messages establish missing-history evidence.
|
||||
Explicit missing request links count as incomplete; unlinked snapshots use a
|
||||
count comparison within each conversation/task, after covering linked replies.
|
||||
Missing schedule runs already counted above cover their messages, avoiding a
|
||||
second missing count. Expert-task messages and nested subagent content are not
|
||||
counted again. A wholly missing reply contributes zero known duration and one
|
||||
incomplete request, not a complete zero.
|
||||
- Exclude the first local assistant message only when it has no request/task link
|
||||
and exactly matches a shipped Chinese/English default greeting. Persisted
|
||||
greetings have no separate marker. Other assistant-only history and identical
|
||||
text later in the conversation remain eligible; do not discard all messages
|
||||
before the first user turn.
|
||||
- These counts are a lower bound when local snapshots have no request links;
|
||||
fully deleted history cannot be reconstructed. `requestCount` includes replies
|
||||
inferred from missing-history evidence. Concurrent top-level requests may
|
||||
overlap in wall-clock time; their durations are summed independently.
|
||||
|
||||
The query materializes scoped task evidence once, then returns only status and
|
||||
terminal events. Only status payloads use JSON extraction. Request membership and
|
||||
remote provenance inspect scalar event columns; last-event and pre-interruption
|
||||
timestamps use `(task_id, id)` index seeks. This preserves a tool/delta event as
|
||||
the last reliable boundary without loading its body. Grouped assistant-message
|
||||
counts read task metadata from message rows; event rows are not scanned for that count.
|
||||
It adds no tables, columns, migration, or persisted timing totals. Stable task
|
||||
lifecycle timestamps are never duration endpoints.
|
||||
|
||||
## Validation Coverage
|
||||
|
||||
The focused execution-statistics tests cover hidden requests, scope isolation,
|
||||
approval waits, repeated intervals, restart interruption, remote replay, missing
|
||||
start times, and history beyond visible-task pagination. IPC tests cover sender
|
||||
and scope validation; preload tests cover typed forwarding. Renderer integration
|
||||
must supply `getExecutionStats` in its `DesktopApi.tasks` mocks.
|
||||
|
||||
The large-payload regression inspects the production query plan, measures paired
|
||||
conversation/project polling on 3,000 events with approximately 94 MiB of payload,
|
||||
and replaces stream/tool bodies with invalid JSON to prove they are not parsed.
|
||||
Run it with `npx vitest run src/main/assistant/assistant-execution-stats.test.ts -t seeks --reporter=verbose --disableConsoleIntercept`
|
||||
to print timings. These are synthetic warm-query measurements, not a guarantee
|
||||
for arbitrarily large histories. Scalar event filtering and per-message metadata
|
||||
work still grow with retained history; if that becomes measurable in deployment,
|
||||
prefer polling only during active execution and refreshing on history/task changes.
|
||||
|
||||
## Custom Task Creation
|
||||
|
||||
The dialog defaults to the current conversation and immediate execution. Content
|
||||
is required; an empty name uses the first 120 characters of whitespace-normalized
|
||||
content. App supplies other local conversations in the current project and also
|
||||
allows creating a new conversation. Scheduled creation retains once/daily/weekly
|
||||
recurrence and requires a future timestamp, checked by both App and Main IPC.
|
||||
|
||||
`ScheduleCreateInput.runImmediately` is an optional creation command, not a stored
|
||||
schedule field. It is valid only for `recurrence: 'once'`. `createSchedule` creates
|
||||
the conversation (if needed), task, disabled schedule, pending schedule run and
|
||||
conversation queue item in one SQLite transaction. Queue insertion failure rolls
|
||||
back all of them. Disabling automatic scheduling prevents timer ticks from adding
|
||||
another occurrence; pending/running queue state still determines task status.
|
||||
|
||||
The create IPC handler publishes the queue change and wakes the existing
|
||||
conversation queue pump. App does not follow creation with `runNow`. Execution
|
||||
uses the existing readiness and conversation serialization rules. A runtime
|
||||
failure belongs to the created task; it does not reject creation or create another
|
||||
task. Refresh failures after a successful creation are notifications, not a reason
|
||||
to retry creation. No schema migration or preload changes are required.
|
||||
|
||||
`schedule-creation.test.ts` verifies transaction rollback and timer non-duplication
|
||||
with real SQLite. IPC tests exercise that database through the production queue
|
||||
and ordinary run handler with a controlled runtime, including failed execution.
|
||||
App creation tests cover destination selection, time validation, and failure handling.
|
||||
@@ -0,0 +1,136 @@
|
||||
# GoodBuddy 更新预览:画布笔记、文档处理与应用管理
|
||||
|
||||
> **编辑标注说明(对外发布前删除):**「待补图」是图片放置位置,含必补、建议和可选三种优先级;「待替换」是尚未确定的发布信息或外发链接;「待核对」只在最终版本行为有变化时改文案。以下图片均未拍摄,文件名仅为计划,不是已有素材。
|
||||
|
||||
魔法笔记可以在同一页里写字、画图、放图片,也能导入 PDF 做批注。聊天和知识库中的文档则可以查看解析后的图文,选取插图继续提问,保存的附件在重启后仍能回看。
|
||||
|
||||
这次更新以画布笔记和文档处理为重点,同时加入应用管理、Agent 执行清单和远程 Continue。应用管理也将成为后续私有化应用市场的入口。以下内容来自尚未发布的开发版本,正式版本号和下载入口待定。
|
||||
|
||||
> **【待替换 R01|标题与版本信息】** 正式版本可下载后,将标题中的“更新预览”改为实际发布标题,并替换上一段最后一句。参考句式:`GoodBuddy v{桌面版本} 已于 {发布日期} 发布。下载:{正式下载链接};完整更新日志:{更新日志链接}。` 所有字段须核验后填写;仍未发布时保留原预览说明,不填推测版本。
|
||||
|
||||
## 新增功能
|
||||
|
||||
### 画布笔记:文字、手写和图片写在同一页
|
||||
|
||||
原有富文本笔记之外,魔法笔记新增了分页画布。打开一本笔记,在顶部输入区选择“画布”,就可以从空白、横线、方格或点阵纸张开始记录。
|
||||
|
||||
画笔适合随手写画,高亮笔用来标记重点。正文可以键入并跨页排版,图片和浮动文字可以放到页面上,再移动、缩放或旋转。需要新的一页时可以继续添加,图文内容一起保存在这条画布记录中。
|
||||
|
||||
> **【待补图 01|必补|画布笔记主图】** 在这里放保存后重新打开的真实画布,画面同时包含键入文字、手写标记和虚构示意图片,尽量带上左侧记录索引,勿只拍空白画布。计划文件:`assets/01-canvas-note.png`。
|
||||
>
|
||||
> 建议图注:同一页画布中混排文字、手写标记和图片,保存后仍可继续编辑。
|
||||
|
||||
#### 在 PDF 上批注,保存后继续编辑
|
||||
|
||||
PDF 可以作为底版导入,在原页面上圈画重点、补写说明。点击“保存”后,以后仍能从这条记录的“编辑”入口继续修改。需要分享时,可以导出带批注的 PDF。
|
||||
|
||||
> **【待补图 02|建议|PDF 批注】** 在这里放一份虚构 PDF 导入画布后的截图,展示原文、高亮和补充文字。计划文件:`assets/02-pdf-annotation.png`。若改用导出文件截图,图注须明确是导出结果,不能暗示仍有可搜索文字层。
|
||||
>
|
||||
> 建议图注:将 PDF 作为底版,在原页面上圈画重点并补充说明。
|
||||
|
||||
导出的每一页由背景、文字和批注合成图像,不保留原 PDF 的可搜索文字层;需要检索或编辑原文时请保留原文件。画布导入扫描 PDF 本身不会触发 OCR,也不保证将手写内容转换成文字。
|
||||
|
||||
编辑和阅读都支持 25% 到 300% 的缩放,点击百分比回到 100%。“适应宽度”会随窗口或侧栏宽度调整,只改变观看大小,不改保存内容和导出尺寸。
|
||||
|
||||
笔记仍需手动保存。离开或切换编辑对象时会提醒未保存内容,但不提供自动保存或崩溃后的草稿恢复。每条画布记录最多 50 页,单个导入 PDF 上限为 32 MiB,单张图片上限为 20 MiB,其他限制见[画布说明](../../../src/renderer/src/magic-canvas/README.md)。
|
||||
|
||||
> **【待替换 R02|画布说明链接】** 对外平台发文时,将上方仓库相对链接换成最终发布版本对应、可公开访问的完整文档链接。若仅在仓库阅读,可保留相对链接;不要编造使用文档地址。
|
||||
|
||||
#### 请 AI 评论画布内容
|
||||
|
||||
书写、保存和导出无需模型。请求 AI 评论时,默认发送当前画布的第 1 页,可在魔法笔记设置的“发送画布页数”中选择 1~8,按页面顺序发送前 N 页;不足 N 页时发送全部页。这个数量指画布页数,不是笔记记录数。支持图片的默认模型收到所选页的图像和文字;纯文字模型只分析同一范围的文字,并标注未查看笔迹、图片和布局。所选页只有图像、没有可提取文字时,需要支持图片的默认模型。
|
||||
|
||||
> **【待核对 C01|最终版本的画布分析页数】** 核对最终发布提交是否包含“默认 1 页、可选前 1~8 页”的设置。若行为不同,按该版本的真实行为替换上一段,不直接沿用旧稿。此处不要求补 AI 结果图;没有真实模型生成证据时,不放模拟评论截图。
|
||||
|
||||
可以手动分析,也可以设为保存后自动分析。画布的“即时”模式同样需要手动点击,不会每画一笔就调用模型;分析失败不会撤销已经成功的保存。内容会发给配置的模型服务,多页画布会携带多张图片,费用按该服务计算。
|
||||
|
||||
#### 升级前请备份笔记
|
||||
|
||||
这次升级会把已有笔记正文和资源迁移到本地文件,数据库继续保存目录、索引等信息。**升级前请先备份。升级后的笔记备份要同时保留数据库和相邻的 `notes/` 目录。**
|
||||
|
||||
建议关闭应用后备份,恢复时将两部分一起恢复。只复制数据库,无法找回新版笔记正文和图片。目前没有一键备份按钮,具体要求见[备份说明](../../features/magic-notes/technical-design.md#backup-and-restore)。
|
||||
|
||||
> **【待替换 R03|备份说明链接】** 对外平台发文时,将上方相对链接替换为对应发布版本的公开备份说明链接,并确认能直接定位到备份章节。保留正文中的数据库与 `notes/` 成套备份提醒,不能用一个下载链接代替。
|
||||
|
||||
### 文档处理:查看图文、选图提问,之后还能回看
|
||||
|
||||
从聊天附件或知识库打开解析结果,可以查看正文中的文字、HTML 表格和已保存的插图。“图片”页集中展示提取出的图片,“详情”页记录本次解析的配置、耗时和警告,方便对照文件检查结果。
|
||||
|
||||
文档中的某张图需要单独讨论,可以在预览中选取,添加到目标会话,再交给支持图片输入的模型。选图只加入草稿,由你补充问题并发送。会话保存的是独立副本,之后删除或重新解析源文档,不会影响这张图片。
|
||||
|
||||
> **【待补图 03|必补|文档图文预览与选图】** 在这里放正常聊天导入后的真实解析预览,显示正文/图片入口及选图加入会话的操作或实际结果;不能用没有选图能力的“设置测试”窗口替代。计划文件:`assets/03-document-to-chat.png`。若图注提到 PaddleOCR-VL,必须使用该服务实际返回的结果。
|
||||
>
|
||||
> 建议图注:查看文档解析出的图文,选择需要讨论的插图加入会话草稿。
|
||||
|
||||
聊天文件会随草稿、消息或排队请求保存。只要这些记录仍引用附件,重启后就能重新打开文件和已有解析结果,回看无需再次做 OCR。历史附件要重新解析时,可以复制到当前草稿,原消息保持不变;排队中的附件需要修改,则先恢复到草稿。
|
||||
|
||||
知识库文档也能使用当前设置重新解析。新结果成功后才替换正文、分块和图片,失败时保留旧结果。
|
||||
|
||||
附件保存从本轮版本开始生效,旧消息中原本没保存的文件不会自动补回。
|
||||
|
||||
#### 扫描资料可以接入团队的 PaddleOCR-VL 服务
|
||||
|
||||
对于扫描 PDF 和图片中的文字,本轮新增 **HTTP PaddleOCR-VL 接入**。已有这项服务的团队,可以将它用于聊天附件和知识库导入,桌面端无需为此下载本地 OCR 模型。原有本地解析仍然可用。
|
||||
|
||||
在“设置 → 文档解析”选择“远程服务”,填写地址,并按部署选择无需认证或 Bearer API Key。服务配置共用,聊天和知识库可以分别选择解析方式:自动模式先读 PDF 的原生文字,仅将需要 OCR 的页面交给服务;高保真模式可以发送整份 PDF;快速文本/快速索引模式不上传 OCR 内容。PPTX 保留原生文字,并按设置识别幻灯片直接引用的内嵌图片。
|
||||
|
||||
内容过滤可以沿用服务默认、全部保留,或自行决定是否保留页眉、页脚、页码等内容。跨页表格可启用合并,整理结果保留来源页供核对。方向分类、去扭曲、图表与印章识别、标题层级调整也有配置入口,实际效果取决于文件和服务部署。
|
||||
|
||||
保存后,可以从“测试解析”选择聊天或知识库场景,用实际文件检查结果。测试文件不会加入正式会话或知识库;“检查连接”只确认服务可连接,不代替解析测试。
|
||||
|
||||
> **【待补图 04|可选|HTTP OCR 文件测试】** 若主图 03 不足以说明服务解析效果,在这里补真实文件测试的结果图,展示实际返回的文字、表格或插图。计划文件:`assets/04-http-ocr-test.png`。不暴露服务地址或密钥,不用连接成功提示冒充解析结果。
|
||||
>
|
||||
> 建议图注:用实际文件测试 HTTP PaddleOCR-VL 的解析结果。若不采用此图,删除本标注即可。
|
||||
|
||||
普通聊天图片默认仍以图片发送。只需要文字时,可选择“提取图片文字”,也能切回发送原图。手动提取会调用已保存的 OCR 来源,即使聊天采用快速文本模式。HTTP OCR 会把需要识别的页面或图片发到配置的服务,跨页整理还会增加请求,请确认资料使用范围和服务费用。
|
||||
|
||||
### 应用管理:当前管理内置应用,后续连接私有应用市场
|
||||
|
||||
侧栏底部的应用中心提供统一入口。进入“管理应用”,可以搜索、打开内置应用,修改可选应用设置,并调整四个应用的顺序。菜单和侧栏采用同一份保存后的顺序。
|
||||
|
||||
知识库和智能心跳始终显示;魔法笔记、本机推理监控可以设置是否启用、是否常驻侧栏。新配置中的本机推理监控默认不常驻,需要时从应用菜单打开,已有设置会保留。
|
||||
|
||||
> **【待补图 05|建议|应用管理】** 在这里放真实“管理应用”界面,显示当前四个内置应用、打开/设置入口及排序操作。计划文件:`assets/05-app-management.png`。不要使用未来应用市场原型,也不要添加当前没有的安装或市场目录界面。
|
||||
>
|
||||
> 建议图注:在应用管理中打开、设置和排列内置应用,侧栏使用同一份保存后的顺序。
|
||||
|
||||
本机推理监控可以查看语音识别、OCR、向量等本地服务的状态,执行各服务实际支持的启停、重启或模型加载、释放。独立向量进程可显示 CPU 和内存占用;共享进程的服务会说明无法独立统计的原因。它不管理远程 HTTP OCR 服务,关闭监控窗口也不会停止本地服务。
|
||||
|
||||
**后续的应用市场将支持组织私有化部署。** 按规划,团队可以部署自己的市场,用户配置该来源后获取组织提供的应用,不必依赖公共市场。当前这一轮先提供内置应用管理;市场目录、应用获取与安装属于后续建设内容,尚未上线。应用市场来源也不会替换模型、桌面更新或 Runtime 的下载来源。
|
||||
|
||||
### Agent 执行清单与远程 Continue
|
||||
|
||||
OpenCode 和 Continue 提供的原生执行清单会显示在对话顶部,列出条目状态和完成数量,可以展开或收起。清单随对应消息保存,原来的工具活动记录仍然可查,本地和远程的 OpenCode、Continue 都支持。
|
||||
|
||||
清单由 Runtime 更新,用户只能查看,不能手动勾选。请求结束或取消后,条目保留实际状态,剩余事项不会自动变成“已完成”。Runtime 没有提供清单时,普通回复里的列表也不会被当成执行进度。
|
||||
|
||||
远程项目新增 Continue 选择。安装匹配的 GoodBuddy Agent/Runtime 包后,可以从桌面发送请求,由远程主机执行,支持 Ask、Execute、停止当前请求及应用重启后续发;取消后也能在同一对话接着使用。仅更新桌面应用还不够,正式远端包及版本组合以发布说明为准。
|
||||
|
||||
> **【待替换 R04|远程 Continue 的配套版本与获取方式】** 正式发布时,将上一段最后一句补为已核验的桌面版本、Agent 包版本、对应 Runtime 版本及包获取/安装说明链接。参考句式:`使用远程 Continue 需 Desktop {版本} 与 Agent {版本},配套 Continue {版本};安装说明:{链接}。` 不能将开发测试包写成可下载正式包;正式组合未确定时保持草稿状态。
|
||||
|
||||
> **【待补图 06|建议|远程 Continue 执行清单】** 在这里放真实远程 Continue 会话产生清单后的界面,展开顶部清单,能辨认 Runtime 和条目状态。计划文件:`assets/06-runtime-checklist.png`。使用虚构任务,不注入假事件,避免主机地址、工作目录或私人对话出现在画面中。
|
||||
>
|
||||
> 建议图注:远程 Continue 的执行清单显示在对话顶部,条目状态随本轮执行更新。
|
||||
|
||||
### 会话置顶
|
||||
|
||||
常用会话可以从菜单置顶,重启后仍保留,也可以随时取消置顶。
|
||||
|
||||
## 功能改进
|
||||
|
||||
### 笔记记录连续浏览
|
||||
|
||||
同一本笔记的文字和画布记录连续排列。左侧索引显示画布首页缩略图和页数,点击后滚动到对应记录,正在写的内容留在原处。索引与 AI 评论区可以分别收起,宽窗口下还可以调宽。保存一条新记录后,顶部输入区清空,供下一条使用。
|
||||
|
||||
### 常用操作更集中
|
||||
|
||||
应用管理采用紧凑单列列表,打开、设置和排序操作放在对应应用旁。聊天与图片预览的下载入口统一为带提示的图标按钮。顶部移除了重复的模型/Runtime 状态标记,模型选择和切换反馈保留在输入区,自动状态检查失败仍通过通知提示。
|
||||
|
||||
## 问题修复
|
||||
|
||||
- **长会话历史被截断**:修复连续发送时只保留最近 500 条消息的问题,追加消息会保留已有历史。模型的上下文窗口和既有压缩规则不变,也不会恢复此前已经丢失的内容。
|
||||
- **较早会话找不到**:会话列表和搜索不再局限于最近 100 个会话,较早的未归档会话也能列出和搜索。
|
||||
- **取消后紧接着发送异常**:修复 OpenCode 上一次取消影响下一次请求的问题;已结束的回复也不会因迟到的状态更新重新显示“停止”按钮。
|
||||
- **弹窗遮住菜单和通知**:打开设置或其他对话框时,所属菜单和通知可以正常显示、操作,保留键盘导航和关闭后的焦点返回。
|
||||
- **网页的新窗口链接无响应**:修复内置浏览器直接拦截这类链接的问题,现在会在 GoodBuddy 管理的标签中打开;AI 点击产生新标签后,后续浏览器操作会跟随新页面。
|
||||
- **知识图谱抽取提前耗尽输出预算**:移除额外的固定 8192-token 限制。OpenAI 请求由模型服务决定输出上限,Anthropic 使用模型连接配置;服务自身的限制、超时和取消仍然有效。
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user