Repository navigation
V2.0.1 - #123
Merged
Merged
V2.0.1#123
Conversation
…vider config loop
Config.get() overlays the SettingsStore DeepAgent runtime settings as a
settings-only pseudo provider.deepagent entry so gateway/intelligence
readers keep their historic options location. The provider config loop
treated that entry as a custom configured provider, so any user with
DeepAgent settings on disk got a ghost connected provider (19 catalog
models, source "custom") in the model picker without ever connecting.
Skip the entry when it carries no apiKey, no baseURL and no models.
Real connections via the auth key store ("api"), DEEPAGENT_API_KEY
("env"), or a genuine config/model-scoped entry are unaffected.
…et upgrades reach the handler
When a workspace is active, the workspace-routing middleware proxied /pty/*
requests (including WebSocket upgrades for /pty/{id}/connect) to the
workspace server. The workspace has no PTY handler, so the upgrade silently
dropped and the terminal timed out with 'Terminal creation timed out'.
Adding /pty to the local-rules table keeps all PTY REST and WebSocket
traffic on the control-plane process that owns the PTY sessions.
…lity egress Wire-form rows (epoch millis, not Date instances) failed the Schema.is guards in compatibilityEvent, silently skipping the legacy info rebuild. Clients then received native V2 session shapes without a top-level directory, which crashed the desktop sidebar sort in pathKey.
…shing A session payload without a directory reached sortedRootSessions and the undefined access took the whole layout down via the error boundary. Return a neutral key so the row simply never matches a root.
Pending reviews carry a null diagnosis; the table mapping crashed the whole command on 'review.diagnosis.status'.
@deepagent-code/plugin is not published to the registry, so the
unconditional per-config-directory install failed with a 404 on every
instance boot, and Plugin init blocks on waitForDependencies whenever
plugins exist — stalling instance-scoped requests (config.get,
provider.list) behind serialized doomed registry round-trips and
freezing project switches on slow networks.
- config: fork the install only for directories that actually host a
file-based plugin (auto-discovered plugin(s)/*.{ts,js} or a declared
file:// spec located under the directory); npm-packaged plugins
install through Npm.add and never needed this.
- npm: cache registry 404s per (directory, packages) for the process
lifetime so a repeated request skips the round-trip; the first
failure still surfaces for diagnostics.
fff logs a per-file diagnostic line at debug level; on repos where its git index queries systematically fail this flooded gigabytes of logs (a 1.1GB / 4.1M-line fff log was observed). Clamp the forwarded level to info when the app runs at DEBUG.
The watcher consumer is forkScoped, so disposing an instance interrupts it — the satisfied end state, logged as WARN 'Location index runtime stopped' with an opaque '[object Object]' cause on every project switch. Swallow interrupt-only causes and pretty-print real failures.
A dispatch refused because the event type is not in the V2 admission registry (e.g. the daily schedule.scan until its product lane ships) can never succeed on retry, but was nacked through the full retry budget into the DLQ with an ERROR per attempt. The bridge now raises a typed EventNotRegisteredError and the dispatcher settles it as a terminal drop (recordDrop + ack).
Drop unrouted /:dir/agent and /:dir/review pages, the orphaned side-panel-oversight/side-panel-mcp wrappers, and the right-sidebar MCP rail that duplicated the top-bar StatusPopover (V2.0.1-001 WS1 P0).
RejectedError/CorrectedError/DeniedError carried no message, and 15 tool mapError sites rewrote them to generic "Unable to ..." text, so the model could not tell a user denial (or its feedback) from a technical failure. Port the V1 wording onto the V2 error classes and pass it through via a shared permissionFailureMessage helper (V2.0.1-001 WS5/B1).
History rebuild discarded state.result for non-provider-executed tools
and synthesized an {error, content, structured} object that protocols
stringify to the literal "[object Object]", so every local tool error
(including permission denials) became unreadable after a history
reload. Reuse the persisted ToolResultValue when present
(BUG-V2.0-003, V2.0.1-001 WS5).
The UI hid reverted messages client-side while the model kept receiving them, and goal-loop rollbacks rewound the workspace silently. Publish an idempotent durable Synthetic notice (deterministic ID keyed on session+mutationEpoch, resp. goal+tick) so the model can re-calibrate. Autonomous paths pass notice:false so only the actor-accurate rollback notice lands (V2.0.1-001 WS6 C2/B5).
Knowledge Governance + Repo&Wiki + Domain Packs merge into one Knowledge dialog (review/wiki/packs tabs); Help moves to a Settings About tab; Plugins becomes a Settings tab; Debug + Profiler + Context evidence merge into one Dev panel; Usage&cost moves into the SessionContextUsage dialog with an All-sessions tab; Browser rail is desktop-only. Legacy stored panel modes fold to the new modes (V2.0.1-001 WS1).
The duplicated TUI/app formatters move to @deepagent-code/sdk/transcript as the canonical formatter for TUI /export, the GUI session.export command, and the CLI. Adds file/patch/subtask/compaction part coverage, message timestamps, title-based filenames, no-overwrite dedupe, a visible GUI menu entry, and deepagent export --format md (V2.0.1-001 WS2).
The compaction checkpoint wrapper now carries survival rules telling the model to re-verify state with tools, and turn prep injects an ephemeral volatile budget notice at 70%/90% of the same occupancy estimate that drives compaction. normalizeState no longer resets an explicitly configured maxTotalTokens (V2.0.1-001 WS6 B3/B4).
V2.0.1-001 WS3+WS4b: - WS4b: task_status/task_read (read-only), task_close/task_recovery, pr_finalize (thin over core TaskPRReview); task output gains branch visibility, an 8k tail-biased bound with a task_read pointer, and an optional file_scope with non-blocking sibling overlap warnings; task_timeout now retains the worktree (resumable) instead of pruning it while telling the parent to resume. - WS3: pack_search + domain_pack_load (per-turn cap 2, ~600-token bodies, session dedupe) make the 136 seeded domain packs reachable by the model; the inactive capability-load prototype is removed. - Registration gates (builtins/inventory/readOnlyActions/tool-surface guard) updated for all seven new tools.
V2.0.1-001 WS3/WS4a content wave: - New deepagent.dac-manual domain pack: 19 indexed documents (orchestration, code intel, context survival, permissions, reliability) plus 2 failure dossiers, each 339-410 tokens in the five-part manual format, discoverable via pack_search and loadable via domain_pack_load. - L0 capability catalog grows 6 -> 15 manifests (task orchestration, oversight, worktree merge loop, manual chain, plan/question/skill, permission-denial and context-survival concept rows); approved L0 budget raised 700 -> 1000 tokens, measured render 727. - task.txt rewritten (45 lines): full roster, worktree/merge-loop semantics, file_scope, guardrails, timeout/recovery, result bound.
Tool failures from permission rejections now carry metadata.failureCode (user_rejected_permission / user_corrected_permission / permission_denied_rule) through the registry settlement into the durable Tool.Failed event, whose error.type narrows to permission_rejected / permission_corrected / permission_denied. Model-visible text is unchanged; the five protocol lowerings are untouched (V2.0.1-001 WS5 phase 2).
knowledge_propose gives the model a "remember this" write entry that stages a human-review candidate (sensitivity scrub, 10/session cap); im_send delivers a message to the session's bound IM group behind an ask-by-default permission, AgentPushPolicy gates, and an audit row. Both join the L0 catalog (827/1000 tokens) and the dac-manual pack (V2.0.1-001 WS7 B6/C4).
Classify the nine V2.0.1 tools (task_status/task_read/pack_search/ domain_pack_load read-only; task_close/task_recovery/pr_finalize/ knowledge_propose/im_send mutating) so the plan gate covers the whole builtin registry again, and re-pin the C7-10 manifest digest to the current tree (the previous pin was already unreproducible on origin/dev; the contract group's L0-budget literal drifted in a7d8ed2).
…udget exhaustion
bug-V2.0-003: the V2 foreground task tool dropped the 1.0 degraded escape.
finalizeStructured hard-failed the parent turn when both bounded finalizer
attempts exhausted; the 1.0 finalizer settled degraded instead. Port the
degraded settlement: the exhausted loop returns a receipt-stamped
{_degraded,_reason,_attempts,_raw} payload (structured_output_missing /
structured_output_invalid) and seals a durable validation_failed row in
session_v2_structured_output_evidence for fresh durable launches. Attempt
budget, prompt shape, the validated success path, and the timeout hard
failure are unchanged.
…roducibility K-05/B-11: the C7-10 manifest digest historically read the git-ignored packages/core/.artifacts/ tree (caller-inventory report, perf baseline), so a machine that had run those tools pinned a digest no clean checkout could reproduce (O-TEST-3 / O-W12-3). - manifest.ts: the input set is now closed over committed, git-tracked files (contract / migration registry / package versions / runtime flag-config), itemized in the exported ManifestInputRoots; the includeExternalEvidence option and the live .artifacts reads are gone. External evidence enters only as explicit content-addressed extraInputs, which is how the candidate-ledger generator already binds the freshly produced inventory report. - head-pin.ts: the pinned HEAD digests move to a single machine-readable source of truth shared by the C7-10 test, the reproducibility gate and the docs-claims gate. Values are unchanged: the pin 001f5b1 recorded was already computed over the committed-tree-only set. - assert-reproducible.ts: new gate asserting byte-stable double generation, pin match, no untracked/ignored .ts under the input roots, input-set closure from exactly the listed files, and immunity to a planted .artifacts decoy tree. Wired into CI as the manifest-digest-gate job in test.yml. - script/assert-docs-claims.ts: new root gate (assert:docs-claims) that machine-checks release evidence products — deterministic-manifest internal consistency plus referenced-input existence and content hashes, evidence manifest decode and (at HEAD) file-backed digest recomputation, and the full authoritative-ledger digest chain.
The column stopped being a timestamp long ago: it carries the V2 execution claim's random CAS fence token, and every production reader already uses the claim alias (current_session_claim_token, executionClaimToken). Rename the column and its partial index to make the semantics unambiguous. - migration 20260922152631_execution_claim_token: ALTER TABLE session RENAME COLUMN, then DROP session_time_suspended_idx + CREATE session_execution_claim_token_idx (RENAME COLUMN does not rename indexes); partial-index semantics (WHERE ... is not null) preserved - drizzle snapshot advances with the rename recorded; registry resealed - mechanical rename across src and tests (138 references); the frozen 20260803000000_time_suspended migration, its registry entry, and its isolation test keep the old name - new static gate (session-claim-token-gate.test.ts): only session/store.ts and session/runner/recovery-durable-store.ts may write the column; any other SessionTable update or raw-SQL write of execution_claim_token fails the gate
…on compaction POST /api/session/:sessionID/compact previously admitted no payload, so the handler called SessionV2.compact without a model and always hit the typed explicit-model refusal, surfacing a permanent 503. The contract now requires providerID/modelID (mirroring the instance summarize payload), the handler passes them through, and the 503 body carries the concrete refusal reason (empty history, unsettled budget, terminal outcome). A missing model is a typed 400 contract rejection; the server never defaults an identity. Also retire stale comments that still referenced the removed host-side CurrentManualCompaction seam; they now describe the durable RI-18 CompactionRequest chain.
…nment, gzip stream errors, fixture dedup F-2: advisory-sourced reclaim candidates are re-validated against the live scan's containment + residue-shape rules — a tampered or corrupted advisory JSON can no longer widen the delete set beyond dataRoot (regression test stages a real chain, tampers the advisory, asserts the outside path survives confirm:true). F-3: every stream in the gzip archive chain gets an error listener — a mid-stream failure now surfaces as the typed archive_failed instead of an unhandled 'error' event crashing the maintenance process (regression test replaces a backup file with a directory). Also dedupe the compaction-request CREATE lines the k01 merge doubled in the two handcrafted inventory fixtures (both sides had added the same table).
F-3 regression asserts the corrupted source stays in place (a partially created destination artifact races the error and is acceptable); the provider-resolution facade joins the recovery route's owning paths (route totality); the export help snapshot gains the --format md option the V2.0.1-001 WS2 export wave added.
… env whitelists The child-command whitelists in run-live-llm-all and pre-push-live-llm dropped HOME: bun install children lost ~/.bunfig.toml AND the global bun cache, so a warm no-op install became a full cold download through the default registry — on networks where that registry is unreachable the setup gate hangs to its 20-minute timeout. HOME plus BUN_CONFIG_REGISTRY now flow through; the pinned prePushEnvironment test covers both.
The V2 port rendered 'Command exited with code N.' — validation classifiers regex on the canonical /exit code: N/ form (V1 shell.ts ground-truth trailer) and read its absence as a missing success marker; the live shell-exit-contract suite failed on every bash result. Align the trailer wording (single line, last, null renders 'null (terminated)') and pin the unit expectations.
The journal-to-V1-wire projection filled part.state.metadata from
provider.resultMetadata alone, which is absent for every locally-executed
tool, so every projected V2 session serialized empty tool metadata. The
bash exit code disappeared from the CLI run output (scrollBashFinal reads
metadata.exit) and the live battery lost every truncation/exit assertion
(bash-truncation metadata {}, bash-repair exit 17, shell-exit-contract
Oracle 3).
Project the completed ToolState's durable structured output as the
metadata authority, alias exitCode to the legacy exit vocabulary every V1
consumer reads, surface ToolOutputStore truncation through the legacy
truncated/outputPath keys, and keep the provider channel (plan protocol
riders) as the override layer on explicit key collisions.
The headless suite spawned the from-source CLI without any V2 owner or roster wiring, so after the 2.0 cutover (V2 owner gate default-on, V2 agent-roster admission) every run refused before the first provider turn: the entry dev-mint skipped itself (InstallationVersion "local" is not a 0.0.0- dev stamp) leaving v2_owner_unavailable, and once armed the AgentV2 roster was empty because DEEPAGENT_CODE_CONFIG_CONTENT only reaches the V1 app config while the Core Location config reads config files. - pin DEEPAGENT_CODE_V2_OWNER_DEV_MINT=1 (same pin as test/lib/cli-process.ts) so the entry bootstrap mints the dev owner row - write the workspace deepagent-code.json so the Location config (and the roster admission resolves through) sees the live-test agent, mirroring the in-process fixture - accept the V2 read tool's relative `path` input alongside V1's absolute `filePath` in the canonical read assertion
…e tool metadata wire projection
The CoreTaskTool finalizer built its durable-evidence input with a
misnamed property — { outputMessageID } against the authority's
outputMessageId field. The conditional spread defeated excess-property
checking and the optional field dropped silently, so every 'validated'
record inserted output_message_id: NULL and the insert guard aborted
with 'invalid v2 structured output evidence' (previously misread as a
UNIQUE(run_id) race). live subagent-foreground hit that abort on every
deepseek run, and the tool's retry guidance re-ran into the same abort.
- tool/task.ts: pass the binding under the authority's field name; the
parameter is renamed to match so the shorthand cannot drift again.
- session/task-run.ts: the evidence insert is now the UNIQUE(run_id) CAS
in one statement (ON CONFLICT DO NOTHING plus a post-conflict
re-read), so a writer landing between the pre-read and the insert
converges or diverges typed instead of surfacing a raw constraint
error. The recorded verdict is still never overwritten and divergence
stays the typed StructuredEvidenceConflict.
- tests: a validated finalizer candidate now seals bound evidence
(regression for the misnamed field), and concurrent double-writes of
the same run converge on one row with divergence still typed.
…sage + single-statement CAS convergence
Port the V1 #26514 subagent permission-narrowing semantics (deepagent-code deriveSubagentSessionPermission) to the core V2 task path, role-aware like the V1 original: - the parent AGENT contributes only its edit rules (deny/ask forward as deny, allow forwards verbatim) — the parent agent's broader working posture (a blanket deny-all plus narrow task-tool allows) governs the parent's own tools and must not strip the delegated agent's built-in allow face; - the parent SESSION forwards deny rules verbatim as hard runtime ceilings and ask rules as deny, while an ALLOW forwards only when it names a concrete non-wildcard action whose (action, resource) pair the delegated agent's own ruleset already permits independently, order preserved. Previously every parent non-allow rule forwarded as a child deny, so the recommended deny-all parent posture derived a bare deny-* child ruleset: every tool definition was filtered out (isActionWhollyDenied) and the child session ran with zero tools. Delegation still can never turn ask/deny into allow: a forwarded allow cannot grant anything the delegated agent's own ruleset does not already permit, and deny-wins evaluation keeps the child's own denies final.
…eground The task_read input schema is provider-tolerant (limit: number | numeric string), so a model sending limit "100" is a valid complete-transcript request; compare numerically instead of strictly.
… V2 contract The V2-owner cutover (6f171ea61) deleted the legacy session-metadata projection: deepagent.subagent state, the session-level child model stamp, the structured_finalizer user metadata, and the StructuredOutput synthesis for finalizer prompts are no longer written — the durable task_run row is authoritative and the core finalizer rides the schema in the prompt text. Assert the same facts through the surviving durable surfaces: the task call returns the schema-validated ResearchResult (parsed from the task output), the child identity rides its assistant messages, and the finalizer prompt is observed by its instruction text with the final answer parsed as the ResearchResult JSON. Also drop the unused nested-record helpers.
…e + align subagent-foreground assertions
…onRuntimeHost seam The keyed Location tree self-provided ContextQueryAuthorization.defaultLayer, but Layer.fresh gives every tree a private memo map — so the runner's admitSelection binds landed in a tree-local store while the host-captured CodeIntelFacade / ContextQueryFacade instances resolved from the app-root store. In production the split meant code_intel / context_query could never see a bound envelope and always answered authorization_unavailable once the ContextToolRuntime seam was available. Move the store to the LocationRuntimeHost seam (like ProductionV2Sources and ContextToolRuntime): the core default host keeps a per-tree store (bare-core behavior unchanged), and the production runnerFrameHost captures the ONE memoized ContextQueryAuthorization.defaultLayer build that also feeds the facades' internal requirements, handing it to every keyed tree.
…ery authority
The live harness host provided ProductionV2Sources as a bare {} seam, so the
selection admission built authorization envelopes on the v2:local degradation
frame while the code index answers with the real instance identity — code_intel
returned scope_denied even with the authority store shared. Mirror production's
runnerFrameSeamFor: the empty base stays (the harness drives no production
context adapters) but the per-ref REAL instance identity is attached, and the
shared query-authority store is captured with the facades exactly like
runnerFrameHost does.
… the LocationRuntimeHost seam + harness production identity
…adds Four late campaign worktrees swept their isolated live-test homes (.tmp/, 3020 knowledge-doc files) into their branches; the merges carried them into the tree. Untrack and ignore .tmp/ at any level.
|
Hey! Your PR title Please update it to start with one of:
Where See CONTRIBUTING.md for details. |
|
This PR doesn't fully meet our contributing guidelines and PR template. What needs to be fixed:
Please edit this PR description to address the above within 2 hours, or it will be automatically closed. If you believe this was flagged incorrectly, please let a maintainer know. |
Keep only the newest snapshot (20260922182457) as the drizzle-kit generation baseline; intermediate snapshots from this PR's earlier migrations add ~37k review lines with no consumer (apply path reads migration.sql via the registry; generation diffs against the newest snapshot only). Follows the release-PR slimming precedent.
deepagent-ai
added a commit
that referenced
this pull request
Sep 23, 2026
lxrzlyr
pushed a commit
that referenced
this pull request
Oct 13, 2026
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.
Issue for this PR
Closes #
Type of change
What does this PR do?
Please provide a description of the issue, the changes you made to fix it, and why they work. It is expected that you understand why your changes work and if you do not understand why at least say as much so a maintainer knows how much to value the PR.
If you paste a large clearly AI generated description here your PR may be IGNORED or CLOSED!
How did you verify your code works?
Screenshots / recordings
If this is a UI change, please include a screenshot or recording.
Checklist
If you do not follow this template your PR will be automatically rejected.