Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
188 commits
Select commit Hold shift + click to select a range
4efd80d
chore(core): remove zero-reference deps and redirect_links domain (#5…
YellowSnnowmann Jul 21, 2026
bea380d
feat(bench): embedded-RSS benchmark harness + report-only CI (#5046) …
YellowSnnowmann Jul 21, 2026
4019a9a
feat(core): gate the desktop-automation cluster behind a default-ON f…
YellowSnnowmann Jul 21, 2026
fc0222f
perf(agent): share one Arc<Config> across per-build tool/provider/ref…
YellowSnnowmann Jul 21, 2026
9d14d91
chore(release): merge release v0.63.0 back into main
github-actions[bot] Jul 21, 2026
846db8a
feat(flows): render Workflow Copilot chat via the shared composer tra…
senamakel Jul 21, 2026
f92888a
feat(flows): add "Save & enable" to the Flow Canvas copilot proposal …
graycyrus Jul 21, 2026
7ec16e8
fix(agentbox): normalize GMI_MAAS_BASE_URL with trailing /v1 (#5091)
Horst1993 Jul 21, 2026
78949de
fix(conversations): stop the thread-goal footer text from auto-scroll…
jgentes Jul 21, 2026
86e1a4c
fix(approval): preserve replacement routes during cleanup (#4786)
samrusani Jul 21, 2026
e2f9014
fix(core): seed tool-execution timeout on the always-on boot path (#5…
oxoxDev Jul 21, 2026
dfa5738
fix(keyring): declare msg mutable for cfg(windows) push_str (E0596) (…
myi1 Jul 21, 2026
4b2d92c
fix(api): classify Atlas Cloud as inference provider (#4885)
binyangzhu000-sudo Jul 21, 2026
c309be8
fix(flows): show plain-language step labels in the workflow proposal …
graycyrus Jul 21, 2026
40852cd
feat(flows): let the copilot live test-run flows behind the approval …
graycyrus Jul 21, 2026
81f58ee
test(ci): exercise full validation suite (#5083)
senamakel Jul 22, 2026
1fb1538
fix(flows): live-refresh the runs rail when a run starts (B35) (#5099)
graycyrus Jul 22, 2026
31fca02
fix(inference): keep Claude CLI prompts out of Windows argv (#5103)
samrusani Jul 22, 2026
ddeee5b
feat(core): shed embedded deps behind inference/documents/crash-repor…
YellowSnnowmann Jul 22, 2026
06be00c
fix(tinyplace): wire handle transfer end-to-end (Closes #4929) (#4998)
M3gA-Mind Jul 22, 2026
f18414f
fix(voice): handle U16 sample format in the fallback capture path (#4…
mysma-9403 Jul 22, 2026
c5fea23
fix(mcp): hide raw registry errors (#4716)
samrusani Jul 22, 2026
63e4fe2
feat(security): opt-in auto-approve-all setting that bypasses the app…
graycyrus Jul 22, 2026
5fcde25
feat(dev): library-mode benchmark environment, fleet/budget gates, an…
senamakel Jul 22, 2026
0eeee12
feat(core): channels compile-time feature gate (#4801) — completes ep…
oxoxDev Jul 22, 2026
002530d
feat(flows): link to the saved workflow from the chat proposal card (…
graycyrus Jul 22, 2026
58494e1
fix(flows): shorter approval TTL for copilot live-run parks (#5112)
graycyrus Jul 22, 2026
9b72616
feat(flows): FlowRunFinished event — fully event-driven runs rail (co…
graycyrus Jul 22, 2026
41bad05
feat(flows): teach the workflow builder to pick specialist agents via…
graycyrus Jul 22, 2026
9e312b6
chore(deps): update vendored runtime crates (#5081)
senamakel Jul 22, 2026
353e07c
fix(embeddings): classify 403 'not an embeddings model' as model-inco…
M3gA-Mind Jul 22, 2026
f55376b
fix(react-ui): surface layered pipeline status in Data Sync (GH-4690)…
M3gA-Mind Jul 22, 2026
81ac128
fix(flows): reject an unknown agent_ref at author time instead of mid…
graycyrus Jul 22, 2026
5b8a9f2
fix(composio): drop non-existent Discord channel/message slugs from c…
YellowSnnowmann Jul 22, 2026
a8d13fa
fix(flows): repair main compile break from crossed PRs (#5128)
senamakel Jul 22, 2026
9bb59ca
feat(medulla_local): supervise a local medulla-serve child (Flavor A …
senamakel Jul 22, 2026
60a7618
feat(settings): add dismissible in-app GitHub star CTA (#5005) (#5110)
M3gA-Mind Jul 22, 2026
fb1f909
chore(release): merge release v0.63.1 back into main
github-actions[bot] Jul 22, 2026
c480e6d
fix(agents): run custom registry agents with their real tools (flows …
graycyrus Jul 22, 2026
2bcd344
refactor(memory): finish TinyCortex consolidation (#5140)
senamakel Jul 23, 2026
81d09b6
feat(tui): add logs-first tabbed CLI experience (#5131)
senamakel Jul 23, 2026
5041454
docs: Add CITATION.cff for software citation metadata (#5142)
mwakidenis Jul 23, 2026
08f28de
feat(profiles): hermes-style agent profile homes — per-profile SOUL.m…
senamakel Jul 23, 2026
87fe420
feat(runtime): pool node/python runtime processes across agents and s…
YellowSnnowmann Jul 23, 2026
49b5bb7
feat(conversations): unify agent activity into one live rail (#5141)
sanil-23 Jul 23, 2026
1c30d3f
chore(tinyagents): align v2.1 and reconcile migration plan (#5130)
senamakel Jul 23, 2026
e9686e4
fix(agents): fail clearly instead of silently dropping spawn_async_su…
graycyrus Jul 23, 2026
1fbd05f
fix(flows): live-run reliability — reconcile orphaned/cancelled runs …
graycyrus Jul 23, 2026
6459790
docs(contributing): fix Windows desktop setup (#5145)
NgoQuocViet2001 Jul 23, 2026
5199757
docs(install): fix macOS Homebrew install to use official cask (#5152)
senamakel Jul 23, 2026
21ec097
fix(approval): redact non-canonically-cased home paths in scrub_paths…
mysma-9403 Jul 23, 2026
d465096
fix(cron): stop one-shot At jobs from re-firing every poll (#5039)
mysma-9403 Jul 23, 2026
f0d21a2
perf(orchestration): batch the per-session unread_count into one quer…
mysma-9403 Jul 23, 2026
83d1dc3
feat(learning): surface facet provenance (cue_families, evidence_refs…
mysma-9403 Jul 23, 2026
c8ecb85
fix(flows): extract embedded JSON from prose text, preventing output_…
senamakel Jul 23, 2026
69196b4
fix(composio): add global auto-proceed safety net for re-delegated su…
senamakel Jul 23, 2026
e780277
refactor(runtime): un-bundle non-channel bootstrap jobs from ServiceS…
oxoxDev Jul 23, 2026
2525400
chore(core): slim the core — remove dead modules, move desktop featur…
senamakel Jul 24, 2026
d70e206
chore(fmt): rustfmt caps.rs + provider.rs to unblock the CI pipeline …
graycyrus Jul 24, 2026
dbf2d35
refactor(core): make spaCy opt-in + demand-gate the Python runtime (#…
graycyrus Jul 24, 2026
c9f2582
refactor(app): remove dead screen-intelligence UI remnants (#5056) (#…
graycyrus Jul 24, 2026
7aca0ea
refactor(app): remove orphaned frontend modules (#5056) (#5095)
graycyrus Jul 24, 2026
ef38f1c
fix(flows): stop the builder authoring memory steps that fabricate (#…
graycyrus Jul 24, 2026
7db104b
refactor(tinyagents): complete provider cutover and lifecycle consoli…
senamakel Jul 24, 2026
8261f34
ci: lint macOS-gated Rust in CI Full (#5181)
senamakel Jul 24, 2026
75e212c
fix(flows): make native `oh:` tool results usable and fail loudly, do…
graycyrus Jul 24, 2026
55d3ab5
fix: Sentry triage — resolve 16 actionable issues across tauri-react,…
senamakel Jul 24, 2026
2d6064c
feat(memory): expose compiled flavour profiles via a read-only memory…
graycyrus Jul 24, 2026
b9e0ce6
feat(flows): per-flow memory namespace + post-run digest (flow:<id>) …
graycyrus Jul 24, 2026
ca0e65f
chore(app): remove unused files and code with Knip (#5185)
senamakel Jul 25, 2026
79e2fc2
fix(appimage): anchor sharun library paths to AppDir (#5189)
senamakel Jul 25, 2026
1094472
refactor(ui): establish reusable React foundations (#5188)
senamakel Jul 25, 2026
9f22ae4
fix(config): heal and diagnose an unreadable config.toml instead of d…
YellowSnnowmann Jul 25, 2026
ee1fa76
refactor: remove screen awareness and capture surfaces (#5186)
senamakel Jul 25, 2026
fd04d37
chore(release): merge release v0.63.2 back into main
github-actions[bot] Jul 27, 2026
b5ba021
chore(release): merge release v0.63.3 back into main
github-actions[bot] Jul 27, 2026
0dd1396
fix(chat): wrap long URLs in message bubbles so they don't overflow (…
graycyrus Jul 27, 2026
8e73d69
fix(agent-experience): make experience recall test deterministic (#52…
graycyrus Jul 27, 2026
d34dfc3
feat(flows): general read-only flow memory-agent (#5204) (#5205)
graycyrus Jul 27, 2026
b3fb23b
fix(agentworld): frame Tiny Place World as local sim; document relay …
CodeGhost21 Jul 27, 2026
7e8c088
feat(search): attribute managed web search to the resolved provider (…
M3gA-Mind Jul 27, 2026
9a484aa
feat(search): add Exa as a BYOK search provider option (#5207)
M3gA-Mind Jul 27, 2026
5564423
chore(staging): v0.63.4 [skip ci]
github-actions[bot] Jul 27, 2026
3228970
chore(staging): v0.63.5 [skip ci]
github-actions[bot] Jul 27, 2026
35f3b1c
feat(tinyplace): resolve identity marketplace seller-side as web-only…
CodeGhost21 Jul 27, 2026
44783ce
docs(search): document Exa as the managed search provider and BYOK op…
M3gA-Mind Jul 27, 2026
fc2279f
fix(flows): warn (not block) on inference-provider readiness while au…
graycyrus Jul 27, 2026
9d328fb
feat(orchestration): node-level checkpoint & resume for delegations (…
mysma-9403 Jul 27, 2026
6b33376
fix(voice): repair execute bit on extracted Piper binaries (#5045) (#…
M3gA-Mind Jul 27, 2026
ae9124e
feat(agent): filesystem-offload convention for worker artifacts on lo…
M3gA-Mind Jul 27, 2026
0639478
fix(cost): exempt BYOK inference from the managed-credit budget (#5218)
M3gA-Mind Jul 27, 2026
cf71f55
fix(inference): local model reliability — /api/tags fallback + real O…
M3gA-Mind Jul 27, 2026
dcc5b9b
fix(tauri): clamp window to one monitor on mixed-DPI multi-monitor (#…
M3gA-Mind Jul 27, 2026
af48970
feat(medulla): OpenHuman as the Medulla core — embed facade, DomainGr…
senamakel Jul 28, 2026
cad9623
fix(mcp): add the missing flow_memory_agent resource-catalog entry (#…
senamakel Jul 28, 2026
46ee9a0
refactor(memory): finish TinyCortex tool-memory cutover (#5237)
senamakel Jul 28, 2026
4ed193b
refactor(tui): move terminal UI to crate root (#5236)
senamakel Jul 28, 2026
149137a
refactor(goals): consolidate thread goals under tinyagents (#5235)
senamakel Jul 28, 2026
2d32656
refactor(todos): cut over task boards to TinyAgents (#5239)
senamakel Jul 28, 2026
fe33726
refactor(core): cut managed backend traffic over to Rust SDK (#5233)
senamakel Jul 28, 2026
7620945
feat(embed): Medulla sub-facade and the RPC surface to drive a sessio…
senamakel Jul 28, 2026
0004869
refactor(memory): shim content tag storage through TinyCortex (#5244)
senamakel Jul 28, 2026
ec6885c
test(memory): pin crate KV tier in golden-workspace parity harness (#…
CodeGhost21 Jul 28, 2026
861b21c
refactor(inference): route local AI through TinyAgents RPC (#5240)
senamakel Jul 28, 2026
cfbfe3d
fix(ci): stabilize full Rust and chat E2E suites (#5241)
senamakel Jul 28, 2026
539c444
Trim Rust dependency graph and duplicate TLS stacks (#5202)
senamakel Jul 28, 2026
5758e4e
feat(flows): add a Stop button to the Workflow Copilot composer (#5223)
graycyrus Jul 28, 2026
61fb2d2
refactor(api): vendor tinyhumans-sdk and route the backend client thr…
senamakel Jul 28, 2026
4ce2a8c
refactor(memory): retire legacy sync compatibility paths (#5246)
senamakel Jul 28, 2026
9750b80
fix(appimage): unblock release smoke by relaxing the userns sysctl (m…
M3gA-Mind Jul 28, 2026
11d744a
fix(appimage): give the release startup smoke a D-Bus session bus (#5…
M3gA-Mind Jul 28, 2026
447db97
fix(medulla): resolve the backend URL like every other hosted call (#…
senamakel Jul 29, 2026
6c665f4
feat(flows): host adapter for the `memory` node — in-graph memory rea…
graycyrus Jul 29, 2026
39fe7db
feat(flows): frontend for the `memory` node — palette, config panel, …
graycyrus Jul 29, 2026
4811ea4
fix(inference): route vision at vision-capable models and refresh loc…
M3gA-Mind Jul 29, 2026
73d3439
fix(inference): explain background-role provider fallback and verify …
M3gA-Mind Jul 29, 2026
8072f08
fix(ai-settings): allow free-text Azure deployment names (#5213) (#5222)
M3gA-Mind Jul 29, 2026
e213bc5
chore(vendor): bump tinyhumans-sdk to the typed medulla namespace (#5…
senamakel Jul 29, 2026
d125b85
feat(flows): consolidated save+enable pre-authorization card (Approve…
graycyrus Jul 29, 2026
713751f
feat(flows): frontend for the `dedup` node — palette, key config, i18…
graycyrus Jul 29, 2026
0f66d38
fix(tests): serialize backend-env tests behind one lock (fixes flaky …
graycyrus Jul 29, 2026
dd491ca
feat(flows): host `DedupCommitSubscriber` for the exactly-once dedup …
graycyrus Jul 29, 2026
bb83836
feat(medulla): serve the workflow plane to the hosted orchestrator (#…
senamakel Jul 30, 2026
4ecc212
feat(tinyplace): resolve feed post media as web-only (#4924) (#5295)
CodeGhost21 Jul 30, 2026
6a21f3f
fix(flows): give `flows_resume` the run-lifecycle safety `flows_run` …
graycyrus Jul 31, 2026
8144a54
fix(rhai): restore the documented timeout ordering and honour a reuse…
graycyrus Jul 31, 2026
7d7fb48
docs(flows): fix contract-drift comments and repair the workflow-buil…
graycyrus Jul 31, 2026
d630f15
fix(flows): close residual Rust minors from the flows review (#5297)
graycyrus Jul 31, 2026
46e9010
fix(flows): store resilience — skip corrupt rows, transactional step …
graycyrus Jul 31, 2026
6c0381a
fix(flows): close two authorization boundaries in flow-run tools (#5287)
graycyrus Jul 31, 2026
ecb22ce
chore(staging): v0.63.6 [skip ci]
github-actions[bot] Jul 31, 2026
77a2f74
fix(flows): translate n8n `$json` bindings to the real engine scope r…
graycyrus Jul 31, 2026
4884e9a
fix(flows): expose flows_run_detached over RPC and switch both UI Run…
graycyrus Jul 31, 2026
89818fe
fix(flows): pin a parked run to the graph it was approved against (#5…
graycyrus Jul 31, 2026
fe08b05
fix(flows): localize node-summary and cron descriptions (i18n ×14) (#…
graycyrus Jul 31, 2026
54ca06d
fix(flows): frontend residual minors — stale route, edge id collision…
graycyrus Jul 31, 2026
0bd2284
feat(flows): canvas visual refresh — kind tiles, elevation, step numb…
graycyrus Jul 31, 2026
1846dd6
feat(flows): per-item fan-out — run an array of flow work concurrentl…
senamakel Jul 31, 2026
068ae45
feat(flows): accept declared workflow inputs on every run path (#5304)
senamakel Jul 31, 2026
1f79cc4
fix(orchestration): remove dead hosted-steering poll (TAURI-RUST-PNK)…
sanil-23 Jul 31, 2026
0294a8e
fix(theme): cancel mesh-gradient timers on teardown (#5160) (#5273)
M3gA-Mind Jul 31, 2026
c096ecb
fix(chat): stop the composer-footer render loop (#5162) (#5274)
M3gA-Mind Jul 31, 2026
00452ba
fix(memory): canonicalize memory identifiers symmetrically (#5164) (#…
M3gA-Mind Jul 31, 2026
eb2fa8c
fix(harness-init): bound the status poll loop instead of retrying for…
M3gA-Mind Jul 31, 2026
cfc2d36
fix(ipc): recover the CEF postMessage fallback instead of dereferenci…
M3gA-Mind Jul 31, 2026
19efd7a
fix(channels): read checkbox state before the setState updater runs (…
M3gA-Mind Jul 31, 2026
3e2a81c
fix(memory): stop paging on caller ingest-payload schema errors (#516…
M3gA-Mind Jul 31, 2026
5c73a9f
fix(threads): keep the conversation store off the async workers (#515…
M3gA-Mind Jul 31, 2026
0c2b9c4
fix(channels): stop a missing edit route from looking like a deleted …
M3gA-Mind Jul 31, 2026
43cc1b4
fix(inference): error on a misconfigured vision model instead of subs…
M3gA-Mind Jul 31, 2026
a52a599
feat: kernelize openhuman — cut the workflow dependency floor 418→280…
senamakel Aug 2, 2026
67a9b4c
feat(agent-world): Tiny Place directory search + pagination (#4776 §6…
CodeGhost21 Aug 2, 2026
baaa63b
feat(agent-world): Tiny Place ledger filters + copy tx-id (#4776 §4) …
CodeGhost21 Aug 2, 2026
7a87e9d
fix(chat): name the chat-template rejection instead of blaming model/…
M3gA-Mind Aug 2, 2026
7fb3bbc
fix(context-scout): stop out-of-credits background scouts paging Sent…
M3gA-Mind Aug 2, 2026
a40ba85
fix(auth): keep the /auth/me 401 classifiable as session expiry (#530…
M3gA-Mind Aug 2, 2026
b668251
refactor(kernel): collapse 124 flat domains into 31 gate-aligned fami…
senamakel Aug 3, 2026
2e98f39
fix(json-schema): gate flow-only helper exports (#5335)
senamakel Aug 3, 2026
20589ab
feat(kernel): realign DomainGroup with the family directories (#5332)
senamakel Aug 3, 2026
45328dc
fix(auth): bound store-time /auth/me validation so a slow backend def…
YellowSnnowmann Aug 3, 2026
caa6d92
fix(config): stop transient active_user.toml read faults from resetti…
YellowSnnowmann Aug 3, 2026
210cd6c
test(mcp-registry): regression test for graceful empty-catalog on tot…
YellowSnnowmann Aug 3, 2026
5efb24a
feat(deep-link): confirm the tokenmaxxxing download when the app open…
M3gA-Mind Aug 3, 2026
f65dc29
fix(kernel): guard the three unenforced DomainGroup consumers (#5364)
senamakel Aug 4, 2026
00a0cf1
fix(config): surface a user notice when a corrupt config.toml is rese…
YellowSnnowmann Aug 4, 2026
a64f0de
fix(deps): bump tinyhumans-sdk — unblock file uploads (multipart cont…
graycyrus Aug 4, 2026
fb2bd8e
Don't discard a valid cloud key when the add-time model probe fails t…
YellowSnnowmann Aug 4, 2026
74fd17a
fix(embeddings): read managed session token from config-scoped store …
YellowSnnowmann Aug 4, 2026
7af8b0e
feat(kernel): gate the macOS Contacts cohort behind `contacts` (#5365)
senamakel Aug 4, 2026
202b313
feat(flows): surface bounded loops from the tinyflows engine (#5366)
senamakel Aug 4, 2026
d75b0a4
fix(human): stop mascot lipsync re-renders from locking tab navigatio…
YellowSnnowmann Aug 4, 2026
4dfa6f2
feat(kernel): host capability adapters, config seam, and the Phase 2 …
senamakel Aug 5, 2026
d9d03af
fix(kernel): resolve team/delegate model pins through the config acce…
senamakel Aug 5, 2026
b3cda1b
fix(human): stream TTS sentence-by-sentence to remove the voice delay…
YellowSnnowmann Aug 5, 2026
ccad8f5
fix(memory): classify Ollama-unavailable embed failures actionably (#…
YellowSnnowmann Aug 5, 2026
5dc2f35
fix(memory-sources): drain coding-session backlog across bounded pass…
YellowSnnowmann Aug 5, 2026
0682302
fix(agent): preserve chat context across a Quick/Reasoning switch (#5…
YellowSnnowmann Aug 5, 2026
542edac
fix(voice): stop the Piper voice id leaking into non-Piper TTS provid…
YellowSnnowmann Aug 5, 2026
934c21a
fix(agent): send user image attachments as image blocks, not text (#5…
YellowSnnowmann Aug 5, 2026
41f455c
fix(inference): route Codex OAuth chat to the subscription backend (#…
YellowSnnowmann Aug 5, 2026
88895a2
feat(memory): warn before the embedding budget silently stops memory …
YellowSnnowmann Aug 5, 2026
68679a0
feat(voice): ElevenLabs Voice Agents — core RPC + realtime session + …
YellowSnnowmann Aug 5, 2026
1847e69
fix(media): error instead of false success on the generation poll tim…
YellowSnnowmann Aug 5, 2026
e29bfc6
feat(threads): name a thread in three words (#5412)
senamakel Aug 5, 2026
d781949
feat(mascot): accept PNG/GIF/JPEG/WebP custom avatars with file uploa…
YellowSnnowmann Aug 6, 2026
bb4493c
fix(voice): send the relay identity token where the backend can read …
YellowSnnowmann Aug 6, 2026
8deb5f2
fix(memory): stop a signed-in user's embeddings failing, and unstick …
YellowSnnowmann Aug 6, 2026
6c3d648
Promote main -> release (conflict-resolved)
M3gA-Mind Aug 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions app/src/components/intelligence/MemoryTreeStatusPanel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,22 @@ import {
const mockPipelineStatus = vi.fn();
const mockSetEnabled = vi.fn();
const mockSyncStatusList = vi.fn();
const mockRetryFailed = vi.fn();
// #5324: the panel now navigates (budget CTA) and dispatches (escalating the
// blocking cause to the shell-mounted UserErrorCenter). Stub both so the
// suite keeps rendering the panel bare, without a Router or a Redux store.
const mockNavigate = vi.fn();
const mockDispatch = vi.fn();
// Analytics is a consent-gated side effect that reaches into the core-state
// snapshot; stub it so the panel renders bare and the retry-success path can be
// asserted without a real analytics pipeline.
const mockTrackAnalyticsEvent = vi.fn();

vi.mock('react-router-dom', () => ({ useNavigate: () => mockNavigate }));
vi.mock('../../store/hooks', () => ({ useAppDispatch: () => mockDispatch }));
vi.mock('../analytics', () => ({
trackAnalyticsEvent: (...args: unknown[]) => mockTrackAnalyticsEvent(...args),
}));

vi.mock('../../utils/tauriCommands', async importOriginal => {
// Inherit everything else (types, sibling wrappers) verbatim so the panel
Expand All @@ -41,6 +49,7 @@ vi.mock('../../utils/tauriCommands', async importOriginal => {
memoryTreePipelineStatus: (...args: unknown[]) => mockPipelineStatus(...args),
memoryTreeSetEnabled: (...args: unknown[]) => mockSetEnabled(...args),
memorySyncStatusList: (...args: unknown[]) => mockSyncStatusList(...args),
memoryTreeRetryFailed: (...args: unknown[]) => mockRetryFailed(...args),
};
});

Expand Down Expand Up @@ -73,6 +82,8 @@ describe('<MemoryTreeStatusPanel />', () => {
mockPipelineStatus.mockReset();
mockSetEnabled.mockReset();
mockSyncStatusList.mockReset();
mockRetryFailed.mockReset();
mockTrackAnalyticsEvent.mockReset();
mockSyncStatusList.mockResolvedValue([]); // default: empty, harmless to existing tests
});

Expand Down Expand Up @@ -550,6 +561,125 @@ describe('<MemoryTreeStatusPanel />', () => {
expect(mockDispatch).toHaveBeenCalled();
});
});

// ── Retry-failed affordance ─────────────────────────────────────────────

it('offers a retry when jobs are parked in failed', async () => {
mockPipelineStatus.mockResolvedValue(
payload({
status: 'error',
reason: '29 unrecoverable failure(s) need action',
pipeline_jobs: { ready: 0, running: 0, failed: 29 },
})
);
render(<MemoryTreeStatusPanel />);

await waitFor(() => {
expect(screen.getByTestId('memory-tree-retry-failed')).toBeInTheDocument();
});
});

/**
* The affordance keys off the failed-job counter, not off the blocking-cause
* banner. A failure the pipeline has already worked past no longer surfaces a
* remediation (the core withholds a superseded cause), but its rows still sit
* in `failed` and still need clearing — so the button must be reachable with
* no banner on screen. Without this the user is left in a permanent `error`
* state with no way out, which is the bug.
*/
it('offers the retry even when no blocking cause is surfaced', async () => {
mockPipelineStatus.mockResolvedValue(
payload({
status: 'error',
reason: '29 unrecoverable failure(s) need action',
pipeline_jobs: { ready: 0, running: 0, failed: 29 },
first_blocking_cause: null,
})
);
render(<MemoryTreeStatusPanel />);

await waitFor(() => {
expect(screen.getByTestId('memory-tree-retry-failed')).toBeInTheDocument();
});
expect(screen.queryByTestId('memory-tree-blocking-cause')).not.toBeInTheDocument();
});

it('hides the retry when nothing has failed', async () => {
mockPipelineStatus.mockResolvedValue(payload({ status: 'running' }));
render(<MemoryTreeStatusPanel />);

await waitFor(() => {
expect(screen.getByTestId('memory-tree-status-label')).toHaveTextContent(/running/i);
});
expect(screen.queryByTestId('memory-tree-retry-failed')).not.toBeInTheDocument();
});

it('requeues the failed jobs, reports the count, and re-fetches', async () => {
mockPipelineStatus.mockResolvedValue(
payload({
status: 'error',
reason: '29 unrecoverable failure(s) need action',
pipeline_jobs: { ready: 0, running: 0, failed: 29 },
})
);
mockRetryFailed.mockResolvedValue({ requeued: 29 });
const onToast = vi.fn();
render(<MemoryTreeStatusPanel onToast={onToast} />);

await waitFor(() => {
expect(screen.getByTestId('memory-tree-retry-failed')).toBeInTheDocument();
});
const callsBefore = mockPipelineStatus.mock.calls.length;
await act(async () => {
fireEvent.click(screen.getByTestId('memory-tree-retry-failed'));
});

expect(mockRetryFailed).toHaveBeenCalledTimes(1);
// Successful domain outcome is tracked with the privacy-safe count only.
expect(mockTrackAnalyticsEvent).toHaveBeenCalledWith('memory_tree_retry_succeeded', {
count: 29,
});
await waitFor(() => {
expect(onToast).toHaveBeenCalledWith(
expect.objectContaining({ type: 'success', message: expect.stringContaining('29') })
);
});
// Successful domain outcome is tracked with a privacy-safe count only.
expect(mockTrackAnalyticsEvent).toHaveBeenCalledWith('memory_tree_retry_succeeded', {
count: 29,
});
await waitFor(() => {
expect(mockPipelineStatus.mock.calls.length).toBeGreaterThan(callsBefore);
});
});

it('surfaces an error toast when the requeue fails', async () => {
mockPipelineStatus.mockResolvedValue(
payload({
status: 'error',
reason: '29 unrecoverable failure(s) need action',
pipeline_jobs: { ready: 0, running: 0, failed: 29 },
})
);
mockRetryFailed.mockRejectedValue(new Error('UNIQUE constraint failed'));
const onToast = vi.fn();
render(<MemoryTreeStatusPanel onToast={onToast} />);

await waitFor(() => {
expect(screen.getByTestId('memory-tree-retry-failed')).toBeInTheDocument();
});
await act(async () => {
fireEvent.click(screen.getByTestId('memory-tree-retry-failed'));
});

await waitFor(() => {
expect(onToast).toHaveBeenCalledWith(
expect.objectContaining({ type: 'error', message: 'UNIQUE constraint failed' })
);
});
// The button must stay usable so a transient failure is not a dead end.
expect(screen.getByTestId('memory-tree-retry-failed')).not.toBeDisabled();
});
});

describe('integration health helpers', () => {
Expand Down
66 changes: 66 additions & 0 deletions app/src/components/intelligence/MemoryTreeStatusPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ import {
type MemorySyncStatusRow,
memoryTreePipelineStatus,
type MemoryTreePipelineStatus,
memoryTreeRetryFailed,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Recreate this as a real promote merge

This is a commit-level issue rather than a problem with this import: git rev-list --parents -n 1 40a0edc4 shows only parent 2ba803a246f8af20e3278c8acb62f34be2b421f5 and does not record 8deb5f2769c97ad059be2a6acae9f23c9c00dc3b as a second parent, even though the release promotion path is supposed to be a merge from main into release. If this lands, main is still not an ancestor of release, so the next promote/back-merge keeps the stale merge-base and re-fights these same conflicts; recreate it as a true two-parent merge before merging.

Useful? React with 👍 / 👎.

memoryTreeSetEnabled,
} from '../../utils/tauriCommands';
import { trackAnalyticsEvent } from '../analytics';
import Button from '../ui/Button';

/** Translator function shape exposed by `useT()`. */
Expand Down Expand Up @@ -338,6 +340,7 @@ export function MemoryTreeStatusPanel({ onToast }: MemoryTreeStatusPanelProps) {
const dispatch = useAppDispatch();
const { status, integrations, loading, error, refresh } = useMemoryTreeStatus();
const [toggleBusy, setToggleBusy] = useState(false);
const [retryBusy, setRetryBusy] = useState(false);

// #002 (FR-004): the single first blocking cause. Prefer the explicit
// `first_blocking_cause`; fall back to the active degradation cause so older
Expand Down Expand Up @@ -376,6 +379,46 @@ export function MemoryTreeStatusPanel({ onToast }: MemoryTreeStatusPanelProps) {
}
}, [status, toggleBusy, refresh, onToast, t]);

/**
* Requeue every terminally-failed job.
*
* An unrecoverable failure (auth, budget, dimension mismatch) is terminal by
* design — the worker never retries it — so a batch that failed under a
* since-fixed config stays parked forever and pins this panel on `error`.
* The `memory_tree_retry_failed` RPC existed for exactly this, but had no
* caller anywhere in the app, leaving the user with a permanent error state
* and no way to clear it.
*/
const handleRetryFailed = useCallback(async () => {
if (retryBusy) {
console.debug('[ui-flow][memory-tree-status] retryFailed: skipped busy=true');
return;
}
console.debug('[ui-flow][memory-tree-status] retryFailed: entry');
setRetryBusy(true);
console.debug('[ui-flow][memory-tree-status] retryFailed: busy=true rpc:start');
try {
const { requeued } = await memoryTreeRetryFailed();
console.debug('[ui-flow][memory-tree-status] retryFailed: rpc:ok requeued=%d', requeued);
// Record the successful domain outcome (not just the click). Privacy-safe:
// a non-identifying count only, no ids or user text.
trackAnalyticsEvent('memory_tree_retry_succeeded', { count: requeued });
onToast?.({
type: 'success',
title: t('memoryTree.status.retryFailedDone'),
message: t('memoryTree.status.retryFailedCount').replace('{count}', String(requeued)),
});
await refresh();
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
console.warn('[ui-flow][memory-tree-status] retryFailed: error %s', message);
onToast?.({ type: 'error', title: t('memoryTree.status.retryFailedError'), message });
} finally {
console.debug('[ui-flow][memory-tree-status] retryFailed: busy=false exit');
setRetryBusy(false);
}
}, [retryBusy, refresh, onToast, t]);

const statusKind = status?.status ?? 'idle';
// #5324: "Error — 936 unrecoverable failures need action" told the user
// nothing they could act on. When the blocking cause is a spent embedding
Expand Down Expand Up @@ -413,6 +456,12 @@ export function MemoryTreeStatusPanel({ onToast }: MemoryTreeStatusPanelProps) {
// with a localized remediation.
const degraded = status?.degraded;

// Parked failures are the one panel state the user can act on directly, and
// the affordance is keyed off the counter rather than off the blocking-cause
// banner: a failure the pipeline has already worked past no longer surfaces a
// remediation, but its rows still sit in `failed` and still need clearing.
const failedJobs = status?.pipeline_jobs.failed ?? 0;

const checked = !(status?.is_paused ?? false);

const tileClass =
Expand Down Expand Up @@ -513,6 +562,23 @@ export function MemoryTreeStatusPanel({ onToast }: MemoryTreeStatusPanelProps) {
{status.reason ? (
<div className="mt-0.5 text-[11px] text-content-muted">{status.reason}</div>
) : null}
{failedJobs > 0 ? (
<div className="mt-2">
<Button
variant="secondary"
size="xs"
disabled={retryBusy}
data-testid="memory-tree-retry-failed"
analyticsId="memory-tree-retry-failed-jobs"
onClick={() => {
void handleRetryFailed();
}}>
{retryBusy
? t('memoryTree.status.retryFailedBusy')
: t('memoryTree.status.retryFailed')}
</Button>
</div>
) : null}
</>
)}
</div>
Expand Down
Loading
Loading