@harness-mix/cli 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +86 -56
  2. package/docs/storage-verification-design.md +49 -0
  3. package/output/native-build/renderer-extension.js +2743 -2330
  4. package/package.json +3 -2
  5. package/scripts/codebuddy-migration-test.cjs +1 -1
  6. package/scripts/core-runtime-test.cjs +9 -1
  7. package/scripts/native-protocol-test.cjs +11 -1
  8. package/scripts/native-ui-smoke.cjs +23 -4
  9. package/scripts/projector-test.cjs +13 -0
  10. package/scripts/storage-verification-test.cjs +78 -0
  11. package/scripts/switch-harness-test.cjs +1 -1
  12. package/src/main/host/collaboration.js +2 -0
  13. package/src/main/host/core-session.js +7 -1
  14. package/src/main/host/handoff-checkpoints.js +8 -1
  15. package/src/main/host/runtime.js +150 -36
  16. package/src/main/host/store.js +32 -11
  17. package/src/main/host/thread-storage.js +73 -0
  18. package/src/main/host/thread-store.js +123 -0
  19. package/src/main/host/verification-gates.js +118 -0
  20. package/src/main/native/protocol.js +20 -5
  21. package/src/main/protocol-core/projector.js +20 -3
  22. package/src/main/shared-contracts/event.js +2 -1
  23. package/src/main/shared-contracts/item.js +1 -0
  24. package/src/native-ui/renderer-extension/dist/types/harness-mix-settings.d.ts +2 -2
  25. package/src/native-ui/renderer-extension/dist/types/harness-mix-settings.d.ts.map +1 -1
  26. package/src/native-ui/renderer-extension/dist/types/renderer-binding-probe.d.ts.map +1 -1
  27. package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts +5 -0
  28. package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts.map +1 -1
  29. package/src/native-ui/renderer-extension/dist/types/renderer-settings-lifecycle.d.ts +2 -1
  30. package/src/native-ui/renderer-extension/dist/types/renderer-settings-lifecycle.d.ts.map +1 -1
  31. package/src/native-ui/renderer-extension/dist/types/settings/icons.d.ts +1 -1
  32. package/src/native-ui/renderer-extension/dist/types/settings/icons.d.ts.map +1 -1
  33. package/src/native-ui/renderer-extension/dist/types/settings/localization.d.ts.map +1 -1
  34. package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts +5 -3
  35. package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts.map +1 -1
  36. package/src/native-ui/renderer-extension/dist/types/settings/storage-page.d.ts +20 -0
  37. package/src/native-ui/renderer-extension/dist/types/settings/storage-page.d.ts.map +1 -0
  38. package/src/native-ui/renderer-extension/dist/types/tsconfig.tsbuildinfo +1 -1
  39. package/src/native-ui/renderer-extension/src/harness-mix-settings.ts +8 -89
  40. package/src/native-ui/renderer-extension/src/renderer-binding-probe.ts +5 -1
  41. package/src/native-ui/renderer-extension/src/renderer-model-client.ts +4 -0
  42. package/src/native-ui/renderer-extension/src/renderer-settings-lifecycle.ts +12 -0
  43. package/src/native-ui/renderer-extension/src/settings/icons.ts +6 -3
  44. package/src/native-ui/renderer-extension/src/settings/localization.ts +4 -2
  45. package/src/native-ui/renderer-extension/src/settings/pages.ts +12 -5
  46. package/src/native-ui/renderer-extension/src/settings/shell.css +8 -3
  47. package/src/native-ui/renderer-extension/src/settings/storage-page.ts +74 -0
  48. package/src/native-ui/renderer-extension/test/renderer-model-client.test.ts +2 -0
  49. package/src/native-ui/renderer-extension/test/settings/harness-settings.test.ts +5 -0
  50. package/src/native-ui/renderer-extension/test/settings/localization.test.ts +1 -1
  51. package/src/native-ui/renderer-extension/test/settings/pages.test.ts +50 -4
  52. package/src/native-ui/renderer-extension/test/settings/shell.test.ts +3 -0
@@ -0,0 +1,74 @@
1
+ import type { RendererSettingsPageDefinition, RendererSettingsPageMountContext } from './core.js';
2
+ import type { RendererSettingsMessages } from './localization.js';
3
+ import { createRendererSettingsIcon } from './icons.js';
4
+
5
+ export interface RendererStorageInspection {
6
+ readonly storageSchemaVersion: number;
7
+ readonly threadCount: number;
8
+ readonly loadedThreadCount: number;
9
+ readonly indexBytes: number;
10
+ readonly recordBytes: number;
11
+ readonly recordCount: number;
12
+ readonly legacyBytes: number;
13
+ }
14
+
15
+ export interface RendererStorageClient {
16
+ inspectStorage(): Promise<RendererStorageInspection>;
17
+ optimizeStorage(): Promise<{ before: RendererStorageInspection; after: RendererStorageInspection }>;
18
+ }
19
+
20
+ function bytes(value: number): string {
21
+ if (value < 1024) return `${value} B`;
22
+ const units = ['KB', 'MB', 'GB'];
23
+ let scaled = value;
24
+ let unit = 'B';
25
+ for (const next of units) { scaled /= 1024; unit = next; if (scaled < 1024) break; }
26
+ return `${scaled.toFixed(scaled >= 10 ? 0 : 1)} ${unit}`;
27
+ }
28
+
29
+ export function createStorageSettingsPage(messages: RendererSettingsMessages, getClient: () => RendererStorageClient | null): RendererSettingsPageDefinition {
30
+ const zh = messages.locale === 'zh-CN';
31
+ return Object.freeze({
32
+ id: 'storage', label: zh ? '存储与验证' : 'Storage & verification', icon: 'storage',
33
+ mount(context: RendererSettingsPageMountContext) {
34
+ const document = context.content.ownerDocument;
35
+ const heading = document.createElement('div'); heading.className = 'settings-section-label'; heading.textContent = zh ? '会话存储' : 'Session storage';
36
+ const storage = document.createElement('section'); storage.className = 'settings-storage-block';
37
+ const panel = document.createElement('section'); panel.className = 'settings-update-panel'; panel.setAttribute('aria-live', 'polite');
38
+ const actions = document.createElement('div'); actions.className = 'settings-update-actions';
39
+ const optimize = document.createElement('button'); optimize.type = 'button'; optimize.className = 'settings-command-button';
40
+ optimize.append(createRendererSettingsIcon('storage', 16), zh ? '优化存储' : 'Optimize storage');
41
+ actions.append(optimize);
42
+ const render = (value: RendererStorageInspection): void => {
43
+ panel.replaceChildren();
44
+ const title = document.createElement('strong'); title.className = 'settings-update-panel__title'; title.textContent = `Schema v${value.storageSchemaVersion}`;
45
+ const copy = document.createElement('p'); copy.className = 'settings-update-summary';
46
+ copy.textContent = zh
47
+ ? `${value.threadCount} 个任务,冷启动仅载入 ${value.loadedThreadCount} 个完整任务;索引 ${bytes(value.indexBytes)},分片 ${bytes(value.recordBytes)}。`
48
+ : `${value.threadCount} tasks; ${value.loadedThreadCount} full tasks loaded at cold start. Index ${bytes(value.indexBytes)}, shards ${bytes(value.recordBytes)}.`;
49
+ panel.append(title, copy);
50
+ };
51
+ const client = getClient();
52
+ if (!client) { panel.textContent = zh ? '当前 Host 不支持存储治理。' : 'Storage governance is unavailable on this Host.'; optimize.disabled = true; }
53
+ else {
54
+ void context.runLatest(() => client.inspectStorage(), { success: render, failure(error) { panel.textContent = error instanceof Error ? error.message : String(error); } });
55
+ optimize.addEventListener('click', () => {
56
+ optimize.disabled = true;
57
+ void context.runLatest(() => client.optimizeStorage(), {
58
+ success(result) { optimize.disabled = false; render(result.after); },
59
+ failure(error) { optimize.disabled = false; panel.textContent = error instanceof Error ? error.message : String(error); },
60
+ });
61
+ });
62
+ }
63
+ const gate = document.createElement('section'); gate.className = 'settings-about-panel';
64
+ const gateTitle = document.createElement('strong'); gateTitle.textContent = zh ? '任务验证门禁' : 'Task verification gates';
65
+ const gateCopy = document.createElement('p'); gateCopy.textContent = zh
66
+ ? '在任务命令面板使用 /gate 配置策略,使用 /verify 立即运行。验证报告会显示在对应回合并随接力证据传递。'
67
+ : 'Use /gate in a task command palette to configure policy and /verify to run it. Reports appear on the Turn and travel with handoff evidence.';
68
+ gate.append(gateTitle, gateCopy);
69
+ storage.append(panel, actions);
70
+ context.content.append(heading, storage, gate);
71
+ return undefined;
72
+ },
73
+ });
74
+ }
@@ -296,6 +296,7 @@ describe("Renderer fixed Model request client", () => {
296
296
  "inspectCodexAccountUsage",
297
297
  "inspectHarness",
298
298
  "inspectHarnessCommands",
299
+ "inspectStorage",
299
300
  "inspectThread",
300
301
  "inspectThreadCommands",
301
302
  "inspectThreadUsage",
@@ -313,6 +314,7 @@ describe("Renderer fixed Model request client", () => {
313
314
  "logoutCodexAccount",
314
315
  "messageThread",
315
316
  "openHarnessWebUi",
317
+ "optimizeStorage",
316
318
  "readCurrentVersion",
317
319
  "readUpdateStatus",
318
320
  "refreshCodexAccounts",
@@ -15,8 +15,13 @@ import { rendererSettingsMessages } from "../../src/settings/localization.js";
15
15
  import { RendererSettingsPageScope } from "../../src/settings/core.js";
16
16
  import { KNOWN_RENDERER_AGENTS, type ExternalRendererAgent } from "../../src/agent-selection-state.js";
17
17
  import { installRendererSettingsLifecycle } from "../../src/harness-mix-settings.js";
18
+ import { installRendererSettingsLifecycle as installCanonicalRendererSettingsLifecycle } from "../../src/renderer-settings-lifecycle.js";
18
19
 
19
20
  describe("Harness Mix Settings & Model Configuration", () => {
21
+ it("uses the canonical settings lifecycle from the live renderer entrypoint", () => {
22
+ expect(installRendererSettingsLifecycle).toBe(installCanonicalRendererSettingsLifecycle);
23
+ });
24
+
20
25
  it("provides known install commands without guessing npm packages for native CLIs", () => {
21
26
  const externalAgents = KNOWN_RENDERER_AGENTS.filter(
22
27
  (agent): agent is ExternalRendererAgent => agent !== "codex",
@@ -75,6 +75,6 @@ describe("Renderer settings localization", () => {
75
75
  createDefaultRendererSettingsPages(rendererSettingsMessages("zh-CN")).map(
76
76
  ({ label }) => label,
77
77
  ),
78
- ).toEqual(["连接", "账号", "MCP", "Skills", "会话导入", "更新", "关于"]);
78
+ ).toEqual(["连接", "账号", "MCP", "Skills", "会话导入", "存储与验证", "更新", "关于"]);
79
79
  });
80
80
  });
@@ -2030,7 +2030,7 @@ describe("Renderer Session Import page", () => {
2030
2030
  scope.dispose();
2031
2031
  });
2032
2032
 
2033
- it("ignores a Session list that resolves after the settings page is disposed", async () => {
2033
+ it("ignores a Session list that resolves after the settings page is disposed", async () => {
2034
2034
  const listed = deferred<{ candidates: never[]; total: number }>();
2035
2035
  const client = {
2036
2036
  listSessionImportSources: vi.fn(async () => ({
@@ -2064,6 +2064,52 @@ describe("Renderer Session Import page", () => {
2064
2064
  await Promise.resolve();
2065
2065
  await Promise.resolve();
2066
2066
 
2067
- expect(visibleText(content)).toBe(before);
2068
- });
2069
- });
2067
+ expect(visibleText(content)).toBe(before);
2068
+ });
2069
+
2070
+ it("shows sharded storage metrics and optimizes loaded task data", async () => {
2071
+ const inspection = {
2072
+ storageSchemaVersion: 3,
2073
+ threadCount: 12,
2074
+ loadedThreadCount: 2,
2075
+ indexBytes: 2048,
2076
+ recordBytes: 5 * 1024 * 1024,
2077
+ recordCount: 12,
2078
+ legacyBytes: 0,
2079
+ };
2080
+ const client = {
2081
+ inspectStorage: vi.fn(async () => inspection),
2082
+ optimizeStorage: vi.fn(async () => ({ before: inspection, after: { ...inspection, recordBytes: 4 * 1024 * 1024 } })),
2083
+ };
2084
+ const page = createDefaultRendererSettingsPages(
2085
+ rendererSettingsMessages("zh-CN"),
2086
+ () => null,
2087
+ () => null,
2088
+ () => null,
2089
+ () => null,
2090
+ undefined,
2091
+ () => null,
2092
+ () => client,
2093
+ ).find(({ id }) => id === "storage");
2094
+ if (!page) throw new Error("Storage page is not registered");
2095
+ const document = new FakeDocument();
2096
+ const content = document.createElement("main");
2097
+ const scope = new RendererSettingsPageScope();
2098
+ page.mount({
2099
+ content: content as unknown as HTMLElement,
2100
+ signal: scope.signal,
2101
+ runLatest: (operation, handlers) => scope.runLatest(operation, handlers),
2102
+ });
2103
+
2104
+ await vi.waitFor(() => expect(visibleText(content)).toContain("12 个任务"));
2105
+ expect(visibleText(content)).toContain("Schema v3");
2106
+ expect(visibleText(content)).toContain("2.0 KB");
2107
+ expect(visibleText(content)).toContain("5.0 MB");
2108
+ const optimize = descendants(content).find((element) => element.className === "settings-command-button");
2109
+ if (!optimize) throw new Error("Optimize storage action is not rendered");
2110
+ optimize.dispatch("click");
2111
+ await vi.waitFor(() => expect(client.optimizeStorage).toHaveBeenCalledOnce());
2112
+ await vi.waitFor(() => expect(visibleText(content)).toContain("4.0 MB"));
2113
+ scope.dispose();
2114
+ });
2115
+ });
@@ -26,6 +26,7 @@ describe("Renderer settings foundation", () => {
26
26
  "MCP",
27
27
  "Skills",
28
28
  "Session Import",
29
+ "Storage & verification",
29
30
  "Updates",
30
31
  "About",
31
32
  ]);
@@ -35,6 +36,7 @@ describe("Renderer settings foundation", () => {
35
36
  "connections",
36
37
  "session-import",
37
38
  "session-import",
39
+ "storage",
38
40
  "updates",
39
41
  "about",
40
42
  ]);
@@ -70,6 +72,7 @@ describe("Renderer settings foundation", () => {
70
72
  "mcp",
71
73
  "skills",
72
74
  "session-import",
75
+ "storage",
73
76
  "updates",
74
77
  "about",
75
78
  ]);