Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
f0ea173
fix(storage): ignore retired artifact metadata
Astro-Han Sep 4, 2026
134c197
fix(runtime): return only child output artifacts
Astro-Han Sep 4, 2026
352fa32
refactor(core): remove unused artifact sources
Astro-Han Sep 4, 2026
2030907
refactor(storage): remove retired capture sweep
Astro-Han Sep 4, 2026
7e6dbce
refactor(artifacts): replace tombstones with physical deletion
Astro-Han Sep 4, 2026
a9adecb
fix(desktop): allow deleting all artifacts
Astro-Han Sep 4, 2026
02f0e37
refactor(storage): remove self-managed artifact writes
Astro-Han Sep 4, 2026
8a1ebc4
refactor(artifacts): remove dead change notifications
Astro-Han Sep 4, 2026
cc747d5
refactor(storage): retire artifact status metadata
Astro-Han Sep 4, 2026
101d98d
refactor(storage): remove global artifact deletion
Astro-Han Sep 4, 2026
3493f6e
refactor(artifacts): require explicit source ownership
Astro-Han Sep 4, 2026
cdd6dbf
refactor(storage): use artifact id as metadata key
Astro-Han Sep 4, 2026
4a39425
refactor(storage): remove artifact metadata readiness shim
Astro-Han Sep 4, 2026
3082d03
refactor(storage): remove artifact first-read lock state
Astro-Han Sep 4, 2026
66fe293
refactor(storage): remove artifact metadata interface
Astro-Han Sep 4, 2026
c5fd3d8
refactor(storage): remove unscoped attachment reads
Astro-Han Sep 4, 2026
ff232f8
refactor(storage): remove unscoped artifact reads
Astro-Han Sep 4, 2026
a46bec0
refactor(storage): remove artifact purge journal
Astro-Han Sep 4, 2026
24df74a
refactor(storage): remove artifact store facade type
Astro-Han Sep 4, 2026
e0301ab
fix(storage): discard invalid artifact metadata
Astro-Han Sep 4, 2026
410f487
refactor(storage): remove artifact publication recovery
Astro-Han Sep 4, 2026
f8075a4
refactor(storage): remove artifact recovery API
Astro-Han Sep 4, 2026
997b6ec
refactor(storage): inline artifact lock layout
Astro-Han Sep 4, 2026
f9a01e9
test(artifacts): align disposable residue gates
Astro-Han Sep 5, 2026
2633cb2
test(desktop): use visible artifact story fixture
Astro-Han Sep 5, 2026
744b8e9
fix(storage): preserve durable artifacts across v1 upgrades
Astro-Han Sep 5, 2026
c1edfdf
fix(desktop): refresh visible artifact panes after publication
Astro-Han Sep 5, 2026
8ce9865
fix(artifacts): protect workflow-owned evidence from user deletion
Astro-Han Sep 5, 2026
1f49db3
fix(storage): retain artifact cleanup obligations until durable removal
Astro-Han Sep 5, 2026
380622c
fix(runtime-host): fence artifact publication with session admission
Astro-Han Sep 5, 2026
7add3a0
test(runtime-host): keep recap retries safe after rejected artifact d…
Astro-Han Sep 5, 2026
4cd3f1d
refactor(artifacts): let session cleanup own projection image reclama…
Astro-Han Sep 5, 2026
8848652
fix(desktop): refresh artifact counts on background workbar tabs
Astro-Han Sep 5, 2026
445954e
test(desktop): use supported tab for background artifact coverage
Astro-Han Sep 5, 2026
66f3a82
test(desktop): decouple artifact count checks from tab name formatting
Astro-Han Sep 5, 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
2 changes: 1 addition & 1 deletion apps/desktop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ Sub-folders hold OS-facing implementations such as `browser/`, `computer-use/`,
Three patterns, all rooted in preload's `maka` namespace. Channel names are `<domain>:<action>`.

- **Request/response** — `ipcRenderer.invoke('<domain>:<action>', …args)` in preload ↔ `ipcMain.handle('<domain>:<action>', …)`. Runtime domains are projected by `runtime-host-*-ipc-main.ts`; OS-facing client domains use a focused `*-ipc-main.ts` module.
- **Main→renderer push** — main sends through the safe-send guard (`safeSendToRenderer` via `mainWindowController.send`), not raw `webContents.send` (which throws when the window/`webContents` is destroyed); preload subscribes via `ipcRenderer.on` and returns an unsubscribe fn (e.g. `sessions:changed`, `scheduled-tasks:changed`, `artifacts:changed`). The guard checks both the `BrowserWindow` and its `webContents` before delivery. Route every new main-window push through it.
- **Main→renderer push** — main sends through the safe-send guard (`safeSendToRenderer` via `mainWindowController.send`), not raw `webContents.send` (which throws when the window/`webContents` is destroyed); preload subscribes via `ipcRenderer.on` and returns an unsubscribe fn (e.g. `sessions:changed`, `scheduled-tasks:changed`). The guard checks both the `BrowserWindow` and its `webContents` before delivery. Route every new main-window push through it.
- **Renderer→main fire-and-forget** — `ipcRenderer.send('<domain>:<action>', …)` in preload ↔ `ipcMain.on('<domain>:<action>', …)`. Used when no response is needed (e.g. `browser:active-session`, `browser:setViewport`).

Adding a new IPC surface: if extracting, write the `*-ipc-main.ts` exporting a `register*Ipc(...)`, import it in `main.ts`, and call it inside `registerIpc()`; add the matching method to the `maka` namespace in `preload.ts`; add the method to the `window.maka` type in `src/global.d.ts` (the renderer's typed bridge — without it, renderer calls get a TS error); keep the `<domain>:<action>` channel naming. A handler file that isn't registered in `registerIpc()` compiles but never mounts.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ function attachmentReadHandler(
streamArtifact,
} as never,
mainWindowController: {} as never,
sendToRenderer() {},
showItemInFolder() {},
});
const handler = handlers.get("attachments:readBytes");
Expand All @@ -78,7 +77,6 @@ test("Runtime Host Artifact IPC preserves previews and streams complete exports"
const content = Buffer.alloc(70 * 1024, 5);
const handlers = new Map<string, Handler>();
const opened: string[] = [];
const events: unknown[] = [];
const artifact = {
id: "artifact-1",
sessionId: "session-1",
Expand All @@ -105,7 +103,7 @@ test("Runtime Host Artifact IPC preserves previews and streams complete exports"
return { ok: false, reason: "unsupported_mime" };
},
async deleteArtifact() {
return { kind: "deleted", artifact: { ...artifact, status: "deleted" } };
return { kind: "deleted" };
},
async streamArtifact(
_sessionId: string,
Expand All @@ -128,7 +126,6 @@ test("Runtime Host Artifact IPC preserves previews and streams complete exports"
mainWindowController: {
showSaveDialog: async () => ({ canceled: false, filePath: savedPath }),
} as never,
sendToRenderer: (_channel, event) => events.push(event),
showItemInFolder: (path) => opened.push(path),
presentationRoot,
});
Expand Down Expand Up @@ -159,7 +156,6 @@ test("Runtime Host Artifact IPC preserves previews and streams complete exports"
assert.deepEqual(await readFile(opened[0]!), content);

await handlers.get("artifacts:delete")?.({}, "session-1", "artifact-1");
assert.equal((events[0] as { reason: string }).reason, "deleted");
} finally {
await rm(root, { recursive: true, force: true });
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ function createBridgeRecorder(): {
'browser.setViewport',
'browser.onState',
'browser.onLive',
'artifacts.subscribeChanges',
'inspector.subscribeUsageChanges',
]);
// Adapters that reshape a bridge answer need one to reshape.
Expand Down Expand Up @@ -132,11 +131,10 @@ describe('createDesktopWorkbarServices', () => {
services.browser.subscribeState(eventHandler)();
services.browser.subscribeLive(eventHandler)();

await services.artifacts.list('s', { includeDeleted: true });
await services.artifacts.list('s');
await services.artifacts.readText('s', 'a');
await services.artifacts.readBinary('s', 'a');
await services.artifacts.delete('s', 'a');
services.artifacts.subscribeChanges(eventHandler)();
await services.artifacts.openPath('s', 'a');
await services.artifacts.saveAs('s', 'a');

Expand Down Expand Up @@ -207,7 +205,6 @@ describe('createDesktopWorkbarServices', () => {
'artifacts.readText',
'artifacts.readBinary',
'artifacts.delete',
'artifacts.subscribeChanges',
'app.openArtifactPath',
'app.saveArtifactAs',
'inspector.trace',
Expand Down
32 changes: 5 additions & 27 deletions apps/desktop/src/main/runtime-host-artifacts-ipc-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ interface RuntimeHostArtifactsIpcDeps {
readonly ipcMain: ReconnectableReadIpcMain;
readonly client: DesktopRuntimeHostClient;
readonly mainWindowController: ReturnType<typeof createMainWindowController>;
readonly sendToRenderer: (channel: string, ...args: unknown[]) => void;
readonly showItemInFolder: (path: string) => void;
readonly presentationRoot?: string;
}
Expand All @@ -61,16 +60,7 @@ export function registerRuntimeHostArtifactsIpc(
handleReconnectableRead(
deps.ipcMain,
"artifacts:list",
async (
_event,
sessionId: string,
options?: { includeDeleted?: boolean },
) => {
const artifacts = await deps.client.listArtifacts(sessionId);
return options?.includeDeleted
? artifacts
: artifacts.filter(({ status }) => status !== "deleted");
},
(_event, sessionId: string) => deps.client.listArtifacts(sessionId),
);
handleReconnectableRead(
deps.ipcMain,
Expand All @@ -86,23 +76,15 @@ export function registerRuntimeHostArtifactsIpc(
);
deps.ipcMain.handle(
"artifacts:delete",
async (_event, sessionId: string, artifactId: string) => {
const result = await deps.client.deleteArtifact(sessionId, artifactId);
deps.sendToRenderer("artifacts:changed", {
reason: "deleted",
artifactId,
sessionId,
ts: Date.now(),
});
return result;
},
(_event, sessionId: string, artifactId: string) =>
deps.client.deleteArtifact(sessionId, artifactId),
);
registerRuntimeHostAttachmentPreviewIpc(deps);
deps.ipcMain.handle(
"app:openArtifactPath",
async (_event, sessionId: string, artifactId: string) => {
const artifact = await deps.client.getArtifact(sessionId, artifactId);
if (!artifact || artifact.status === "deleted") {
if (!artifact) {
return { ok: false as const, reason: "missing" as const };
}
try {
Expand All @@ -128,7 +110,6 @@ export function registerRuntimeHostArtifactsIpc(
): Promise<ArtifactSaveResult> => {
const artifact = await deps.client.getArtifact(sessionId, artifactId);
if (!artifact) return { ok: false, reason: "not_found" };
if (artifact.status === "deleted") return { ok: false, reason: "deleted" };
const result = await deps.mainWindowController.showSaveDialog({
title: `另存为 ${artifact.name}`,
defaultPath: artifact.name,
Expand Down Expand Up @@ -161,10 +142,7 @@ export function registerRuntimeHostAttachmentPreviewIpc(
"attachments:readBytes",
async (_event, sessionId: string, artifactId: string) => {
const artifact = await deps.client.getArtifact(sessionId, artifactId);
if (
!artifact ||
artifact.status === "deleted"
) {
if (!artifact) {
return { ok: false as const, reason: "not_found" };
}
const preview = resolveArtifactImagePreview(artifact);
Expand Down
1 change: 0 additions & 1 deletion apps/desktop/src/main/runtime-host-boot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1499,7 +1499,6 @@ function registerHostClientIpc(
ipcMain: scopedIpc,
client,
mainWindowController,
sendToRenderer,
showItemInFolder: (path) => shell.showItemInFolder(path),
});
registerRuntimeHostOAuthIpc({
Expand Down
4 changes: 1 addition & 3 deletions apps/desktop/src/preload/bridge-contract.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ import type {
} from '@maka/core/git-review';
import type {
ArtifactBinaryReadResult,
ArtifactChangedEvent,
ArtifactDescriptor,
ArtifactSaveResult,
ArtifactTextReadResult,
Expand Down Expand Up @@ -1810,11 +1809,10 @@ export interface MakaBridge {
getState(): Promise<E2eFixtureState | null>;
};
artifacts: {
list(sessionId: string, opts?: { includeDeleted?: boolean }): Promise<ArtifactDescriptor[]>;
list(sessionId: string): Promise<ArtifactDescriptor[]>;
readText(sessionId: string, artifactId: string): Promise<ArtifactTextReadResult>;
readBinary(sessionId: string, artifactId: string): Promise<ArtifactBinaryReadResult>;
delete(sessionId: string, artifactId: string): Promise<void>;
subscribeChanges(handler: (event: ArtifactChangedEvent) => void): () => void;
};
skills: {
list(host?: DesktopRuntimeHostRef): Promise<SkillEntry[]>;
Expand Down
13 changes: 2 additions & 11 deletions apps/desktop/src/preload/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,6 @@ import type {
} from '@maka/core/git-review';
import type {
ArtifactBinaryReadResult,
ArtifactChangedEvent,
ArtifactDescriptor,
ArtifactSaveResult,
ArtifactTextReadResult,
Expand Down Expand Up @@ -3598,8 +3597,8 @@ const makaBridge = {
},
},
artifacts: {
list(sessionId: string, opts?: { includeDeleted?: boolean }): Promise<ArtifactDescriptor[]> {
return invokeProjectedSessionRuntimeHost('artifacts:list', sessionId, opts);
list(sessionId: string): Promise<ArtifactDescriptor[]> {
return invokeProjectedSessionRuntimeHost('artifacts:list', sessionId);
},
readText(sessionId: string, artifactId: string): Promise<ArtifactTextReadResult> {
return invokeSessionRuntimeHost('artifacts:readText', sessionId, artifactId);
Expand All @@ -3610,14 +3609,6 @@ const makaBridge = {
delete(sessionId: string, artifactId: string): Promise<void> {
return invokeSessionRuntimeHost('artifacts:delete', sessionId, artifactId);
},
subscribeChanges(handler: (event: ArtifactChangedEvent) => void): () => void {
return subscribeEveryRuntimeHostEvent('artifacts:changed', (scope, event: ArtifactChangedEvent) =>
handler({
...event,
sessionId: recordRuntimeHostSessionScope(scope, event.sessionId),
}),
);
},
},
skills: {
list(host?: DesktopRuntimeHostRef): Promise<SkillEntry[]> {
Expand Down
9 changes: 1 addition & 8 deletions apps/desktop/src/renderer/features/workbar/ports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import type {
} from '@maka/core/events';
import type {
ArtifactBinaryReadResult,
ArtifactChangedEvent,
ArtifactDescriptor,
ArtifactSaveResult,
ArtifactTextReadResult,
Expand Down Expand Up @@ -126,10 +125,7 @@ export type WorkbarOpenArtifactResult =
};

export interface WorkbarArtifactsService {
list(
sessionId: string,
options?: { includeDeleted?: boolean },
): Promise<ArtifactDescriptor[]>;
list(sessionId: string): Promise<ArtifactDescriptor[]>;
readText(
sessionId: string,
artifactId: string,
Expand All @@ -139,9 +135,6 @@ export interface WorkbarArtifactsService {
artifactId: string,
): Promise<ArtifactBinaryReadResult>;
delete(sessionId: string, artifactId: string): Promise<void>;
subscribeChanges(
handler: (event: ArtifactChangedEvent) => void,
): WorkbarUnsubscribe;
openPath(
sessionId: string,
artifactId: string,
Expand Down
1 change: 0 additions & 1 deletion apps/desktop/src/renderer/features/workbar/testing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ export function createFakeWorkbarServices(
readText: async () => ({ ok: false, reason: 'not_found' }),
readBinary: async () => ({ ok: false, reason: 'not_found' }),
delete: async () => undefined,
subscribeChanges: noopSubscription,
openPath: async () => ({ ok: false, reason: 'missing' }),
saveAs: async () => ({ ok: false, reason: 'canceled' }),
},
Expand Down
Loading