fix(memory-core): expose extraction configuration in skill archive responses - #1358
Open
VittorioCai wants to merge 2 commits into
Open
VittorioCai wants to merge 2 commits into
VittorioCai wants to merge 2 commits into
Conversation
…sponses
The three archive entry points — /v3/skill/extract, /v3/skill/conversation/add
and /v3/skill/conversation/force-archive — answer a success with ok/status and a
task_id. That is accurate: SkillTriggerService.archive() writes the archive,
appends the SkillTaskEntry and calls enqueueAgent, all three inside the tasks
mutex. It has never meant that a Skill was produced, and with
skill.extraction.enabled=false nothing in the response says the accepted task
cannot be executed in this configuration.
Observed on standalone/sqlite: the response is code 0 with a task_id, the task
is registered in the agent's _tasks.json with enqueued_at_ms, the worker pool
starts, and every worker then logs
[skill-worker-pool] consumeAgent error: standalone SkillExtractor unavailable
and retries. None of that is visible to the caller, which therefore waits.
This adds one field, extraction_enabled, to the success responses of the three
entry points. It reports the configuration switch and nothing more:
- not worker readiness and not task completion — true only means the switch
is on;
- null, never false, when the resolved config cannot be reached, so
"cannot tell" stays distinguishable from "it is off".
Deliberately out of scope: the retry policy for tasks already in the queue, and
any claim that every storage mode honours the switch — the extractor is built
per storage mode (service builds one per instance, standalone uses the process
singleton). Error responses are unchanged.
Signed-off-by: Vittorio Cai <vittoriocaiyx@gmail.com>
The field is only useful if a caller knows it exists and knows what it does not mean. The three archive entry points' response shapes in v3-api-memorycore-doc.md, the TypeScript response interfaces and the Python client docstrings now all carry it, with the same limits stated in one place: the configuration switch, not worker readiness and not completion; null is "unknown", not "off". MemoryProxy and MemoryPanel pass the response through and ignore unknown fields, so they keep working untouched; surfacing the switch in those two surfaces is a separate change. Signed-off-by: Vittorio Cai <vittoriocaiyx@gmail.com>
VittorioCai
pushed a commit
to VittorioCai/TencentDB-Agent-Memory
that referenced
this pull request
Sep 12, 2026
…hecks are in the record Recorded in the three places a reader lands: the PR description's upstream section (now links TencentCloud#1358 instead of saying "prepared"), STATE's fifth-review section, and the candidate's own README, which gains the verification table. Checked after it opened, by comparison rather than by eye: base feat/server_team and head VittorioCai:fix/skill-extraction-flag; two commits, both authored by Vittorio Cai <vittoriocaiyx@gmail.com> and both carrying the DCO sign-off; +212/−5 in the same five files as locally; the body's five headings and six checkboxes are word-for-word the upstream template (diff of the '## ' lines is empty), with Bug fix and Documentation update ticked and both self-test lines carrying their command and result; zero hits for an AI-generated marker. No automated check runs on it — pr-ci.yml triggers on `pull_request: branches: [main]` and this PR targets feat/server_team — so the body's self-test is the only evidence. Noted with it that re-pointing the base at main is not an option (unrelated history), so a maintainer who wants CI has to trigger it manually or re-run locally.
VittorioCai
pushed a commit
to VittorioCai/TencentDB-Agent-Memory
that referenced
this pull request
Sep 12, 2026
The teacher's requirement on record (2026-09-07) is "GitHub branch code + documentation", not a pull request, so closing fork PR #1 costs nothing it was asked to provide. It also was not a good browsing entry: its diff baseline is upstream feat/server_team, so it counted the fork's entire delta against upstream — 2596 files, +233680 — rather than this work. PR-DESCRIPTION, STATE and REVIEW-GUIDE now name the branch as the deliverable (tree/topic4-attribution-gate) and say the PR was opened as an entry point and closed on the user's decision, with the branch and its history unaffected. PR-DESCRIPTION.md stays the delivery document; it simply stops being a PR body. Upstream PR TencentCloud#1358 is a separate branch's contribution and stays open.
VittorioCai
pushed a commit
to VittorioCai/TencentDB-Agent-Memory
that referenced
this pull request
Sep 12, 2026
Looking for whether this PR had an issue to point at turned up something better: the caller that cannot tell is the model itself. MemoryProxy injects skill_extract into every session's read-only tool set, describing it as triggering a skill extraction; the bridge's `sub === "extract"` branch forwards it to Core's /v3/skill/conversation/force-archive — one of the three endpoints this PR touches — and returns Core's envelope to the model verbatim (finalRespText = respText, rewritten only for team search). So with extraction off the model is told it triggered extraction, is handed a task_id, and has nothing in the answer to say otherwise. Issue TencentCloud#972 reported that same tool as injected-but-unusable. Its 40003 "trigger path retired" half no longer applies — the bridge now forwards instead of rejecting — but the other half does, and is now quieter: the call succeeds and still produces nothing. The body relates to TencentCloud#972 and states plainly that this PR does NOT fix it, since TencentCloud#972 asks for the tool to be removed from the injected set, which this does not touch. Corrects my own claim in the previous exchange that the body had already been edited; it had not.
VittorioCai
pushed a commit
to VittorioCai/TencentDB-Agent-Memory
that referenced
this pull request
Sep 13, 2026
TencentCloud#1359 gets its own folder — problem, fix scope, evidence, dedup, remaining defects — and the PR body is kept beside it so the submitted text is reproducible. TencentCloud#1358's folder gains the same status wording it was missing. Both are labelled "已提交上游,待审核" and nothing else. Three things are stated rather than left to inference: neither is merged; `mergeable=MERGEABLE` means no textual conflict, not a review verdict; and neither has any automated check at all, because upstream's pr-ci.yml triggers on `pull_request: branches: [main]` while both PRs target feat/server_team — so every "通过" in these folders means a local run and is written that way. evaluation/upstream/README.md indexes the two and records what was found but deliberately not submitted: one defect whose disclosure rule sends it to private email rather than a public issue or PR, which the user declined to send, recorded without detail; and the three other reproduced proxy defects, which wait behind delivery acceptance. PR-DESCRIPTION, STATE and REVIEW-GUIDE now describe two upstream contributions instead of one, with the same non-overclaiming wording.
Collaborator
|
Thank you so much for your attention and contribution! We will arrange an internal review for this PR shortly, and all feedback will be shared right here in the discussion. |
VittorioCai
pushed a commit
to VittorioCai/TencentDB-Agent-Memory
that referenced
this pull request
Sep 13, 2026
… longer matched the branch Read as a reviewer would, front to back. The worst find is that PR-DESCRIPTION.md — the delivery description itself — never mentioned the third dev-loop task. Zero hits for resource-download. Someone reading the document that says what was delivered would not have known the scenario exists, let alone that its first batch was voided. It is now item 5, with the three things that have to be said alongside the 0/4 against 4/4: every use event is needs_review, the working copy carried a shared alternative source that the two arms read unequally (4/4 against 2/4) so its effect on the gap cannot be determined, and the first sixteen runs were voided because I had left the reference implementation on disk. The later items are renumbered. Two documents claimed deliver-check "regenerates them all" and diffs them. It does not: thirteen are regenerated, one is registered as not regenerable with its cost, fifteen are historical. Both sentences now say what actually happens and point at the registry. Counts that had moved on: six rounds of review became eight (and rounds seven and eight are now written up in both PR-DESCRIPTION and REVIEW-GUIDE rather than only in STATE); conditions-check's twenty FAILs became twenty-one, and its post-batch class fourteen to fifteen; the committed run directories gained the third task's twenty-four; the sample line said 5+5 and 2+2 and now says 4+4 too; task five's row in the official-task table said two dev-loop tasks; and the claim that upstream PR bodies had been checked for the AI marker named only TencentCloud#1358 when there are four. One duplication removed: the gate-port bullet stated "four conflicts, all of them import lists" twice after an earlier edit. Dead-link scan across all four documents: zero. Suite 746/746, acceptance offline and live both pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description | 描述
POST /v3/skill/extract,POST /v3/skill/conversation/addandPOST /v3/skill/conversation/force-archiveanswer a success withok/statusand atask_id. That answer is accurate:SkillTriggerService.archive()writes the archive,appends the
SkillTaskEntryand callsenqueueAgent, all three inside the tasks mutex.It has never meant that a Skill was produced — and with
skill.extraction.enabled=falsethe accepted task cannot be executed in that configuration, while no field in the
response says so.
The caller therefore cannot distinguish "accepted, extraction under way" from "accepted,
extraction cannot run here": both are
code: 0with atask_id. A client that waits fora skill to appear waits forever, and a client polling
/v3/skill/listcannot tell anempty result caused by the switch from one caused by a session with nothing worth
keeping.
One of those callers is the product's own agent-facing tool. MemoryProxy injects
skill_extractinto the read-only tool set of every session(
MemoryProxy/src/injection/injectors/skill-tools-injector.ts), described to the model as"立即归档当前对话触发一次 skill 抽取(异步任务,由后台 agent 分析对话内容生成 skill)".
When the model calls it, the bridge forwards it to Core's
/v3/skill/conversation/force-archive(skill-bridge.ts, thesub === "extract"branch)and returns Core's envelope to the model verbatim — the response body is passed through
unchanged except for team-search filtering. So with extraction off, the model is told it
triggered extraction, is handed a
task_id, and has nothing in the answer to tell itotherwise. With this field it does.
This adds one field,
extraction_enabled, to the success responses of those three entrypoints. It reports the configuration switch and nothing more:
trueonly means the switch is on;null, neverfalse, when the resolved skill config cannot be reached, so"cannot tell" stays distinguishable from "it is off".
The archive contract is unchanged: the slice is still archived, the task is still
registered and enqueued, and error responses keep their existing shape. Rejecting the
call instead would be worse — the archive and the task entry are already written by the
time the handler returns, so a failure response would invite a retry that duplicates
them.
Related Issue | 关联 Issue
Related: #972 —
skill_extractis injected unconditionally while the call cannot do whatits description promises. The 40003 "trigger path retired" half of that report no longer
applies (the bridge now forwards
extracttoconversation/force-archive), but the otherhalf does: the tool is still advertised to every session, and when extraction is off the
call now succeeds and still produces nothing. This PR does not fix #972 — it does not
change what is injected into the tool set, which is what that issue asks for. It makes the
failure visible in the response instead of silent.
Also related but not overlapping: #1117 (see Additional Notes). No issue was filed for this
specific gap — it was found while running the product end to end and hitting it.
Change Type | 修改类型
Self-test Checklist | 自测清单
Verified locally | 本地验证通过
npx vitest run src/gateway/skill-archive-extraction-flag.test.ts— 6 tests, all passing:archiveis still called once,ok/task_id/archive_keyunchanged, andextraction_enabled: false;nothing in it speaks about the outcome of extraction;
nullin both cases: the optional dep absent, and the deppresent but returning
undefined(skill not yet constructed). Never a fabricatedfalse;conversation/add— reported both below the threshold (status: "ok") and at it(
status: "archived",archivedpreserved);force-archive— reported on both theemptyand thearchivedanswer;50001, original message) and the newfield is not injected into the error envelope.
Also reproduced live against a running Core — evidence under Additional Notes.
No existing features affected | 无影响现有功能
The field is additive on success envelopes only; no request schema, no status code and
no existing field changes.
bash scripts/ci/check-skill-queue-isolation.shwithBASE_REF=origin/feat/server_team→ PASS.tscon the touched file reports the same18 pre-existing diagnostics before and after (none added).
npm pack --dry-rundoesnot include the new test file (
!src/**/*.test.tsinfiles), so the publishedpackage is unchanged in shape. MemoryProxy and MemoryPanel pass the response through
and ignore unknown fields, so they keep working untouched.
Additional Notes | 其他说明
Reproduction
Live, on standalone + sqlite, with
skill.extraction.enabled: false:① the response — no mention of the switch:
{"code":0,"message":"ok","request_id":"req-6f8365f3512d416d", "data":{"ok":true,"task_id":"skill-extract-task-fe84b9ed", "archived_at_ms":1789235870833,"archive_key":"skill_buffer/…/data-….jsonl"}}② the task really is registered and enqueued —
…/agt-repro-extraction-flag/_tasks.jsonafter the call:{"tasks":[{"task_id":"skill-extract-task-fe84b9ed","enqueued_at_ms":1789235870833, "archive_key":"skill_buffer/…/data-1789235870833.jsonl", "…":"…"}]}③ the worker pool starts and then cannot execute it —
docker logs:An empty candidate pool is deliberately not offered as evidence: a real extraction
may also produce no candidates, so on its own it proves nothing.
Scope — stated explicitly
entry and the worker attempts.
storage mode — service mode builds one per instance
(
buildSkillExtractorForInstance), standalone uses the process singleton(
TdaiCore.getSkillExtractor) — and this reproduction covers the standalone path only.The field reports the process-level resolved configuration, which is what
getResolvedSkillConfig()returns.consumeAgent errorabove is left exactly as it is.extraction.enabled=truebut no LLMRunner the resolverrecords a degradation and keeps
enabled: true; this field still reportstrue, andthe doc says so. Surfacing degradations would be a different, larger change.
Relationship to #1117
#1117
(
fix(skill): expose extraction action outcomes) reports what extraction did —aggregate candidate outcomes in the worker's logs and task-completion metadata — and
touches
candidate-outcome.tsandextract-worker.ts. That is the worker side, afterextraction runs. This PR is the synchronous HTTP response, before anything runs, and
touches
skill-handlers.ts. No file and no field overlaps. The two are complementary:#1117 tells you what came of a task that executed, this tells you whether one can
execute at all.
A search of this repository's full pull-request history (not only recent PRs) for
extraction_enabled,skill.extraction.enabled, the three route paths,force-archive,and
skill-handlers/extract-worker/trigger-servicefound no other PR touchingthese response fields. #1216 also edits
skill-handlers.ts, but onlyhandleGet— nohunk overlap.
Size
26 lines in the implementation (
skill-handlers.ts: one helper plus four call sites),125 lines of test, and 61 lines of API-doc and SDK-type declaration —
v3-api-memorycore-doc.md, the three TypeScript response interfaces, and the threePython client docstrings — because a response field nobody has documented is not
finished.
CI
.github/workflows/pr-ci.ymltriggers onpull_request: branches: [main], so it doesnot run for a PR targeting
feat/server_team. The checks above were run locally instead;happy to re-run anything you would like to see.