Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
53d48c0
fix(session): converge durable activity progress
deepagent-ai Aug 12, 2026
1a9e440
fix(app): preserve activity projection on stale pages
deepagent-ai Aug 12, 2026
d42a38c
fix(app): bound activity projection refetch
deepagent-ai Aug 12, 2026
f4b00c3
fix(app): reconcile authoritative activity refresh
deepagent-ai Aug 12, 2026
146bfaa
fix(app): preserve message deltas during refresh
deepagent-ai Aug 12, 2026
013c1c3
fix(session): make legacy activity lifecycle durable
deepagent-ai Aug 12, 2026
87b383d
fix(session): recover crash-ambiguous sessions safely
deepagent-ai Aug 12, 2026
084bf20
fix(sync): bound replay requests before decoding
deepagent-ai Aug 12, 2026
e6596d1
test(session): cover rejected question lifecycle
deepagent-ai Aug 12, 2026
0170c0f
test(desktop): gate rejected question through packaged UI
deepagent-ai Aug 12, 2026
db480e9
fix(session): recover production crash histories
deepagent-ai Aug 12, 2026
4c31f5f
fix(core): preserve cross-table triggers during recovery migration
deepagent-ai Aug 12, 2026
0f9846a
fix(sync): enforce final replay request budget
deepagent-ai Aug 12, 2026
1a9467c
fix(session): filter legacy recovery authorities
deepagent-ai Aug 12, 2026
f4a313c
fix(session): acknowledge implicit prompt admission
deepagent-ai Aug 12, 2026
d990d9e
fix(control-plane): disable unsafe session transfers
deepagent-ai Aug 12, 2026
9e07804
chore(sdk): regenerate transfer conflict types
deepagent-ai Aug 12, 2026
5c81417
fix(session): close activity crash restart matrix
deepagent-ai Aug 12, 2026
537350b
fix(session): close provider recovery and sync gates
deepagent-ai Aug 13, 2026
5afb707
fix(core): bound legacy event artifact backfill
deepagent-ai Aug 13, 2026
9b8285a
fix(core): stop scoped install retry timers
deepagent-ai Aug 13, 2026
432e61e
fix(core): complete bounded event maintenance
deepagent-ai Aug 13, 2026
e323f19
fix(core): preserve synthetic file artifact metadata
deepagent-ai Aug 13, 2026
1298431
fix(session): migrate metadata-only diff artifacts
deepagent-ai Aug 13, 2026
d93ab4c
fix(session): select authoritative diff artifacts
deepagent-ai Aug 13, 2026
b270120
fix: close bounded artifact release gates
deepagent-ai Aug 13, 2026
8c0ae19
fix: compact file artifact source events
deepagent-ai Aug 13, 2026
05a12dc
fix(core): bound event compaction memory
deepagent-ai Aug 13, 2026
d2990de
fix(core): close bounded event maintenance gates
deepagent-ai Aug 13, 2026
ff6e68d
fix(deepagent-code): validate all sessions in maintenance gate
deepagent-ai Aug 13, 2026
3c5d11a
feat(core): add durable authority schemas
deepagent-ai Aug 13, 2026
8ce6495
feat(core): make released knowledge authoritative
deepagent-ai Aug 13, 2026
dbd7bb3
feat(core): add durable learning authority
deepagent-ai Aug 13, 2026
897a3fb
feat(core): make activity effects durable
deepagent-ai Aug 13, 2026
e1992b4
feat(core): cut over durable provider turns
deepagent-ai Aug 13, 2026
675ae0b
fix(deepagent-code): persist structured task results
deepagent-ai Aug 13, 2026
8a5b999
fix(deepagent-code): wire durable runtime authorities
deepagent-ai Aug 13, 2026
1d5c78c
test(deepagent-code): assert immutable wiki revisions
deepagent-ai Aug 13, 2026
55aa871
chore(sdk): regenerate durable authority clients
deepagent-ai Aug 13, 2026
53cbc3b
fix(core): merge durable authority migration histories
deepagent-ai Aug 13, 2026
3b61836
fix(core): harden V2 runtime and migration gates
deepagent-ai Aug 14, 2026
6e79cce
chore: prepare desktop 1.4.6 release
deepagent-ai Aug 14, 2026
74e91a3
fix(core): keep durable learning node-compatible
deepagent-ai Aug 14, 2026
3cc420f
chore(core): consolidate unpublished migration snapshots
deepagent-ai Aug 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 3 additions & 3 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@deepagent-code/app",
"version": "1.4.5",
"version": "1.4.6",
"description": "",
"type": "module",
"exports": {
Expand Down
66 changes: 58 additions & 8 deletions packages/app/src/components/review/dialog-review-contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { listPending, reviewSummary, setStatus, listEnvFacts, decideEnvFact, mod
// P1-C route contract: the V3.1 self-learning Review dialog talks to the raw-request escape-hatch
// routes (NOT the generated SDK). These assertions lock the exact method/url/body so a backend
// rename of /deepagent/knowledge/{pending,approve,reject-ids} or a payload shape change breaks CI
// here instead of silently shipping a dead Review UI. Mirrors the backend group schema
// (DeepAgentKnowledgeStatusInput = { ids: string[] }).
// here instead of silently shipping a dead Review UI. Review decisions must round-trip the exact
// immutable authority returned by the list endpoint; a bare id can alias project/global revisions.
type Recorded = { method: string; url: string; body?: unknown; headers?: Record<string, string> }

function client(calls: Recorded[], data: unknown) {
Expand All @@ -24,7 +24,13 @@ describe("DeepAgent review dialog route contract", () => {
const calls: Recorded[] = []
const items = [
{
sourceStore: "project" as const,
id: "memory:1",
version: 3,
hash: "hash-3",
candidateId: "candidate-3",
fingerprint: "fingerprint-3",
governanceRevision: "governance-3",
type: "memory" as const,
summary: "s",
evidence_strength: "strong" as const,
Expand All @@ -49,29 +55,73 @@ describe("DeepAgent review dialog route contract", () => {
expect(calls).toEqual([{ method: "GET", url: "/deepagent/knowledge/review-summary" }])
})

test("approve POSTs /deepagent/knowledge/approve with { ids }", async () => {
test("approve POSTs the exact listed authority to /deepagent/knowledge/approve", async () => {
const calls: Recorded[] = []
await setStatus(client(calls, { updated: ["a"] }), "approve", ["a", "b"])
const item = {
sourceStore: "project" as const,
id: "a",
version: 2,
hash: "hash-a-2",
candidateId: "candidate-a",
fingerprint: "fingerprint-a",
governanceRevision: "governance-a-2",
type: "knowledge" as const,
summary: "A",
evidence_strength: "strong" as const,
evidence_refs: [],
approval_status: "pending" as const,
}
await setStatus(client(calls, { updated: item }), "approve", item)

expect(calls).toEqual([
{
method: "POST",
url: "/deepagent/knowledge/approve",
body: { ids: ["a", "b"] },
body: {
sourceStore: "project",
id: "a",
version: 2,
hash: "hash-a-2",
candidateId: "candidate-a",
fingerprint: "fingerprint-a",
expectedGovernanceRevision: "governance-a-2",
},
headers: { "Content-Type": "application/json" },
},
])
})

test("reject POSTs /deepagent/knowledge/reject-ids with { ids }", async () => {
test("reject POSTs the exact global authority to /deepagent/knowledge/reject-ids", async () => {
const calls: Recorded[] = []
await setStatus(client(calls, { updated: ["a"] }), "reject-ids", ["a"])
const item = {
sourceStore: "user_global" as const,
id: "a",
version: 4,
hash: "hash-a-4",
candidateId: "candidate-a",
fingerprint: "fingerprint-a",
governanceRevision: "governance-a-4",
type: "memory" as const,
summary: "A",
evidence_strength: "medium" as const,
evidence_refs: [],
approval_status: "pending" as const,
}
await setStatus(client(calls, { updated: item }), "reject-ids", item)

expect(calls).toEqual([
{
method: "POST",
url: "/deepagent/knowledge/reject-ids",
body: { ids: ["a"] },
body: {
sourceStore: "user_global",
id: "a",
version: 4,
hash: "hash-a-4",
candidateId: "candidate-a",
fingerprint: "fingerprint-a",
expectedGovernanceRevision: "governance-a-4",
},
headers: { "Content-Type": "application/json" },
},
])
Expand Down
31 changes: 28 additions & 3 deletions packages/app/src/components/review/dialog-review.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@
// for back-compat. Keep this file free of any solid-js/UI imports.

export type KnowledgeItem = {
sourceStore: "user_global" | "project"
id: string
version: number
hash: string
candidateId: string
fingerprint: string
governanceRevision: string
type: "knowledge" | "strategy" | "methodology" | "memory" | "skill" | "failure_dossier"
summary: string
evidence_strength: "strong" | "medium" | "weak" | "none"
Expand Down Expand Up @@ -47,16 +53,35 @@ export const reviewSummary = async (client: ReviewClient): Promise<{ pendingCoun
export const setStatus = async (
client: ReviewClient,
action: "approve" | "reject-ids",
ids: string[],
item: KnowledgeItem,
): Promise<void> => {
await client.client.request<{ updated: string[] }>({
await client.client.request<{ updated: KnowledgeItem }>({
method: "POST",
url: `/deepagent/knowledge/${action}`,
body: { ids },
body: {
sourceStore: item.sourceStore,
id: item.id,
version: item.version,
hash: item.hash,
candidateId: item.candidateId,
fingerprint: item.fingerprint,
expectedGovernanceRevision: item.governanceRevision,
},
headers: { "Content-Type": "application/json" },
})
}

export const reviewAuthorityKey = (item: KnowledgeItem) =>
JSON.stringify([
item.sourceStore,
item.id,
item.version,
item.hash,
item.candidateId,
item.fingerprint,
item.governanceRevision,
])

// V3.8.1 §G environment-fact use-gate. Provisional user-global environment facts surface here so the
// user decides, per project, whether to adopt them (§G.5). Credentials never appear — only secret_ref
// pointers. `degraded` marks a fact whose last connection attempt failed (§G.6).
Expand Down
25 changes: 16 additions & 9 deletions packages/app/src/components/review/dialog-review.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
listEnvFacts,
decideEnvFact,
modifyEnvFact,
reviewAuthorityKey,
type KnowledgeItem,
type ReviewClient,
type EnvFactItem,
Expand All @@ -25,6 +26,7 @@ export {
listEnvFacts,
decideEnvFact,
modifyEnvFact,
reviewAuthorityKey,
type KnowledgeItem,
type ReviewClient,
type EnvFactBody,
Expand Down Expand Up @@ -187,33 +189,36 @@ export const DialogReview: Component<{ client: ReviewClient }> = (props) => {
setCollapsed(next)
}

const toggle = (id: string) => {
const toggle = (item: KnowledgeItem) => {
const id = reviewAuthorityKey(item)
const next = new Set<string>(selected())
if (next.has(id)) next.delete(id)
else next.add(id)
setSelected(next)
}
const selectAll = () => setSelected(new Set(pending().map((i) => i.id)))
const selectAll = () => setSelected(new Set(pending().map(reviewAuthorityKey)))
const invert = () => {
const cur = selected()
setSelected(
new Set(
pending()
.map((i) => i.id)
.map(reviewAuthorityKey)
.filter((id) => !cur.has(id)),
),
)
}

const apply = async (action: "approve" | "reject-ids") => {
const ids = [...selected()]
if (ids.length === 0 || busy()) return
const decisions = (items() ?? []).filter((item) => selected().has(reviewAuthorityKey(item)))
if (decisions.length === 0 || busy()) return
setBusy(true)
try {
await setStatus(props.client, action, ids)
await Promise.all(decisions.map((item) => setStatus(props.client, action, item)))
setSelected(new Set<string>())
await refetch()
} catch (error) {
setSelected(new Set<string>())
await refetch()
showToast({
variant: "error",
title: language.t("review.title"),
Expand All @@ -225,14 +230,14 @@ export const DialogReview: Component<{ client: ReviewClient }> = (props) => {
}

const Row = (item: KnowledgeItem) => {
const checked = createMemo(() => selected().has(item.id))
const checked = createMemo(() => selected().has(reviewAuthorityKey(item)))
return (
<label
data-action="review-item"
data-status={item.approval_status}
class="flex cursor-pointer items-start gap-3 border-b border-v2-border-border-muted px-3 py-2.5 last:border-b-0 hover:bg-v2-background-bg-layer-01"
>
<input type="checkbox" class="mt-0.5" checked={checked()} onChange={() => toggle(item.id)} />
<input type="checkbox" class="mt-0.5" checked={checked()} onChange={() => toggle(item)} />
<div class="flex min-w-0 flex-1 flex-col gap-0.5">
<span class="break-words text-13-medium text-v2-text-text-base">{item.summary}</span>
<span class="break-words text-11-regular text-v2-text-text-faint">
Expand Down Expand Up @@ -393,7 +398,9 @@ export const DialogReview: Component<{ client: ReviewClient }> = (props) => {
</label>
</div>
<span class="text-11-regular text-v2-text-text-faint">
{language.t(d.mode === "global" ? "review.envFacts.scope.globalHint" : "review.envFacts.scope.projectHint")}
{language.t(
d.mode === "global" ? "review.envFacts.scope.globalHint" : "review.envFacts.scope.projectHint",
)}
</span>
</div>
<div class="flex items-center justify-end gap-2">
Expand Down
Loading
Loading