@harness-mix/cli 0.1.5 → 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.
- package/README.md +86 -56
- package/docs/harness-management.md +4 -4
- package/docs/multi-agent-collaboration.md +9 -9
- package/docs/storage-verification-design.md +49 -0
- package/output/native-build/renderer-extension.js +4210 -2273
- package/package.json +11 -9
- package/scripts/codebuddy-migration-test.cjs +1 -1
- package/scripts/collaboration-test.cjs +8 -3
- package/scripts/collaboration-ui-smoke.cjs +40 -26
- package/scripts/core-runtime-test.cjs +9 -1
- package/scripts/integrations-test.cjs +63 -4
- package/scripts/integrations-ui-smoke.cjs +77 -16
- package/scripts/native-protocol-test.cjs +113 -2
- package/scripts/native-sidebar-test.cjs +7 -0
- package/scripts/native-skill-roots-test.cjs +90 -0
- package/scripts/native-ui-smoke.cjs +23 -4
- package/scripts/projector-test.cjs +13 -0
- package/scripts/storage-verification-test.cjs +78 -0
- package/scripts/switch-harness-test.cjs +1 -1
- package/scripts/test-live-mentions.cjs +152 -0
- package/src/main/adapters/antigravity.js +20 -17
- package/src/main/adapters/claude.js +41 -8
- package/src/main/adapters/codebuddy.js +1 -0
- package/src/main/adapters/codex.js +20 -2
- package/src/main/adapters/cursor.js +5 -1
- package/src/main/adapters/dsh.js +8 -1
- package/src/main/adapters/grok.js +4 -1
- package/src/main/adapters/hermes.js +8 -0
- package/src/main/adapters/kiro.js +2 -1
- package/src/main/adapters/managed-mcp.js +15 -2
- package/src/main/adapters/omp.js +11 -0
- package/src/main/adapters/openclaw.js +6 -0
- package/src/main/adapters/opencode.js +5 -1
- package/src/main/adapters/pi.js +5 -1
- package/src/main/adapters/qoder.js +1 -0
- package/src/main/adapters/trae.js +4 -0
- package/src/main/adapters/zcode.js +3 -0
- package/src/main/host/collaboration.js +59 -11
- package/src/main/host/core-session.js +7 -1
- package/src/main/host/handoff-checkpoints.js +8 -1
- package/src/main/host/integrations.js +164 -26
- package/src/main/host/runtime.js +270 -116
- package/src/main/host/store.js +32 -11
- package/src/main/host/thread-storage.js +73 -0
- package/src/main/host/thread-store.js +123 -0
- package/src/main/host/verification-gates.js +118 -0
- package/src/main/native/protocol.js +209 -83
- package/src/main/native/thread-list.js +3 -0
- package/src/main/protocol-core/projector.js +20 -3
- package/src/main/shared-contracts/event.js +2 -1
- package/src/main/shared-contracts/item.js +1 -0
- package/src/native-ui/renderer-extension/dist/types/harness-mix-settings.d.ts +2 -2
- package/src/native-ui/renderer-extension/dist/types/harness-mix-settings.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-binding-probe.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-chatgpt-context.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-mentions.d.ts +11 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-mentions.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-integrations-client.d.ts +29 -4
- package/src/native-ui/renderer-extension/dist/types/renderer-integrations-client.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts +5 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-settings-lifecycle.d.ts +2 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-settings-lifecycle.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/icons.d.ts +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/icons.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/integrations-page.d.ts +2 -1
- package/src/native-ui/renderer-extension/dist/types/settings/integrations-page.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/localization.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts +5 -3
- package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/storage-page.d.ts +20 -0
- package/src/native-ui/renderer-extension/dist/types/settings/storage-page.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/tsconfig.tsbuildinfo +1 -1
- package/src/native-ui/renderer-extension/src/harness-mix-settings.ts +8 -88
- package/src/native-ui/renderer-extension/src/renderer-binding-probe.ts +150 -168
- package/src/native-ui/renderer-extension/src/renderer-chatgpt-context.ts +5 -1
- package/src/native-ui/renderer-extension/src/renderer-harness-mentions.ts +556 -123
- package/src/native-ui/renderer-extension/src/renderer-integrations-client.ts +36 -3
- package/src/native-ui/renderer-extension/src/renderer-model-client.ts +4 -0
- package/src/native-ui/renderer-extension/src/renderer-settings-lifecycle.ts +12 -0
- package/src/native-ui/renderer-extension/src/settings/icons.ts +6 -3
- package/src/native-ui/renderer-extension/src/settings/integrations-page.ts +1434 -62
- package/src/native-ui/renderer-extension/src/settings/localization.ts +8 -4
- package/src/native-ui/renderer-extension/src/settings/pages.ts +17 -8
- package/src/native-ui/renderer-extension/src/settings/shell.css +103 -8
- package/src/native-ui/renderer-extension/src/settings/storage-page.ts +74 -0
- package/src/native-ui/renderer-extension/test/renderer-chatgpt-context.test.ts +4 -1
- package/src/native-ui/renderer-extension/test/renderer-model-client.test.ts +2 -0
- package/src/native-ui/renderer-extension/test/settings/harness-settings.test.ts +5 -0
- package/src/native-ui/renderer-extension/test/settings/localization.test.ts +1 -1
- package/src/native-ui/renderer-extension/test/settings/pages.test.ts +50 -4
- package/src/native-ui/renderer-extension/test/settings/shell.test.ts +8 -2
|
@@ -511,8 +511,10 @@ const ENGLISH_MESSAGES: RendererSettingsMessages = Object.freeze({
|
|
|
511
511
|
pageLabels: Object.freeze({
|
|
512
512
|
connections: "Connections",
|
|
513
513
|
accounts: "Accounts",
|
|
514
|
-
|
|
515
|
-
|
|
514
|
+
mcp: 'MCP',
|
|
515
|
+
skills: 'Skills',
|
|
516
|
+
"session-import": "Session Import",
|
|
517
|
+
storage: "Storage & verification",
|
|
516
518
|
updates: "Updates",
|
|
517
519
|
about: "About",
|
|
518
520
|
}),
|
|
@@ -764,11 +766,13 @@ const CHINESE_MESSAGES: RendererSettingsMessages = Object.freeze({
|
|
|
764
766
|
aboutOpenSource: "Harness Mix 是一个开源项目,开源地址:",
|
|
765
767
|
aboutStarCallout: "⭐ 如果这个项目对你有帮助,请给我们一个 Star!⭐",
|
|
766
768
|
aboutRepository: "开源仓库",
|
|
767
|
-
pageLabels: Object.freeze({
|
|
769
|
+
pageLabels: Object.freeze({
|
|
768
770
|
connections: "连接",
|
|
769
771
|
accounts: "账号",
|
|
770
|
-
|
|
772
|
+
mcp: 'MCP',
|
|
773
|
+
skills: 'Skills',
|
|
771
774
|
"session-import": "会话导入",
|
|
775
|
+
storage: "存储与验证",
|
|
772
776
|
updates: "更新",
|
|
773
777
|
about: "关于",
|
|
774
778
|
}),
|
|
@@ -27,9 +27,10 @@ import {
|
|
|
27
27
|
type RendererImportedThreadOpener,
|
|
28
28
|
} from "./session-import-page.js";
|
|
29
29
|
import { createReleaseNotesElement } from "./release-notes.js";
|
|
30
|
-
import {
|
|
30
|
+
import { createMcpSettingsPage, createSkillsSettingsPage } from './integrations-page.js';
|
|
31
31
|
import type { RendererIntegrationsClient } from '../renderer-integrations-client.js';
|
|
32
|
-
import { createAccountsSettingsPage, type RendererCodexAccountClient } from "./accounts-page.js";
|
|
32
|
+
import { createAccountsSettingsPage, type RendererCodexAccountClient } from "./accounts-page.js";
|
|
33
|
+
import { createStorageSettingsPage, type RendererStorageClient } from './storage-page.js';
|
|
33
34
|
|
|
34
35
|
export type {
|
|
35
36
|
RendererConnectionAgentSnapshot,
|
|
@@ -74,8 +75,10 @@ function windowsInstallerDownloadUrl(window: Window | null | undefined, version:
|
|
|
74
75
|
export const DEFAULT_RENDERER_SETTINGS_PAGE_IDS = [
|
|
75
76
|
"connections",
|
|
76
77
|
"accounts",
|
|
77
|
-
"
|
|
78
|
-
"
|
|
78
|
+
"mcp",
|
|
79
|
+
"skills",
|
|
80
|
+
"session-import",
|
|
81
|
+
"storage",
|
|
79
82
|
"updates",
|
|
80
83
|
"about",
|
|
81
84
|
] as const;
|
|
@@ -625,12 +628,15 @@ export function createDefaultRendererSettingsPages(
|
|
|
625
628
|
openImportedThread: RendererImportedThreadOpener = () =>
|
|
626
629
|
Promise.reject(new Error("Imported Thread navigation is unavailable")),
|
|
627
630
|
getIntegrationsClient: () => RendererIntegrationsClient | null = () => null,
|
|
628
|
-
|
|
631
|
+
getStorageClient: () => RendererStorageClient | null = () => null,
|
|
632
|
+
): readonly RendererSettingsPageDefinition[] {
|
|
629
633
|
return Object.freeze([
|
|
630
634
|
createConnectionsSettingsPage(messages, getDiagnostics),
|
|
631
635
|
createAccountsSettingsPage(messages, getAccountClient),
|
|
632
|
-
|
|
633
|
-
|
|
636
|
+
createMcpSettingsPage(messages, getIntegrationsClient),
|
|
637
|
+
createSkillsSettingsPage(messages, getIntegrationsClient),
|
|
638
|
+
createSessionImportSettingsPage(messages, getSessionImportClient, openImportedThread),
|
|
639
|
+
createStorageSettingsPage(messages, getStorageClient),
|
|
634
640
|
updatesPage(messages, getUpdateClient),
|
|
635
641
|
aboutPage(messages, getUpdateClient),
|
|
636
642
|
]);
|
|
@@ -644,6 +650,7 @@ export function createDefaultRendererSettingsRegistry(
|
|
|
644
650
|
getSessionImportClient: () => RendererSessionImportClient | null = () => null,
|
|
645
651
|
openImportedThread?: RendererImportedThreadOpener,
|
|
646
652
|
getIntegrationsClient?: () => RendererIntegrationsClient | null,
|
|
653
|
+
getStorageClient?: () => RendererStorageClient | null,
|
|
647
654
|
): RendererSettingsPageRegistry {
|
|
648
655
|
return createRendererSettingsPageRegistry(
|
|
649
656
|
createDefaultRendererSettingsPages(
|
|
@@ -654,8 +661,10 @@ export function createDefaultRendererSettingsRegistry(
|
|
|
654
661
|
getSessionImportClient,
|
|
655
662
|
openImportedThread,
|
|
656
663
|
getIntegrationsClient,
|
|
664
|
+
getStorageClient,
|
|
657
665
|
),
|
|
658
666
|
);
|
|
659
667
|
}
|
|
660
668
|
|
|
661
|
-
export type { RendererCodexAccountClient } from "./accounts-page.js";
|
|
669
|
+
export type { RendererCodexAccountClient } from "./accounts-page.js";
|
|
670
|
+
export type { RendererStorageClient, RendererStorageInspection } from './storage-page.js';
|
|
@@ -1813,13 +1813,19 @@ button {
|
|
|
1813
1813
|
outline-offset: -2px;
|
|
1814
1814
|
}
|
|
1815
1815
|
|
|
1816
|
-
.settings-update-actions {
|
|
1816
|
+
.settings-update-actions {
|
|
1817
1817
|
display: flex;
|
|
1818
1818
|
align-items: center;
|
|
1819
1819
|
min-width: 0;
|
|
1820
1820
|
gap: 12px;
|
|
1821
1821
|
flex-wrap: wrap;
|
|
1822
|
-
}
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
.settings-storage-block {
|
|
1825
|
+
display: grid;
|
|
1826
|
+
gap: 12px;
|
|
1827
|
+
margin-bottom: 16px;
|
|
1828
|
+
}
|
|
1823
1829
|
|
|
1824
1830
|
.settings-command-button,
|
|
1825
1831
|
.settings-update-link {
|
|
@@ -2236,16 +2242,105 @@ button {
|
|
|
2236
2242
|
.settings-install-steps li {
|
|
2237
2243
|
margin-bottom: 2px;
|
|
2238
2244
|
}
|
|
2239
|
-
/* MCP
|
|
2240
|
-
.settings-integrations-controls { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin: 18px 0; }
|
|
2245
|
+
/* MCP and Skills share native settings controls while keeping separate flows. */
|
|
2246
|
+
.settings-integrations-controls { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin: 18px 0 12px; }
|
|
2241
2247
|
.settings-integrations-controls input { flex: 1; min-width: 180px; }
|
|
2242
2248
|
.settings-integrations-controls input[hidden] { display: none; }
|
|
2243
2249
|
.settings-integrations-controls select, .settings-integrations-controls input,
|
|
2244
|
-
.settings-integrations-field input, .settings-integrations-
|
|
2250
|
+
.settings-integrations-field input, .settings-integrations-toolbar input { box-sizing: border-box; padding: 9px 11px; border: 1px solid var(--settings-border, #8885); border-radius: 8px; color: inherit; background: var(--settings-input, transparent); font: inherit; }
|
|
2251
|
+
.settings-integrations-status { min-height: 18px; margin: 0 0 6px; color: var(--settings-muted); font-size: 12px; }
|
|
2245
2252
|
.settings-integrations-body p { font-size: 12px; line-height: 1.6; opacity: .8; }
|
|
2246
|
-
.settings-integrations-
|
|
2253
|
+
.settings-integrations-toolbar { display: flex; gap: 10px; align-items: center; margin: 8px 0 16px; }
|
|
2254
|
+
.settings-integrations-toolbar input { flex: 1; }
|
|
2255
|
+
.settings-integrations-list { display: grid; gap: 8px; }
|
|
2256
|
+
.settings-integrations-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; border: 1px solid var(--settings-border, #8885); padding: 14px; border-radius: 10px; background: var(--settings-surface, transparent); }
|
|
2247
2257
|
.settings-integrations-row > div { flex: 1; min-width: 180px; }
|
|
2248
|
-
.settings-integrations-row
|
|
2249
|
-
.settings-integrations-
|
|
2258
|
+
.settings-integrations-row p { margin: 3px 0; }
|
|
2259
|
+
.settings-integrations-row code { display: block; overflow-wrap: anywhere; font-size: 11px; color: var(--settings-muted); }
|
|
2260
|
+
.settings-integrations-row--native { border-style: dashed; }
|
|
2261
|
+
.settings-integrations-empty { padding: 36px 18px; border: 1px dashed var(--settings-border, #8885); border-radius: 10px; text-align: center; color: var(--settings-muted); font-size: 12px; }
|
|
2262
|
+
.settings-integrations-editor-head { display: flex; align-items: center; gap: 12px; }
|
|
2263
|
+
.settings-integrations-editor-head h3 { margin: 0; }
|
|
2264
|
+
.settings-integrations-form { display: grid; gap: 14px; margin: 16px 0 28px; }
|
|
2265
|
+
.settings-integrations-form--card { padding: 16px; border: 1px solid var(--settings-border, #8885); border-radius: 12px; }
|
|
2250
2266
|
.settings-integrations-field { display: grid; gap: 6px; font-size: 12px; }
|
|
2251
2267
|
.settings-integrations-form > button { justify-self: start; }
|
|
2268
|
+
.settings-mcp-args { display: grid; gap: 7px; }
|
|
2269
|
+
.settings-mcp-arg { display: flex; gap: 8px; align-items: center; }
|
|
2270
|
+
.settings-mcp-arg input { flex: 1; }
|
|
2271
|
+
.settings-mcp-kv-row { display: flex; gap: 8px; align-items: center; }
|
|
2272
|
+
.settings-mcp-kv-row input { flex: 1; }
|
|
2273
|
+
.settings-mcp-add-arg { margin-top: 7px; }
|
|
2274
|
+
.settings-mcp-docs-link { display: inline-flex; align-items: center; gap: 4px; color: var(--settings-muted, #888); font-size: 13px; text-decoration: none; transition: color 150ms ease; }
|
|
2275
|
+
.settings-mcp-docs-link:hover { color: var(--settings-text, #fff); text-decoration: underline; }
|
|
2276
|
+
.settings-integrations-editor-head .settings-command-button--danger { margin-left: auto; }
|
|
2277
|
+
.settings-mcp-type-toggle { display: inline-flex; padding: 3px; border-radius: 8px; background: var(--settings-inset, rgba(255, 255, 255, 0.06)); border: 1px solid var(--settings-border, #8885); gap: 2px; width: fit-content; }
|
|
2278
|
+
.settings-mcp-type-btn { padding: 5px 14px; border-radius: 6px; border: none; background: transparent; color: var(--settings-muted, #888); font: inherit; font-size: 12px; font-weight: 500; cursor: pointer; transition: all 120ms ease; }
|
|
2279
|
+
.settings-mcp-type-btn:hover { color: var(--settings-text, #fff); }
|
|
2280
|
+
.settings-mcp-type-btn[data-active="true"] { background: var(--settings-active, rgba(255, 255, 255, 0.15)); color: var(--settings-text, #fff); font-weight: 600; }
|
|
2281
|
+
.settings-mcp-notice { margin: 4px 0 0; color: var(--settings-muted, #888); font-size: 12px; line-height: 1.5; }
|
|
2282
|
+
.settings-mcp-section { display: grid; gap: 14px; }
|
|
2283
|
+
.settings-mcp-icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; padding: 0; border: 1px solid var(--settings-border, #8885); border-radius: 8px; background: var(--settings-input, transparent); color: var(--settings-muted, #888); cursor: pointer; flex: none; transition: all 120ms ease; }
|
|
2284
|
+
.settings-mcp-icon-btn:hover:not(:disabled) { background: rgba(239, 68, 68, 0.15); border-color: rgba(239, 68, 68, 0.4); color: #f87171; }
|
|
2285
|
+
.settings-mcp-icon-btn svg { width: 14px; height: 14px; display: block; }
|
|
2286
|
+
.settings-integrations-callout { margin: 0; padding: 10px 12px; border-radius: 8px; background: var(--settings-inset); }
|
|
2287
|
+
.settings-skill-dropzone { display: grid; justify-items: center; gap: 8px; padding: 34px 22px; margin: 8px 0 18px; border: 1.5px dashed var(--settings-border, #8887); border-radius: 14px; background: var(--settings-inset); text-align: center; transition: border-color 120ms ease, background 120ms ease, transform 120ms ease; }
|
|
2288
|
+
.settings-skill-dropzone > span { max-width: 520px; color: var(--settings-muted); font-size: 12px; line-height: 18px; }
|
|
2289
|
+
.settings-skill-dropzone[data-drag-over="true"] { border-color: var(--settings-accent, #10a37f); background: var(--settings-accent-subtle, rgba(16, 163, 127, .1)); transform: scale(.995); }
|
|
2290
|
+
.settings-skill-dropzone[data-disabled="true"] { opacity: .55; }
|
|
2291
|
+
.settings-skills-target-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; justify-content: center; margin-top: 6px; }
|
|
2292
|
+
.settings-skills-target-row select { padding: 6px 10px; border: 1px solid var(--settings-border, #8885); border-radius: 8px; background: var(--settings-input, transparent); color: inherit; font: inherit; font-size: 12px; }
|
|
2293
|
+
.settings-skills-overview-stats { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin: 6px 0 10px; font-size: 12px; color: var(--settings-muted); }
|
|
2294
|
+
.settings-skills-tabs { display: inline-flex; padding: 3px; border-radius: 8px; background: var(--settings-inset, rgba(255, 255, 255, 0.06)); border: 1px solid var(--settings-border, #8885); gap: 2px; }
|
|
2295
|
+
.settings-skills-tab-btn { padding: 5px 14px; border-radius: 6px; border: none; background: transparent; color: var(--settings-muted, #888); font: inherit; font-size: 12px; font-weight: 500; cursor: pointer; transition: all 120ms ease; }
|
|
2296
|
+
.settings-skills-tab-btn:hover { color: var(--settings-text, #fff); }
|
|
2297
|
+
.settings-skills-tab-btn[data-active="true"] { background: var(--settings-active, rgba(255, 255, 255, 0.15)); color: var(--settings-text, #fff); font-weight: 600; }
|
|
2298
|
+
.settings-skills-overview-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 14px; margin-top: 10px; }
|
|
2299
|
+
.settings-skills-harness-card { border: 1px solid var(--settings-border, #8885); border-radius: 12px; padding: 14px; background: var(--settings-surface, transparent); display: flex; flex-direction: column; gap: 10px; }
|
|
2300
|
+
.settings-skills-harness-head { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--settings-border, #8883); padding-bottom: 8px; }
|
|
2301
|
+
.settings-skills-harness-head strong { font-size: 14px; }
|
|
2302
|
+
.settings-skills-badge { font-size: 11px; padding: 2px 8px; border-radius: 10px; background: var(--settings-inset, rgba(255, 255, 255, 0.08)); color: var(--settings-muted); }
|
|
2303
|
+
.settings-skills-badge--active { background: rgba(16, 163, 127, 0.15); color: #10a37f; }
|
|
2304
|
+
.settings-skills-list-compact { display: flex; flex-direction: column; gap: 8px; max-height: 280px; overflow-y: auto; }
|
|
2305
|
+
.settings-skills-item-row { display: flex; justify-content: space-between; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 6px; background: var(--settings-inset, rgba(255, 255, 255, 0.03)); font-size: 12px; }
|
|
2306
|
+
.settings-skills-item-row strong { font-size: 13px; }
|
|
2307
|
+
.settings-skills-item-row code { font-size: 10px; color: var(--settings-muted); overflow-wrap: anywhere; display: block; }
|
|
2308
|
+
.settings-skills-item-actions { display: flex; gap: 6px; align-items: center; flex: none; }
|
|
2309
|
+
.settings-skills-matrix-list { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }
|
|
2310
|
+
.settings-skills-matrix-card { border: 1px solid var(--settings-border, #8885); border-radius: 10px; padding: 12px 14px; background: var(--settings-surface, transparent); display: flex; flex-direction: column; gap: 8px; }
|
|
2311
|
+
.settings-skills-matrix-head { display: flex; justify-content: space-between; align-items: center; }
|
|
2312
|
+
.settings-skills-matrix-head span { font-size: 11px; color: var(--settings-muted); }
|
|
2313
|
+
.settings-skills-tags { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
|
|
2314
|
+
.settings-skills-tag { font-size: 11px; padding: 3px 8px; border-radius: 6px; background: rgba(16, 163, 127, 0.15); color: #10a37f; border: 1px solid rgba(16, 163, 127, 0.3); }
|
|
2315
|
+
.settings-skills-sync-actions { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 4px; }
|
|
2316
|
+
.settings-skills-sync-modal { border: 1px solid var(--settings-border, #8885); border-radius: 12px; background: var(--settings-bg, #1e1e1e); color: inherit; padding: 20px 24px; max-width: 420px; box-shadow: 0 16px 32px rgba(0, 0, 0, 0.5); }
|
|
2317
|
+
.settings-skills-sync-modal::backdrop { background: rgba(0, 0, 0, 0.6); backdrop-filter: blur(2px); }
|
|
2318
|
+
.settings-skills-sync-modal h3 { margin: 0 0 8px; font-size: 16px; }
|
|
2319
|
+
.settings-skills-sync-modal p { margin: 0 0 12px; font-size: 12px; color: var(--settings-muted); }
|
|
2320
|
+
.settings-mcp-overview-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 14px; margin-top: 10px; }
|
|
2321
|
+
.settings-mcp-harness-card { border: 1px solid var(--settings-border, #8885); border-radius: 12px; padding: 14px; background: var(--settings-surface, transparent); display: flex; flex-direction: column; gap: 10px; }
|
|
2322
|
+
.settings-mcp-harness-head { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--settings-border, #8883); padding-bottom: 8px; }
|
|
2323
|
+
.settings-mcp-harness-head strong { font-size: 14px; }
|
|
2324
|
+
.settings-mcp-native-box { padding: 8px 10px; border-radius: 8px; background: rgba(16, 163, 127, 0.08); border: 1px dashed rgba(16, 163, 127, 0.3); font-size: 11px; display: flex; flex-direction: column; gap: 4px; }
|
|
2325
|
+
.settings-mcp-native-title { font-weight: 600; color: #10a37f; display: flex; align-items: center; gap: 6px; }
|
|
2326
|
+
.settings-mcp-native-item { display: flex; flex-direction: column; gap: 2px; color: var(--settings-muted); }
|
|
2327
|
+
.settings-mcp-native-item code { font-size: 10px; overflow-wrap: anywhere; }
|
|
2328
|
+
.settings-mcp-list-compact { display: flex; flex-direction: column; gap: 8px; max-height: 280px; overflow-y: auto; }
|
|
2329
|
+
.settings-mcp-item-row { display: flex; justify-content: space-between; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 6px; background: var(--settings-inset, rgba(255, 255, 255, 0.03)); font-size: 12px; }
|
|
2330
|
+
.settings-mcp-item-row strong { font-size: 13px; }
|
|
2331
|
+
.settings-mcp-item-row code { font-size: 10px; color: var(--settings-muted); overflow-wrap: anywhere; display: block; }
|
|
2332
|
+
.settings-mcp-item-actions { display: flex; gap: 6px; align-items: center; flex: none; }
|
|
2333
|
+
.settings-mcp-card-footer { display: flex; justify-content: flex-end; margin-top: auto; padding-top: 6px; border-top: 1px solid var(--settings-border, #8882); }
|
|
2334
|
+
.settings-mcp-matrix-list { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }
|
|
2335
|
+
.settings-mcp-matrix-card { border: 1px solid var(--settings-border, #8885); border-radius: 10px; padding: 12px 14px; background: var(--settings-surface, transparent); display: flex; flex-direction: column; gap: 8px; }
|
|
2336
|
+
.settings-mcp-matrix-head { display: flex; justify-content: space-between; align-items: center; }
|
|
2337
|
+
.settings-mcp-matrix-head span { font-size: 11px; color: var(--settings-muted); }
|
|
2338
|
+
.settings-mcp-tags { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
|
|
2339
|
+
.settings-mcp-tag { font-size: 11px; padding: 3px 8px; border-radius: 6px; background: rgba(16, 163, 127, 0.15); color: #10a37f; border: 1px solid rgba(16, 163, 127, 0.3); }
|
|
2340
|
+
.settings-mcp-tag--disabled { background: rgba(255, 255, 255, 0.05); color: var(--settings-muted); border-color: var(--settings-border, #8885); }
|
|
2341
|
+
.settings-mcp-sync-actions { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 4px; }
|
|
2342
|
+
.settings-mcp-distribute-modal { border: 1px solid var(--settings-border, #8885); border-radius: 12px; background: var(--settings-bg, #1e1e1e); color: inherit; padding: 20px 24px; max-width: 540px; width: 92%; max-height: 85vh; overflow-y: auto; box-shadow: 0 16px 32px rgba(0, 0, 0, 0.5); }
|
|
2343
|
+
.settings-mcp-distribute-modal::backdrop { background: rgba(0, 0, 0, 0.6); backdrop-filter: blur(2px); }
|
|
2344
|
+
.settings-mcp-distribute-modal h3 { margin: 0 0 8px; font-size: 16px; }
|
|
2345
|
+
.settings-mcp-distribute-modal p { margin: 0 0 12px; font-size: 12px; color: var(--settings-muted); }
|
|
2346
|
+
|
|
@@ -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
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { CHATGPT_CONTEXT_MAX_LENGTH, buildChatGptContextDraft, redactChatGptContextText, sanitizeChatGptSourceUrl } from "../src/renderer-chatgpt-context.js";
|
|
2
|
+
import { CHATGPT_CONTEXT_MAX_LENGTH, buildChatGptContextDraft, isChatGptQuickChatEditor, redactChatGptContextText, sanitizeChatGptSourceUrl } from "../src/renderer-chatgpt-context.js";
|
|
3
3
|
|
|
4
4
|
describe("ChatGPT context package", () => {
|
|
5
5
|
it("marks imported text as untrusted context", () => {
|
|
@@ -15,4 +15,7 @@ describe("ChatGPT context package", () => {
|
|
|
15
15
|
expect(sanitizeChatGptSourceUrl("https://chatgpt.com/c/abc?token=nope#part")).toBe("https://chatgpt.com/c/abc");
|
|
16
16
|
expect(sanitizeChatGptSourceUrl("http://chatgpt.com/c/abc")).toBeUndefined(); expect(sanitizeChatGptSourceUrl("https://example.com/c/abc")).toBeUndefined();
|
|
17
17
|
});
|
|
18
|
+
it("ignores partial DOM fixtures that are not browser Elements", () => {
|
|
19
|
+
expect(isChatGptQuickChatEditor({} as Element)).toBe(false);
|
|
20
|
+
});
|
|
18
21
|
});
|
|
@@ -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
|
|
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
|
+
});
|
|
@@ -23,8 +23,10 @@ describe("Renderer settings foundation", () => {
|
|
|
23
23
|
expect(pages.map(({ label }) => label)).toEqual([
|
|
24
24
|
"Connections",
|
|
25
25
|
"Accounts",
|
|
26
|
-
"MCP
|
|
26
|
+
"MCP",
|
|
27
|
+
"Skills",
|
|
27
28
|
"Session Import",
|
|
29
|
+
"Storage & verification",
|
|
28
30
|
"Updates",
|
|
29
31
|
"About",
|
|
30
32
|
]);
|
|
@@ -33,6 +35,8 @@ describe("Renderer settings foundation", () => {
|
|
|
33
35
|
"accounts",
|
|
34
36
|
"connections",
|
|
35
37
|
"session-import",
|
|
38
|
+
"session-import",
|
|
39
|
+
"storage",
|
|
36
40
|
"updates",
|
|
37
41
|
"about",
|
|
38
42
|
]);
|
|
@@ -65,8 +69,10 @@ describe("Renderer settings foundation", () => {
|
|
|
65
69
|
expect(pages.map(({ id }) => id)).toEqual([
|
|
66
70
|
"connections",
|
|
67
71
|
"accounts",
|
|
68
|
-
"
|
|
72
|
+
"mcp",
|
|
73
|
+
"skills",
|
|
69
74
|
"session-import",
|
|
75
|
+
"storage",
|
|
70
76
|
"updates",
|
|
71
77
|
"about",
|
|
72
78
|
]);
|