@ericsanchezok/synergy-workbench 0.0.0-stage → 3.1.1

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 (153) hide show
  1. package/AGENTS.md +30 -0
  2. package/README.md +16 -2
  3. package/dist/agents.js +11 -0
  4. package/dist/cli-adapter.js +8 -0
  5. package/dist/component.js +65 -0
  6. package/dist/config-schema.js +265 -0
  7. package/dist/execution/activity.js +78 -0
  8. package/dist/execution/content.js +184 -0
  9. package/dist/execution/json-sections.js +231 -0
  10. package/dist/execution/presentation.js +79 -0
  11. package/dist/execution/preview.js +13 -0
  12. package/dist/execution/process.js +125 -0
  13. package/dist/execution/projection.js +327 -0
  14. package/dist/execution/query.js +18 -0
  15. package/dist/execution/routes.js +189 -0
  16. package/dist/execution/schema.js +322 -0
  17. package/dist/execution/service.js +1993 -0
  18. package/dist/features.js +22 -0
  19. package/dist/http.js +65 -0
  20. package/dist/performance/analysis.js +330 -0
  21. package/dist/performance/catalog.js +391 -0
  22. package/dist/performance/dashboard.js +483 -0
  23. package/dist/performance/error.js +15 -0
  24. package/dist/performance/index.js +6 -0
  25. package/dist/performance/inflight.js +43 -0
  26. package/dist/performance/projection.js +138 -0
  27. package/dist/performance/prompt/base.txt +38 -0
  28. package/dist/performance/prompt/builder.js +21 -0
  29. package/dist/performance/routes/performance-route.js +439 -0
  30. package/dist/performance/schema.js +628 -0
  31. package/dist/performance/timeline.js +98 -0
  32. package/dist/performance/trace-detail.js +91 -0
  33. package/dist/presentation/service.js +270 -0
  34. package/dist/presentation/tools/workbench-view.js +65 -0
  35. package/dist/presentation/tools.js +10 -0
  36. package/dist/project/directories.js +363 -0
  37. package/dist/project/git-health.js +498 -0
  38. package/dist/project/ide.js +66 -0
  39. package/dist/project/migration.js +22 -0
  40. package/dist/project/routes/directories.js +89 -0
  41. package/dist/project/routes/git.js +30 -0
  42. package/dist/project/routes/task-defaults.js +42 -0
  43. package/dist/project/session-health.js +9 -0
  44. package/dist/project/startup.js +28 -0
  45. package/dist/project/task-defaults.js +68 -0
  46. package/dist/project/tools/session-control.js +664 -0
  47. package/dist/project/tools/session-control.txt +60 -0
  48. package/dist/project/tools/worktree-archive.js +76 -0
  49. package/dist/project/tools/worktree-enter.js +175 -0
  50. package/dist/project/tools/worktree-leave.js +119 -0
  51. package/dist/project/tools/worktree-list.js +52 -0
  52. package/dist/project/tools.js +26 -0
  53. package/dist/project/vcs-branch-watcher.js +76 -0
  54. package/dist/project/vcs.js +114 -0
  55. package/dist/project/worktree-janitor.js +140 -0
  56. package/dist/project/worktrees.js +98 -0
  57. package/dist/push/bridge.js +171 -0
  58. package/dist/push/index.js +4 -0
  59. package/dist/push/routes/push.js +224 -0
  60. package/dist/push/service.js +93 -0
  61. package/dist/push/store.js +121 -0
  62. package/dist/push/types.js +58 -0
  63. package/dist/review/git.js +226 -0
  64. package/dist/review/routes.js +45 -0
  65. package/dist/review/schema.js +32 -0
  66. package/dist/review/state.js +79 -0
  67. package/dist/stats/aggregator.js +252 -0
  68. package/dist/stats/canonical.js +89 -0
  69. package/dist/stats/cli/stats.js +302 -0
  70. package/dist/stats/engine.js +176 -0
  71. package/dist/stats/index.js +5 -0
  72. package/dist/stats/rollup.js +460 -0
  73. package/dist/stats/routes/stats.js +94 -0
  74. package/dist/stats/routes/usage.js +83 -0
  75. package/dist/stats/storage.js +86 -0
  76. package/dist/stats/types.js +267 -0
  77. package/dist/worker.js +6 -0
  78. package/package.json +232 -4
  79. package/src/agents.ts +15 -0
  80. package/src/cli-adapter.ts +11 -0
  81. package/src/component.ts +73 -0
  82. package/src/config-schema.ts +289 -0
  83. package/src/execution/activity.ts +83 -0
  84. package/src/execution/content.ts +189 -0
  85. package/src/execution/json-sections.ts +230 -0
  86. package/src/execution/presentation.ts +91 -0
  87. package/src/execution/preview.ts +12 -0
  88. package/src/execution/process.ts +118 -0
  89. package/src/execution/projection.ts +330 -0
  90. package/src/execution/query.ts +19 -0
  91. package/src/execution/routes.ts +324 -0
  92. package/src/execution/schema.ts +336 -0
  93. package/src/execution/service.ts +2149 -0
  94. package/src/features.ts +22 -0
  95. package/src/http.ts +75 -0
  96. package/src/performance/analysis.ts +356 -0
  97. package/src/performance/catalog.ts +902 -0
  98. package/src/performance/dashboard.ts +521 -0
  99. package/src/performance/error.ts +32 -0
  100. package/src/performance/index.ts +6 -0
  101. package/src/performance/inflight.ts +46 -0
  102. package/src/performance/projection.ts +143 -0
  103. package/src/performance/prompt/base.txt +38 -0
  104. package/src/performance/prompt/builder.ts +24 -0
  105. package/src/performance/routes/performance-route.ts +541 -0
  106. package/src/performance/schema.ts +695 -0
  107. package/src/performance/timeline.ts +100 -0
  108. package/src/performance/trace-detail.ts +97 -0
  109. package/src/presentation/service.ts +303 -0
  110. package/src/presentation/tools/workbench-view.ts +75 -0
  111. package/src/presentation/tools.ts +11 -0
  112. package/src/project/directories.ts +391 -0
  113. package/src/project/git-health.ts +577 -0
  114. package/src/project/ide.ts +80 -0
  115. package/src/project/migration.ts +24 -0
  116. package/src/project/routes/directories.ts +124 -0
  117. package/src/project/routes/git.ts +42 -0
  118. package/src/project/routes/task-defaults.ts +50 -0
  119. package/src/project/session-health.ts +10 -0
  120. package/src/project/startup.ts +28 -0
  121. package/src/project/task-defaults.ts +70 -0
  122. package/src/project/tools/session-control.ts +725 -0
  123. package/src/project/tools/session-control.txt +60 -0
  124. package/src/project/tools/worktree-archive.ts +92 -0
  125. package/src/project/tools/worktree-enter.ts +227 -0
  126. package/src/project/tools/worktree-leave.ts +140 -0
  127. package/src/project/tools/worktree-list.ts +68 -0
  128. package/src/project/tools.ts +29 -0
  129. package/src/project/vcs-branch-watcher.ts +81 -0
  130. package/src/project/vcs.ts +124 -0
  131. package/src/project/worktree-janitor.ts +153 -0
  132. package/src/project/worktrees.ts +110 -0
  133. package/src/push/bridge.ts +182 -0
  134. package/src/push/index.ts +4 -0
  135. package/src/push/routes/push.ts +267 -0
  136. package/src/push/service.ts +108 -0
  137. package/src/push/store.ts +120 -0
  138. package/src/push/types.ts +67 -0
  139. package/src/review/git.ts +221 -0
  140. package/src/review/routes.ts +65 -0
  141. package/src/review/schema.ts +34 -0
  142. package/src/review/state.ts +77 -0
  143. package/src/stats/aggregator.ts +277 -0
  144. package/src/stats/canonical.ts +93 -0
  145. package/src/stats/cli/stats.ts +348 -0
  146. package/src/stats/engine.ts +197 -0
  147. package/src/stats/index.ts +5 -0
  148. package/src/stats/rollup.ts +522 -0
  149. package/src/stats/routes/stats.ts +107 -0
  150. package/src/stats/routes/usage.ts +103 -0
  151. package/src/stats/storage.ts +78 -0
  152. package/src/stats/types.ts +351 -0
  153. package/src/worker.ts +7 -0
package/AGENTS.md ADDED
@@ -0,0 +1,30 @@
1
+ # workbench Package
2
+
3
+ Product Projects, statistics and performance read models, activity presentation and notifications. Keep product read models out of core execution accounting. Session, rollout and retained usage evidence remain canonical in Harness. Statistics routes and compatibility projections consume the public usage service; keep accounting formulas in that owner. Read the root AGENTS.md and the owning architecture document before changes.
4
+
5
+ File review Git comparisons and revisioned session notes belong in `src/review`. Keep comparisons read-only, fence workspace generations and content versions, and use retained Harness snapshots for historical versions. Verify `bun test test/review`, regenerate the SDK after schema changes, and follow [File review](../../docs/reference/file-review.md).
6
+
7
+ - Keep domain tools, routes, configuration and migrations with their implementation.
8
+ - Own configuration schemas, normalization, reference checks and secret handling in `src/config-schema.ts`; consumers use its typed reader and the host composes its registration.
9
+ - Import other packages only through declared public exports; preserve cancellation, permissions and persisted data.
10
+ - Tests live under test/ and use isolated homes through the testing support package.
11
+
12
+ Run bun run typecheck and the affected tests, then the root package and dependency checks.
13
+
14
+ Keep stable Workbench availability separate from the default-off `kanban` restart experiment declared in `./features`. The board's UI and subscriptions use its own capability; statistics and other Workbench consumers remain independent. Follow [feature lifecycle](../../docs/architecture/feature-lifecycle.md).
15
+
16
+ Project directory configuration, explicit sharing and the central migration belong to `src/project/directories.ts` and `src/project/migration.ts`. Preserve Scope identity and historical Worktree sources when changing the main folder. Run `bun test test/project/directories.test.ts` for creation, revision, busy-resource, migration and multi-repository behavior.
17
+
18
+ Project task defaults belong to `src/project/task-defaults.ts` and its scoped HTTP adapter. Verify project-only writes and concurrent edits with `bun test test/project/task-defaults.test.ts`; regenerate the SDK after changing its schemas.
19
+
20
+ The managed-worktree janitor schedules per Scope and drains active sweeps during disposal. Run `bun test test/project/worktree-janitor.test.ts` for scheduler ownership changes.
21
+
22
+ Expose composition through `./component`; keep registration side-effect free until the factory is selected. Declare required components, optional ordering, worker roles and lazy HTTP adapters explicitly. Runtime-scoped reload and lifecycle contributions must preserve isolated instances and failed-start cleanup.
23
+
24
+ Keep published `synergy` component metadata aligned with the factory version, requirements and packaged entry. Component CLI contributions belong in `src/cli-adapter.ts` when this owner supplies commands; keep their handlers lazy and independent of the complete product.
25
+
26
+ Task execution presentation belongs in `src/execution`. Use public Harness Rollout and usage queries and the public `context` reconciliation; keep evidence storage layouts and accounting formulas in Harness. Verify `bun test test/execution`, regenerate the SDK after schema changes, and preserve Scope/round/descendant checks, lazy content and revisioned updates.
27
+
28
+ Execution projections implement the Server contribution contract. Compute detail updates only for retained subscribers or explicit legacy clients; invalidation must not refresh LRU activity. Keep snapshot revisions and field/context deltas independently recoverable and bounded.
29
+
30
+ Existing-resource presentation belongs in `src/presentation`. Use the shared util `WorkbenchView` protocol and Server's public event-client contribution, never Bus/history-driven effects or a second socket. Keep Session/Scope routing, captured Workspace generations, same-connection receipts, identity-based applied validation and Runtime-only deduplication. Verify `bun test test/tool/workbench-view.test.ts test/presentation/service.test.ts`; callback-bound broker tests are unit evidence, not frontend acceptance.
package/README.md CHANGED
@@ -1,3 +1,17 @@
1
- # Temporary Holding Version
1
+ # Workbench
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Product Projects, statistics and performance read models, activity presentation, task execution details and notifications.
4
+
5
+ Keep product read models out of core execution accounting. Session and rollout evidence remain canonical in Harness.
6
+
7
+ Cross-package imports use the explicit entries in `package.json`. Tests and fixtures belong in this package’s `test/` tree. Full API composition tests belong in `presets/test/`.
8
+
9
+ Run `bun run typecheck`, `bun run test`, and `bun run test:coverage` from this package. Build an importable library with `bun script/build-workspace.ts packages/workbench` from the repository root; `bun script/pack-workspace.ts packages/workbench .artifacts/packages` builds its workspace dependency closure.
10
+
11
+ Task execution queries live in `src/execution` and consume public Harness Rollout and usage exports. The scoped session API exposes summary, process/records trajectory, type-specific node detail and versioned content ranges/sections/search/download. Financial presentation is shared with statistics; formulas and immutable prices remain in Harness. Verify ownership, lifecycle, paging, grouped purposes, retained children and large content with `bun test test/execution`.
12
+
13
+ `workbench_view` presents existing Notes, Workspace files, Browser pages and immutable Assets through `src/presentation`. It does not create resources, navigate pages or perform arbitrary UI actions. Only protocol-ready, target-supporting frontends actively displaying the caller's Scope and Session are eligible. Multiple eligible frontends require an explicit returned `clientId`; no frontend is elected or broadcast to. File opens capture the caller Session's valid Workspace identity and generation, never the viewer's directory.
14
+
15
+ Presentation waits for a matching receipt from the selected connection. `applied` opens include the requested visible resource; hides confirm the known view identity as hidden. Resource identity excludes location. Content can remain `loading` after presentation is applied. The default receipt window is 30 seconds; `timeoutSeconds` accepts any positive finite duration without a product maximum. Cancellation after dispatch, active-view changes, send failure, disconnection and missing receipts leave the effect `unknown`, with cancellation sent when possible and no automatic retry.
16
+
17
+ Tool-call identities deduplicate within the Runtime and reject changed arguments. `status` with `requestId` reads only the same Scope/Session's retained confirmed receipt; without one it asks an eligible frontend for current views. Disconnect releases views and receipts, retaining only an in-memory no-replay identity tombstone until Runtime disposal. No command queue or durable state is added. Verify the real socket/tool cases with `bun test test/tool/workbench-view.test.ts` and broker failure/lifecycle units with `bun test test/presentation/service.test.ts`.
package/dist/agents.js ADDED
@@ -0,0 +1,11 @@
1
+ import { createPerformanceAnalystAgent } from "./performance/prompt/builder.js";
2
+ import { AgentBuiltins } from "@ericsanchezok/synergy-harness/agent/builtins";
3
+ export function createDomainAgents(ctx) {
4
+ const performanceAnalyst = createPerformanceAnalystAgent(ctx);
5
+ return {
6
+ [performanceAnalyst.name]: performanceAnalyst,
7
+ };
8
+ }
9
+ export function registerWorkbenchAgents() {
10
+ AgentBuiltins.register("workbench", createDomainAgents);
11
+ }
@@ -0,0 +1,8 @@
1
+ export const commands = [
2
+ {
3
+ command: "stats",
4
+ describe: "show token usage and cost statistics",
5
+ storage: "maintenance",
6
+ load: async () => (await import("./stats/cli/stats.js")).StatsCommand,
7
+ },
8
+ ];
@@ -0,0 +1,65 @@
1
+ import { componentFeatures } from "./features.js";
2
+ import { BusEvent } from "@ericsanchezok/synergy-harness/bus/bus-event";
3
+ import { Vcs } from "./project/vcs.js";
4
+ import { Ide } from "./project/ide.js";
5
+ import { ReviewState } from "./review/state.js";
6
+ import { ExecutionSchema } from "./execution/schema.js";
7
+ import { registerProjectMigrations } from "./project/migration.js";
8
+ import { PushBridge } from "./push/bridge.js";
9
+ import { version } from "../package.json" with { type: "json" };
10
+ import { registerWorkbenchAgents } from "./agents.js";
11
+ import { registerProjectTools } from "./project/tools.js";
12
+ import { registerProjectStartup } from "./project/startup.js";
13
+ import { registerProjectSessionHealth } from "./project/session-health.js";
14
+ import { registerConfig } from "./config-schema.js";
15
+ import { Usage } from "@ericsanchezok/synergy-harness/usage";
16
+ import { ExecutionService } from "./execution/service.js";
17
+ import { WorkbenchPresentation } from "./presentation/service.js";
18
+ import { registerPresentationTools } from "./presentation/tools.js";
19
+ export function workbench() {
20
+ return {
21
+ id: "workbench",
22
+ features: componentFeatures,
23
+ apiVersion: 1,
24
+ version,
25
+ requires: { "local-runtime": version },
26
+ workers: { agent: new URL("./worker.js", import.meta.url) },
27
+ services() {
28
+ let dispose;
29
+ let stopUsage;
30
+ let stopExecution;
31
+ let stopPresentation;
32
+ return {
33
+ resident: {
34
+ async start() {
35
+ dispose = PushBridge.init();
36
+ stopUsage = Usage.service();
37
+ stopExecution = ExecutionService.init();
38
+ stopPresentation = WorkbenchPresentation.init();
39
+ },
40
+ async stop() {
41
+ stopPresentation?.();
42
+ stopPresentation = undefined;
43
+ await stopUsage?.();
44
+ stopExecution?.();
45
+ stopUsage = undefined;
46
+ dispose?.();
47
+ dispose = undefined;
48
+ await PushBridge.flush();
49
+ },
50
+ },
51
+ };
52
+ },
53
+ adapters: { cli: new URL("./cli-adapter.js", import.meta.url), http: new URL("./http.js", import.meta.url) },
54
+ register() {
55
+ BusEvent.register(...Object.values(Vcs.Event), ...Object.values(Ide.Event), ReviewState.Updated, ExecutionSchema.Updated, ExecutionSchema.ProjectionUpdated);
56
+ registerConfig();
57
+ registerProjectMigrations();
58
+ registerWorkbenchAgents();
59
+ registerProjectTools();
60
+ registerPresentationTools();
61
+ registerProjectStartup();
62
+ registerProjectSessionHealth();
63
+ },
64
+ };
65
+ }
@@ -0,0 +1,265 @@
1
+ import { ConfigDomain } from "@ericsanchezok/synergy-harness/config/domain";
2
+ import z from "zod";
3
+ import { ConfigExtensions } from "@ericsanchezok/synergy-harness/config/extensions";
4
+ export const Keybinds = z
5
+ .object({
6
+ leader: z.string().optional().default("ctrl+x").describe("Leader key for keybind combinations"),
7
+ app_exit: z.string().optional().default("ctrl+c,ctrl+d,<leader>q").describe("Exit the application"),
8
+ editor_open: z.string().optional().default("<leader>e").describe("Open external editor"),
9
+ theme_list: z.string().optional().default("<leader>t").describe("List available themes"),
10
+ sidebar_toggle: z.string().optional().default("<leader>b").describe("Toggle sidebar"),
11
+ scrollbar_toggle: z.string().optional().default("none").describe("Toggle session scrollbar"),
12
+ username_toggle: z.string().optional().default("none").describe("Toggle username visibility"),
13
+ status_view: z.string().optional().default("<leader>s").describe("View status"),
14
+ session_export: z.string().optional().default("<leader>x").describe("Export session to editor"),
15
+ session_new: z.string().optional().default("<leader>n").describe("Create a new session"),
16
+ session_list: z.string().optional().default("<leader>l").describe("List all sessions"),
17
+ session_timeline: z.string().optional().default("<leader>g").describe("Show session timeline"),
18
+ session_fork: z.string().optional().default("none").describe("Fork session from message"),
19
+ session_rename: z.string().optional().default("none").describe("Rename session"),
20
+ session_interrupt: z.string().optional().default("escape").describe("Interrupt current session"),
21
+ session_compact: z.string().optional().default("<leader>c").describe("Compact the session"),
22
+ messages_page_up: z.string().optional().default("pageup").describe("Scroll messages up by one page"),
23
+ messages_page_down: z.string().optional().default("pagedown").describe("Scroll messages down by one page"),
24
+ messages_half_page_up: z.string().optional().default("ctrl+alt+u").describe("Scroll messages up by half page"),
25
+ messages_half_page_down: z.string().optional().default("ctrl+alt+d").describe("Scroll messages down by half page"),
26
+ messages_first: z.string().optional().default("ctrl+g,home").describe("Navigate to first message"),
27
+ messages_last: z.string().optional().default("ctrl+alt+g,end").describe("Navigate to last message"),
28
+ messages_next: z.string().optional().default("none").describe("Navigate to next message"),
29
+ messages_previous: z.string().optional().default("none").describe("Navigate to previous message"),
30
+ messages_last_user: z.string().optional().default("none").describe("Navigate to last user message"),
31
+ messages_copy: z.string().optional().default("<leader>y").describe("Copy message"),
32
+ messages_undo: z.string().optional().default("<leader>u").describe("Undo message history only"),
33
+ messages_redo: z.string().optional().default("<leader>r").describe("Redo message history only"),
34
+ messages_toggle_conceal: z
35
+ .string()
36
+ .optional()
37
+ .default("<leader>h")
38
+ .describe("Toggle code block concealment in messages"),
39
+ tool_details: z.string().optional().default("none").describe("Toggle tool details visibility"),
40
+ model_list: z.string().optional().default("<leader>m").describe("List available models"),
41
+ model_cycle_recent: z.string().optional().default("f2").describe("Next recently used model"),
42
+ model_cycle_recent_reverse: z.string().optional().default("shift+f2").describe("Previous recently used model"),
43
+ model_cycle_favorite: z.string().optional().default("none").describe("Next favorite model"),
44
+ model_cycle_favorite_reverse: z.string().optional().default("none").describe("Previous favorite model"),
45
+ command_list: z.string().optional().default("ctrl+p").describe("List available commands"),
46
+ agent_list: z.string().optional().default("<leader>a").describe("List agents"),
47
+ agent_cycle: z.string().optional().default("tab").describe("Next agent"),
48
+ agent_cycle_reverse: z.string().optional().default("shift+tab").describe("Previous agent"),
49
+ variant_cycle: z.string().optional().default("ctrl+t").describe("Cycle model variants"),
50
+ input_clear: z.string().optional().default("ctrl+c").describe("Clear input field"),
51
+ input_paste: z.string().optional().default("ctrl+v").describe("Paste from clipboard"),
52
+ input_submit: z.string().optional().default("return").describe("Submit input"),
53
+ input_newline: z
54
+ .string()
55
+ .optional()
56
+ .default("shift+return,ctrl+return,alt+return,ctrl+j")
57
+ .describe("Insert newline in input"),
58
+ input_move_left: z.string().optional().default("left,ctrl+b").describe("Move cursor left in input"),
59
+ input_move_right: z.string().optional().default("right,ctrl+f").describe("Move cursor right in input"),
60
+ input_move_up: z.string().optional().default("up").describe("Move cursor up in input"),
61
+ input_move_down: z.string().optional().default("down").describe("Move cursor down in input"),
62
+ input_select_left: z.string().optional().default("shift+left").describe("Select left in input"),
63
+ input_select_right: z.string().optional().default("shift+right").describe("Select right in input"),
64
+ input_select_up: z.string().optional().default("shift+up").describe("Select up in input"),
65
+ input_select_down: z.string().optional().default("shift+down").describe("Select down in input"),
66
+ input_line_home: z.string().optional().default("ctrl+a").describe("Move to start of line in input"),
67
+ input_line_end: z.string().optional().default("ctrl+e").describe("Move to end of line in input"),
68
+ input_select_line_home: z.string().optional().default("ctrl+shift+a").describe("Select to start of line in input"),
69
+ input_select_line_end: z.string().optional().default("ctrl+shift+e").describe("Select to end of line in input"),
70
+ input_visual_line_home: z.string().optional().default("alt+a").describe("Move to start of visual line in input"),
71
+ input_visual_line_end: z.string().optional().default("alt+e").describe("Move to end of visual line in input"),
72
+ input_select_visual_line_home: z
73
+ .string()
74
+ .optional()
75
+ .default("alt+shift+a")
76
+ .describe("Select to start of visual line in input"),
77
+ input_select_visual_line_end: z
78
+ .string()
79
+ .optional()
80
+ .default("alt+shift+e")
81
+ .describe("Select to end of visual line in input"),
82
+ input_buffer_home: z.string().optional().default("home").describe("Move to start of buffer in input"),
83
+ input_buffer_end: z.string().optional().default("end").describe("Move to end of buffer in input"),
84
+ input_select_buffer_home: z
85
+ .string()
86
+ .optional()
87
+ .default("shift+home")
88
+ .describe("Select to start of buffer in input"),
89
+ input_select_buffer_end: z.string().optional().default("shift+end").describe("Select to end of buffer in input"),
90
+ input_delete_line: z.string().optional().default("ctrl+shift+d").describe("Delete line in input"),
91
+ input_delete_to_line_end: z.string().optional().default("ctrl+k").describe("Delete to end of line in input"),
92
+ input_delete_to_line_start: z.string().optional().default("ctrl+u").describe("Delete to start of line in input"),
93
+ input_backspace: z.string().optional().default("backspace,shift+backspace").describe("Backspace in input"),
94
+ input_delete: z.string().optional().default("ctrl+d,delete,shift+delete").describe("Delete character in input"),
95
+ input_undo: z.string().optional().default("ctrl+-,super+z").describe("Undo in input"),
96
+ input_redo: z.string().optional().default("ctrl+.,super+shift+z").describe("Redo in input"),
97
+ input_word_forward: z
98
+ .string()
99
+ .optional()
100
+ .default("alt+f,alt+right,ctrl+right")
101
+ .describe("Move word forward in input"),
102
+ input_word_backward: z
103
+ .string()
104
+ .optional()
105
+ .default("alt+b,alt+left,ctrl+left")
106
+ .describe("Move word backward in input"),
107
+ input_select_word_forward: z
108
+ .string()
109
+ .optional()
110
+ .default("alt+shift+f,alt+shift+right")
111
+ .describe("Select word forward in input"),
112
+ input_select_word_backward: z
113
+ .string()
114
+ .optional()
115
+ .default("alt+shift+b,alt+shift+left")
116
+ .describe("Select word backward in input"),
117
+ input_delete_word_forward: z
118
+ .string()
119
+ .optional()
120
+ .default("alt+d,alt+delete,ctrl+delete")
121
+ .describe("Delete word forward in input"),
122
+ input_delete_word_backward: z
123
+ .string()
124
+ .optional()
125
+ .default("ctrl+w,ctrl+backspace,alt+backspace")
126
+ .describe("Delete word backward in input"),
127
+ history_previous: z.string().optional().default("up").describe("Previous history item"),
128
+ history_next: z.string().optional().default("down").describe("Next history item"),
129
+ session_child_cycle: z.string().optional().default("<leader>right").describe("Next child session"),
130
+ session_child_cycle_reverse: z.string().optional().default("<leader>left").describe("Previous child session"),
131
+ session_parent: z.string().optional().default("<leader>up").describe("Go to parent session"),
132
+ terminal_suspend: z.string().optional().default("ctrl+z").describe("Suspend terminal"),
133
+ terminal_title_toggle: z.string().optional().default("none").describe("Toggle terminal title"),
134
+ tips_toggle: z.string().optional().default("<leader>h").describe("Toggle tips on home screen"),
135
+ })
136
+ .strict()
137
+ .meta({
138
+ ref: "KeybindsConfig",
139
+ });
140
+ export const Layout = z.enum(["auto", "stretch"]).meta({
141
+ ref: "LayoutConfig",
142
+ });
143
+ const QuickSwitcherModel = z
144
+ .object({
145
+ providerID: z.string().describe("Provider id for the quick switcher model preference"),
146
+ modelID: z.string().describe("Model id for the quick switcher model preference"),
147
+ state: z.enum(["add", "remove"]).describe("Whether to force-add or force-remove the model from the quick switcher"),
148
+ })
149
+ .strict()
150
+ .meta({ ref: "QuickSwitcherModelConfig" });
151
+ export const QuickSwitcher = z
152
+ .object({
153
+ models: z.array(QuickSwitcherModel).optional().describe("Per-model quick switcher visibility preferences"),
154
+ })
155
+ .strict()
156
+ .meta({ ref: "QuickSwitcherConfig" });
157
+ export const ConfigShape = {
158
+ locale: z.enum(["system", "en", "zh-CN"]).optional().describe("UI locale (system = follow OS, default: system)"),
159
+ theme: z.string().optional().describe("Theme name to use for the interface"),
160
+ activityDisplay: z
161
+ .enum(["full", "balanced", "minimal"])
162
+ .optional()
163
+ .describe("Execution process detail: full = expanded process, balanced = current stage with completed process collapsed, minimal = compact progress. All modes use the same process view (default: balanced)"),
164
+ defaultSessionWorkspace: z
165
+ .enum(["main", "worktree"])
166
+ .optional()
167
+ .describe("Default workspace for new sessions started from the Web composer: main = run in the main checkout, " +
168
+ "worktree = start each new session in an isolated git worktree (default: main). " +
169
+ "Programmatic session creation (API, channels, Cortex) always uses the main checkout."),
170
+ defaultSessionEnvironmentProfile: z
171
+ .string()
172
+ .min(1)
173
+ .nullable()
174
+ .optional()
175
+ .describe("Execution profile for new Web/Desktop composer sessions. Omitted follows the global resource default; null disables execution selection. References an existing global profile without defining hosts or credentials."),
176
+ keybinds: Keybinds.optional().describe("Custom keybind configurations"),
177
+ compactReasoning: z
178
+ .boolean()
179
+ .optional()
180
+ .describe("Show a compact reasoning preview in the execution process (default: false)"),
181
+ preferWorkBrief: z
182
+ .boolean()
183
+ .optional()
184
+ .describe("Prefer a tool call's purpose description over its action and target when available (default: false)"),
185
+ quick_switcher: QuickSwitcher.optional().describe("Quick switcher model visibility preferences"),
186
+ welcomeGames: z
187
+ .boolean()
188
+ .optional()
189
+ .describe("Show interactive games on the new-task greeting (default: true). Set false to render a plain greeting."),
190
+ layout: Layout.optional().describe("@deprecated Always uses stretch layout."),
191
+ toast: z
192
+ .object({
193
+ muted: z
194
+ .array(z.enum(["info", "success", "warning", "error"]))
195
+ .optional()
196
+ .describe("Toast types to suppress. The underlying logic still runs but the visual card is not rendered."),
197
+ durationOverrides: z
198
+ .record(z.enum(["info", "success", "warning", "error"]), z.number().int().positive().max(30000))
199
+ .optional()
200
+ .describe("Override auto-dismiss duration in ms per toast type (max 30s)."),
201
+ })
202
+ .strict()
203
+ .optional()
204
+ .describe("Toast notification preferences"),
205
+ };
206
+ const contribution = {
207
+ shape: ConfigShape,
208
+ references(raw, providerID) {
209
+ const config = raw;
210
+ return (config.quick_switcher?.models ?? []).flatMap((model, index) => model.providerID === providerID ? [`quick_switcher.models[${index}]`] : []);
211
+ },
212
+ normalize(raw) {
213
+ const result = raw;
214
+ if (result.activityDisplay === undefined)
215
+ result.activityDisplay = "balanced";
216
+ if (result.compactReasoning === undefined)
217
+ result.compactReasoning = false;
218
+ if (result.preferWorkBrief === undefined)
219
+ result.preferWorkBrief = false;
220
+ if (!result.keybinds)
221
+ result.keybinds = Keybinds.parse({});
222
+ },
223
+ };
224
+ export function registerConfig() {
225
+ ConfigExtensions.register("workbench", contribution);
226
+ for (const domain of [
227
+ {
228
+ id: "general",
229
+ filename: "00-general.jsonc",
230
+ label: "General",
231
+ ownedKeys: [
232
+ "theme",
233
+ "keybinds",
234
+ "toast",
235
+ "compactReasoning",
236
+ "preferWorkBrief",
237
+ "welcomeGames",
238
+ "locale",
239
+ "activityDisplay",
240
+ "defaultSessionWorkspace",
241
+ "defaultSessionEnvironmentProfile",
242
+ "layout",
243
+ ],
244
+ mergePolicy: "merge",
245
+ reloadTargets: ["config"],
246
+ uiSection: "general",
247
+ importable: true,
248
+ },
249
+ {
250
+ id: "models",
251
+ filename: "10-models.jsonc",
252
+ label: "Models",
253
+ ownedKeys: ["quick_switcher"],
254
+ mergePolicy: "merge",
255
+ reloadTargets: ["config"],
256
+ uiSection: "models",
257
+ importable: true,
258
+ },
259
+ ])
260
+ ConfigDomain.register(domain);
261
+ }
262
+ export async function readConfig() {
263
+ const { Config } = await import("@ericsanchezok/synergy-harness/config/config");
264
+ return Config.current();
265
+ }
@@ -0,0 +1,78 @@
1
+ export var ExecutionActivity;
2
+ (function (ExecutionActivity) {
3
+ function project(rows, root) {
4
+ const byID = new Map(rows.map((node) => [node.id, node]));
5
+ const requests = new Set(rows.filter((node) => node.evidenceKind === "attempt").map((node) => node.parentID));
6
+ const instructions = new Set(rows.filter((node) => node.kind === "subtask").map((node) => node.sessionID + ":" + node.runID));
7
+ const nodes = rows
8
+ .filter((node) => node.kind === "subtask" ||
9
+ node.kind === "tool" ||
10
+ (node.kind === "input" &&
11
+ (node.sessionID === root || !instructions.has(node.sessionID + ":" + node.runID))) ||
12
+ node.evidenceKind === "attempt" ||
13
+ (node.evidenceKind === "call" && !requests.has(node.id)))
14
+ .map((node, index) => ({
15
+ ...node,
16
+ activity: {
17
+ index,
18
+ count: 1,
19
+ endIndex: index,
20
+ instruction: node.kind === "subtask" || (node.kind === "input" && node.sessionID !== root),
21
+ },
22
+ }));
23
+ const segments = [];
24
+ for (const node of nodes) {
25
+ let ancestor = node;
26
+ const seen = new Set();
27
+ while (ancestor && ancestor.sessionID !== root && ancestor.parentID && !seen.has(ancestor.id)) {
28
+ seen.add(ancestor.id);
29
+ ancestor = byID.get(ancestor.parentID);
30
+ }
31
+ const runID = ancestor?.sessionID === root ? ancestor.runID : null;
32
+ const last = segments.at(-1);
33
+ if (last?.runID === runID) {
34
+ last.to = node.activity.index;
35
+ last.count++;
36
+ }
37
+ else
38
+ segments.push({ runID, from: node.activity.index, to: node.activity.index, count: 1, rounds: 1 });
39
+ }
40
+ const chunks = [];
41
+ const size = Math.max(1, Math.ceil(segments.length / 160));
42
+ for (let index = 0; index < segments.length; index += size) {
43
+ const values = segments.slice(index, index + size);
44
+ chunks.push({
45
+ runID: values.length === 1 ? values[0].runID : null,
46
+ from: values[0].from,
47
+ to: values.at(-1).to,
48
+ count: values.reduce((sum, value) => sum + value.count, 0),
49
+ rounds: new Set(values.map((value) => value.runID)).size,
50
+ });
51
+ }
52
+ return {
53
+ nodes,
54
+ segments: chunks,
55
+ humanInputs: nodes.filter((node) => node.kind === "input" && !node.activity.instruction).length,
56
+ taskInstructions: nodes.filter((node) => node.activity.instruction).length,
57
+ };
58
+ }
59
+ ExecutionActivity.project = project;
60
+ function aggregate(nodes, kind) {
61
+ const lane = nodes.filter((node) => kind === "input"
62
+ ? node.kind === "input" || node.kind === "subtask"
63
+ : kind === "model"
64
+ ? node.evidenceKind === "call" || node.evidenceKind === "attempt"
65
+ : node.kind === "tool");
66
+ const size = Math.max(1, Math.ceil(lane.length / 160));
67
+ const result = [];
68
+ for (let index = 0; index < lane.length; index += size) {
69
+ const bucket = lane.slice(index, index + size);
70
+ result.push({
71
+ ...bucket[0],
72
+ activity: { ...bucket[0].activity, count: bucket.length, endIndex: bucket.at(-1).activity.index },
73
+ });
74
+ }
75
+ return result;
76
+ }
77
+ ExecutionActivity.aggregate = aggregate;
78
+ })(ExecutionActivity || (ExecutionActivity = {}));