[BUG] An agent acted on an instruction the user never sent, and the assembled prompt is not recorded #6376
Replies: 3 comments 2 replies
|
wrong submit |
|
I checked the four things you looked at, and your blocker is real. The assembled per-step prompt is not recorded anywhere: the stored Two findings that may be useful in their own right, then the tool. 1. The system prompt is the one prompt surface with no per-section attribution, and the others were designed for it.
That asymmetry looks unintentional, because 2. Your "Not claimed" section is correct, but there is a related mechanism that matters for your case. I confirmed structured role-tagged messages reach the adapters unflattened ( But 3. A plugin that closes the gap without a core change.
It mounts on Your requested shape is what it records — an ordered list of {"request":{"sessionId":"...","turn":1,"step":29,"provider":"go","model":"deepseek-v4.1-flash","attempts":1},
"system":{"chars":4102,"digest":"sha256:..."},
"messages":[
{"role":"user","id":"m1","chars":32,"digest":"sha256:...","source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-agent-instructions","form":"instructions"}},
{"role":"user","id":"m2","chars":8,"digest":"sha256:...","source":{"kind":"user"}}]}Two design points worth stating, because they are what make it answer your question rather than merely be present: The record is not a reconstruction. Each message's The package also ships an offline verifier: Given the instruction text and your
Run against step 29, that separates your two explanations directly. Why a sidecar and not the session log. The storage contract rejects unknown event types on append unless the event is declared ignorable, and that declared-ignorable write path is not reachable from a plugin — so an audit record cannot live in the log without a core change. A sidecar keeps the audit available today. If the maintainers would rather have it in the log, the system-prompt section attribution in point 1 is the change that would make the log itself sufficient. Configuration is optional ( One caveat I would rather state than let you discover: the digest answers was this exact text delivered. It will not match a paraphrase or a partial delivery, so it is a delivery record, not full-text search. |
|
Follow-up: npm propagation has completed — |
Uh oh!
There was an error while loading. Please reload this page.
Environment
Harness: DeepSeek Harness (dsh) 0.1.5-rc.1; session log format v3
Runtime: Node.js v24.15.0 on Windows 10 Home (22H2, build 19045.7725), x64
Agent preset: standard; delegation depth 0; session not seeded
Provider route: go (via the dsh-llm-pi-ai adapter) → model deepseek-v4.1-flash
contextWindow 1,000,000; maxTokens 384,000; declared input modalities: text; compat.chatTemplateKwargs: {}
reasoning effort: not captured in the session artifacts
Permissions: preset workspace-write; sandbox workspace-write; approval policy ask
Tools registered at session start: 36
Summary An agent turn acted on a "user instruction" that no user ever sent, and modified a document in the workspace as a result. Local artifacts cannot show which layer produced that instruction: the assembled per-step prompt is not recorded, so we cannot tell whether a user-role message was delivered without being logged, or whether the model fabricated it.
Observed
A single agent turn contained 38 steps and 44 tool calls. Its only user input was a two-word "continue" (one user/message event).
At step 28 of that turn, the model's own assistant message text ended with a line reading user — a role marker glued to content, emitted inside its own output.
At step 29, that model's reasoning opened with The user says "", and the turn then performed 9 file edits to a document in the workspace.
The log contains no user/message event for that instruction. Counts reconcile exactly: 5 turn/start events and 5 user/message events with source.kind == "user", each carrying its own rpcId, all matching real human inputs. Within the turn in question exactly one user/message exists, and no agent/inbox/spliced insertion carries the instruction.
Impact
A contract-like document in the workspace was modified without authorization (9 edit calls across ~7 locations), and the agent reported the change as completed.
The approval policy was ask, but it did not apply: the writes were inside the workspace, which the workspace-write sandbox permits without approval. A turn whose only human input was two words therefore reached file modification with no confirmation step.
We reverted the change; this is not an ongoing data-loss report. We report it because the instruction's origin could not be established.
What we could not determine Whether (1) the harness delivered a user-role message that was not recorded, or (2) the model fabricated the instruction. Separating these requires the assembled prompt of step 29, which was not obtainable from local artifacts.
What we checked for an existing record
session.v3.jsonl.zstd stores events only; the stored request/header contains provider/model/maxTokens and tool schemas, not the prompt.
No log directory or logging switch under $DSH_HOME; settings.yaml exposes no audit or prompt-log option.
dsh-llm-pi-ai replay state is provider metadata used to reconstruct assistant messages, not the request prompt.
dsh-session-log-deepseek uploads the session log to the official API (opt-in, default off); it is not a local payload record.
Request Because the two explanations above cannot be separated locally, the following would settle it — optionally, and behind an audit/debug flag: persist the assembled per-step request, or at minimum a digest sufficient to answer "was this text ever delivered to the model as a user message?" — an ordered list of (role, message id, content hash) plus the identity/hash of the effective system prompt. Full content is not required for the digest.
Context (observation only — no request attached) In this session, instruction-like content reached the model through the user channel: user/message events carried source.kind values of agent-instructions (workspace instruction files, form: instructions, including a mid-session update), plugin (a runtime-context snapshot) and skill-catalog, alongside genuine source.kind == "user" messages. We mention this only because it means user-channel text is not by itself evidence of human authorship — part of why we wanted to inspect the assembled prompt.
Not claimed
No specific defect is claimed in DSH. We verified that dsh-llm-deepseek (serializeMessages), dsh-llm-pi-ai (toPiContext) and @earendil-works/pi-ai (openai-completions) all pass structured, role-tagged messages; none flattens messages into role-prefixed text.
No specific fix for the underlying authorization question is proposed here. This issue is scoped to auditability.
All reactions