@xingular/harness 1.0.0 → 1.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 (257) hide show
  1. package/CHANGELOG.md +33 -5
  2. package/README.md +70 -19
  3. package/dist/cli.js +10321 -5785
  4. package/dist/sdk/agents/builtin.d.ts +2 -0
  5. package/dist/sdk/agents/builtin.js +34 -0
  6. package/dist/sdk/agents/narrow.d.ts +13 -0
  7. package/dist/sdk/agents/narrow.js +29 -0
  8. package/dist/sdk/agents/profiles.d.ts +80 -0
  9. package/dist/sdk/agents/profiles.js +221 -0
  10. package/dist/sdk/annotations/bridge.d.ts +42 -0
  11. package/dist/sdk/annotations/bridge.js +264 -0
  12. package/dist/sdk/annotations/capture.d.ts +116 -0
  13. package/dist/sdk/annotations/capture.js +308 -0
  14. package/dist/sdk/annotations/delivery.d.ts +72 -0
  15. package/dist/sdk/annotations/delivery.js +130 -0
  16. package/dist/sdk/annotations/inspect.d.ts +10 -0
  17. package/dist/sdk/annotations/inspect.js +33 -0
  18. package/dist/sdk/annotations/isolated.d.ts +6 -0
  19. package/dist/sdk/annotations/isolated.js +51 -0
  20. package/dist/sdk/annotations/pageScript.d.ts +28 -0
  21. package/dist/sdk/annotations/pageScript.js +570 -0
  22. package/dist/sdk/annotations/part.d.ts +10 -0
  23. package/dist/sdk/annotations/part.js +144 -0
  24. package/dist/sdk/annotations/queue.d.ts +4 -0
  25. package/dist/sdk/annotations/queue.js +40 -0
  26. package/dist/sdk/annotations/serverMethods.d.ts +3 -0
  27. package/dist/sdk/annotations/serverMethods.js +62 -0
  28. package/dist/sdk/annotations/store.d.ts +160 -0
  29. package/dist/sdk/annotations/store.js +386 -0
  30. package/dist/sdk/browser/index.d.ts +14 -3
  31. package/dist/sdk/browser/index.js +11 -4
  32. package/dist/sdk/browser/serverMethods.d.ts +3 -0
  33. package/dist/sdk/browser/serverMethods.js +7 -0
  34. package/dist/sdk/browser/session.d.ts +1 -0
  35. package/dist/sdk/browser/session.js +45 -9
  36. package/dist/sdk/cli.js +19 -11
  37. package/dist/sdk/core/bus.js +1 -1
  38. package/dist/sdk/core/env.d.ts +1 -1
  39. package/dist/sdk/core/env.js +1 -1
  40. package/dist/sdk/doctor/serverMethods.d.ts +3 -0
  41. package/dist/sdk/doctor/serverMethods.js +8 -0
  42. package/dist/sdk/harness.d.ts +15 -0
  43. package/dist/sdk/harness.js +44 -5
  44. package/dist/sdk/history/recall.js +8 -8
  45. package/dist/sdk/history/serverMethods.d.ts +3 -0
  46. package/dist/sdk/history/serverMethods.js +8 -0
  47. package/dist/sdk/hub/cli.d.ts +1 -1
  48. package/dist/sdk/hub/cli.js +1 -1
  49. package/dist/sdk/hub/schema.d.ts +1 -1
  50. package/dist/sdk/hub/serverMethods.d.ts +3 -0
  51. package/dist/sdk/hub/serverMethods.js +11 -0
  52. package/dist/sdk/index.d.ts +10 -0
  53. package/dist/sdk/index.js +7 -0
  54. package/dist/sdk/mcp/connections.js +14 -7
  55. package/dist/sdk/mcp/serverMethods.d.ts +3 -0
  56. package/dist/sdk/mcp/serverMethods.js +22 -0
  57. package/dist/sdk/plugins/builtin.d.ts +1 -0
  58. package/dist/sdk/plugins/builtin.js +128 -33
  59. package/dist/sdk/plugins/builtinRegistry.d.ts +53 -0
  60. package/dist/sdk/plugins/builtinRegistry.js +49 -0
  61. package/dist/sdk/plugins/cli.d.ts +1 -1
  62. package/dist/sdk/plugins/cli.js +4 -3
  63. package/dist/sdk/prompt/compile.d.ts +5 -2
  64. package/dist/sdk/prompt/compile.js +25 -8
  65. package/dist/sdk/refine/cli.d.ts +1 -1
  66. package/dist/sdk/refine/cli.js +2 -2
  67. package/dist/sdk/refine/serverMethods.d.ts +3 -0
  68. package/dist/sdk/refine/serverMethods.js +20 -0
  69. package/dist/sdk/server/methods.js +9 -86
  70. package/dist/sdk/server/params.d.ts +4 -0
  71. package/dist/sdk/server/params.js +11 -0
  72. package/dist/sdk/sessions/contracts.d.ts +17 -0
  73. package/dist/sdk/sessions/contracts.js +42 -3
  74. package/dist/sdk/sessions/planMode.d.ts +5 -2
  75. package/dist/sdk/sessions/planMode.js +12 -3
  76. package/dist/sdk/sessions/reasoningSummaries.d.ts +2 -2
  77. package/dist/sdk/sessions/reasoningSummaries.js +3 -3
  78. package/dist/sdk/sessions/registry.d.ts +1 -0
  79. package/dist/sdk/sessions/registry.js +4 -1
  80. package/dist/sdk/sessions/runIndex.js +7 -7
  81. package/dist/sdk/sessions/runStore.js +3 -1
  82. package/dist/sdk/sessions/runner.js +2 -2
  83. package/dist/sdk/sessions/sessions.d.ts +17 -5
  84. package/dist/sdk/sessions/sessions.js +79 -12
  85. package/dist/sdk/sessions/sideQuestionServerMethods.d.ts +3 -0
  86. package/dist/sdk/sessions/sideQuestionServerMethods.js +19 -0
  87. package/dist/sdk/sessions/sideQuestions.d.ts +66 -0
  88. package/dist/sdk/sessions/sideQuestions.js +290 -0
  89. package/dist/sdk/sessions/subagentServerMethods.d.ts +3 -0
  90. package/dist/sdk/sessions/subagentServerMethods.js +41 -0
  91. package/dist/sdk/sessions/subagentService.d.ts +13 -2
  92. package/dist/sdk/sessions/subagentService.js +34 -16
  93. package/dist/sdk/sessions/subagents.d.ts +9 -1
  94. package/dist/sdk/sessions/subagents.js +19 -0
  95. package/dist/sdk/terminal/browser.d.ts +62 -0
  96. package/dist/sdk/terminal/browser.js +41 -0
  97. package/dist/sdk/terminal/browserControl.d.ts +29 -0
  98. package/dist/sdk/terminal/browserControl.js +186 -0
  99. package/dist/sdk/terminal/commandHelp.d.ts +17 -0
  100. package/dist/sdk/terminal/commandHelp.js +20 -0
  101. package/dist/sdk/terminal/controller.d.ts +7 -1
  102. package/dist/sdk/terminal/controller.js +34 -2
  103. package/dist/sdk/terminal/opentui/subagentsWidget.d.ts +28 -0
  104. package/dist/sdk/terminal/opentui/subagentsWidget.js +52 -0
  105. package/dist/sdk/terminal/piExtension.js +33 -48
  106. package/dist/sdk/terminal/socket.d.ts +6 -3
  107. package/dist/sdk/terminal/socket.js +40 -9
  108. package/dist/sdk/terminal/standalone.d.ts +2 -0
  109. package/dist/sdk/terminal/standalone.js +50 -10
  110. package/dist/sdk/terminal/subagentMonitor.d.ts +38 -0
  111. package/dist/sdk/terminal/subagentMonitor.js +121 -0
  112. package/dist/sdk/terminal/terminals.d.ts +42 -6
  113. package/dist/sdk/terminal/terminals.js +359 -18
  114. package/dist/sdk/terminal/trajectory.js +2 -2
  115. package/dist/sdk/terminal/tui/annotateCommand.d.ts +3 -0
  116. package/dist/sdk/terminal/tui/annotateCommand.js +22 -0
  117. package/dist/sdk/terminal/tui/commandPages.d.ts +5 -1
  118. package/dist/sdk/terminal/tui/commandPages.js +54 -6
  119. package/dist/sdk/terminal/tui/commands.d.ts +8 -4
  120. package/dist/sdk/terminal/tui/commands.js +181 -58
  121. package/dist/sdk/terminal/tui/dialogGeometry.d.ts +2 -2
  122. package/dist/sdk/terminal/tui/dialogGeometry.js +4 -2
  123. package/dist/sdk/terminal/tui/dialogs.d.ts +6 -1
  124. package/dist/sdk/terminal/tui/dialogs.js +9 -2
  125. package/dist/sdk/terminal/tui/modelPicker.js +1 -6
  126. package/dist/sdk/terminal/tui/page.d.ts +3 -0
  127. package/dist/sdk/terminal/tui/pageOverlay.js +17 -6
  128. package/dist/sdk/terminal/tui/pages/accountsPage.d.ts +26 -4
  129. package/dist/sdk/terminal/tui/pages/accountsPage.js +264 -36
  130. package/dist/sdk/terminal/tui/pages/agentsPage.d.ts +74 -0
  131. package/dist/sdk/terminal/tui/pages/agentsPage.js +190 -0
  132. package/dist/sdk/terminal/tui/pages/annotatePage.d.ts +94 -0
  133. package/dist/sdk/terminal/tui/pages/annotatePage.js +341 -0
  134. package/dist/sdk/terminal/tui/pages/browserPage.d.ts +87 -0
  135. package/dist/sdk/terminal/tui/pages/browserPage.js +738 -0
  136. package/dist/sdk/terminal/tui/pages/doctorPage.d.ts +2 -0
  137. package/dist/sdk/terminal/tui/pages/doctorPage.js +103 -15
  138. package/dist/sdk/terminal/tui/pages/notesPage.d.ts +75 -0
  139. package/dist/sdk/terminal/tui/pages/notesPage.js +201 -0
  140. package/dist/sdk/terminal/tui/pages/settingsPage.d.ts +8 -0
  141. package/dist/sdk/terminal/tui/pages/settingsPage.js +16 -1
  142. package/dist/sdk/terminal/tui/pages/subagentsPage.d.ts +23 -0
  143. package/dist/sdk/terminal/tui/pages/subagentsPage.js +70 -0
  144. package/dist/sdk/terminal/tui/pages/trajectoryPage.d.ts +12 -5
  145. package/dist/sdk/terminal/tui/pages/trajectoryPage.js +156 -83
  146. package/dist/sdk/terminal/tui/pages/worktreesPage.d.ts +72 -0
  147. package/dist/sdk/terminal/tui/pages/worktreesPage.js +203 -0
  148. package/dist/sdk/terminal/tui/planMode.d.ts +1 -1
  149. package/dist/sdk/terminal/tui/planMode.js +1 -1
  150. package/dist/sdk/terminal/tui/pluginCommands.d.ts +14 -0
  151. package/dist/sdk/terminal/tui/pluginCommands.js +27 -0
  152. package/dist/sdk/terminal/tui/questions.d.ts +2 -0
  153. package/dist/sdk/terminal/tui/runtime.js +4 -2
  154. package/dist/sdk/terminal/tui/settingsModel.d.ts +2 -0
  155. package/dist/sdk/terminal/tui/settingsModel.js +32 -2
  156. package/dist/sdk/terminal/tui/sideQuestion.d.ts +26 -26
  157. package/dist/sdk/terminal/tui/sideQuestion.js +131 -98
  158. package/dist/sdk/terminal/tui/ui.d.ts +2 -0
  159. package/dist/sdk/terminal/tui/ui.js +1 -0
  160. package/dist/sdk/tools/askUser.js +26 -8
  161. package/dist/sdk/tools/checklist.js +2 -2
  162. package/dist/sdk/tools/harnessTools.d.ts +2 -0
  163. package/dist/sdk/tools/harnessTools.js +1 -1
  164. package/dist/sdk/tools/spawnSubagent.js +13 -4
  165. package/dist/sdk/web/part.d.ts +10 -0
  166. package/dist/sdk/web/part.js +6 -0
  167. package/dist/sdk/web/serverMethods.d.ts +3 -0
  168. package/dist/sdk/web/serverMethods.js +7 -0
  169. package/dist/sdk/wiki/serverMethods.d.ts +3 -0
  170. package/dist/sdk/wiki/serverMethods.js +26 -0
  171. package/dist/sdk/worktrees/cli.d.ts +11 -0
  172. package/dist/sdk/worktrees/cli.js +146 -0
  173. package/dist/sdk/worktrees/copy.d.ts +9 -0
  174. package/dist/sdk/worktrees/copy.js +111 -0
  175. package/dist/sdk/worktrees/git.d.ts +16 -0
  176. package/dist/sdk/worktrees/git.js +81 -0
  177. package/dist/sdk/worktrees/serverMethods.d.ts +3 -0
  178. package/dist/sdk/worktrees/serverMethods.js +30 -0
  179. package/dist/sdk/worktrees/service.d.ts +73 -0
  180. package/dist/sdk/worktrees/service.js +483 -0
  181. package/dist/sdk/worktrees/setup.d.ts +23 -0
  182. package/dist/sdk/worktrees/setup.js +65 -0
  183. package/dist/sdk/worktrees/types.d.ts +91 -0
  184. package/dist/sdk/worktrees/types.js +1 -0
  185. package/dist/sdk/world/cli.d.ts +2 -2
  186. package/dist/sdk/world/cli.js +6 -6
  187. package/dist/sdk/world/world.d.ts +3 -1
  188. package/dist/sdk/world/world.js +8 -9
  189. package/dist/terminal-opentui.js +14540 -7374
  190. package/dist/terminal-pi.js +285 -666
  191. package/docs/README.md +22 -2
  192. package/docs/architecture/00-overview.md +28 -16
  193. package/docs/architecture/02-sessions-and-runs.md +72 -15
  194. package/docs/architecture/03-variants.md +10 -2
  195. package/docs/architecture/04-prompt.md +26 -6
  196. package/docs/architecture/06-plugins-and-capabilities.md +39 -3
  197. package/docs/architecture/07-tools-and-gate.md +21 -6
  198. package/docs/architecture/09-accounts-and-connectors.md +8 -0
  199. package/docs/architecture/10-server-protocol.md +111 -2
  200. package/docs/architecture/11-terminal.md +185 -5
  201. package/docs/architecture/12-storage.md +15 -1
  202. package/docs/architecture/13-world-tools.md +28 -6
  203. package/docs/architecture/14-surfaces-and-parity.md +89 -0
  204. package/docs/architecture/README.md +15 -4
  205. package/docs/architecture/proposals/PROP-0006-run-checklist.md +4 -2
  206. package/docs/architecture/proposals/PROP-0013-resource-browser.md +58 -0
  207. package/docs/architecture/proposals/PROP-0014-fullscreen-browser-tabs.md +58 -0
  208. package/docs/architecture/proposals/PROP-0015-one-capability-surface.md +88 -0
  209. package/docs/architecture/proposals/PROP-0016-managed-worktrees.md +156 -0
  210. package/docs/architecture/proposals/PROP-0017-page-annotations.md +127 -0
  211. package/docs/architecture/proposals/PROP-0018-annotation-queue-and-app-targets.md +179 -0
  212. package/docs/architecture/proposals/PROP-0019-contextual-side-questions.md +122 -0
  213. package/docs/architecture/proposals/PROP-0020-builtin-feature-plugins.md +105 -0
  214. package/docs/architecture/proposals/PROP-0021-command-namespaces-and-duplicates.md +99 -0
  215. package/docs/architecture/proposals/PROP-0022-plan-only-questions.md +102 -0
  216. package/docs/architecture/proposals/PROP-0023-xagent-cli-name.md +61 -0
  217. package/docs/architecture/proposals/PROP-0024-question-sketch-previews.md +130 -0
  218. package/docs/architecture/proposals/PROP-0025-contextual-question-wireframes.md +49 -0
  219. package/docs/architecture/proposals/PROP-0026-connected-sketch-display.md +94 -0
  220. package/docs/architecture/proposals/PROP-0027-xingular-agent-product-identity.md +60 -0
  221. package/docs/architecture/proposals/PROP-0028-normalize-question-wireframes.md +55 -0
  222. package/docs/architecture/proposals/PROP-0029-agent-profiles.md +124 -0
  223. package/docs/architecture/proposals/PROP-0030-subagent-title.md +86 -0
  224. package/docs/architecture/proposals/README.md +25 -0
  225. package/docs/decisions/ADR-0009-xharness-command.md +6 -5
  226. package/docs/decisions/ADR-0014-public-release-baseline.md +3 -2
  227. package/docs/decisions/ADR-0015-resource-browser.md +28 -0
  228. package/docs/decisions/ADR-0016-fullscreen-browser-tabs.md +31 -0
  229. package/docs/decisions/ADR-0017-one-capability-surface.md +34 -0
  230. package/docs/decisions/ADR-0018-builtin-feature-plugins.md +35 -0
  231. package/docs/decisions/ADR-0019-managed-git-worktrees.md +31 -0
  232. package/docs/decisions/ADR-0020-page-annotations.md +37 -0
  233. package/docs/decisions/ADR-0021-required-command-namespaces.md +40 -0
  234. package/docs/decisions/ADR-0022-plan-only-question-tools.md +34 -0
  235. package/docs/decisions/ADR-0023-contextual-side-question-runs.md +35 -0
  236. package/docs/decisions/ADR-0024-xagent-command.md +40 -0
  237. package/docs/decisions/ADR-0025-question-choice-sketches.md +49 -0
  238. package/docs/decisions/ADR-0026-contextual-question-wireframes.md +58 -0
  239. package/docs/decisions/ADR-0027-connected-sketch-display.md +60 -0
  240. package/docs/decisions/ADR-0028-xingular-agent-product-identity.md +56 -0
  241. package/docs/decisions/ADR-0029-normalized-question-wireframe-borders.md +33 -0
  242. package/docs/decisions/ADR-0030-agent-profiles.md +43 -0
  243. package/docs/doctor.md +10 -4
  244. package/docs/features/README.md +26 -0
  245. package/docs/features/interfaces/README.md +1 -1
  246. package/docs/future-work.md +48 -0
  247. package/docs/parity.md +78 -0
  248. package/docs/releases/1.0.0.md +2 -2
  249. package/docs/releases/4.0.0.md +46 -0
  250. package/docs/releases/README.md +20 -15
  251. package/docs/study/00-index.md +5 -0
  252. package/docs/study/autonomous-engineering-sources.md +277 -0
  253. package/docs/study/autonomy-sources.md +147 -0
  254. package/docs/study/proactive-autonomy.md +408 -0
  255. package/docs/study/tui-resource-browser.md +78 -0
  256. package/docs/terminal-tui.md +325 -93
  257. package/package.json +3 -3
@@ -0,0 +1,8 @@
1
+ import { text } from "../server/params.js";
2
+ export function doctorServerMethods(harness) {
3
+ return {
4
+ 'doctor.start': (params) => harness.doctor.start(params),
5
+ 'doctor.read': (params) => harness.doctor.read(text(params, 'id')),
6
+ 'doctor.cancel': (params) => harness.doctor.cancel(text(params, 'id'))
7
+ };
8
+ }
@@ -21,7 +21,12 @@ import type { ToolPolicy } from "./sessions/policy.js";
21
21
  import { RunRegistry } from "./sessions/registry.js";
22
22
  import type { SessionFactory } from "./sessions/runner.js";
23
23
  import { Sessions } from "./sessions/sessions.js";
24
+ import { SideQuestions } from "./sessions/sideQuestions.js";
24
25
  import { Terminals, type TerminalAssets } from "./terminal/terminals.js";
26
+ import { Worktrees } from "./worktrees/service.js";
27
+ import { AnnotationCapture } from "./annotations/capture.js";
28
+ import { NoteDeliveries } from "./annotations/delivery.js";
29
+ import type { WorktreesOptions } from "./worktrees/types.js";
25
30
  export interface HarnessOptions {
26
31
  /** Disable the generic backend when a host supplies its own browser tools. */
27
32
  browser?: boolean;
@@ -60,6 +65,8 @@ export interface HarnessOptions {
60
65
  oauthClients?: AppOAuthClients;
61
66
  /** Replaces how a plugin's hooks file is read as a module, for tests. */
62
67
  hookImporter?: (address: string) => Promise<unknown>;
68
+ /** Where managed worktrees are made, the prefix of branches they get, and how many `prune` keeps. */
69
+ worktrees?: WorktreesOptions;
63
70
  }
64
71
  /** What starting on a data root found and repaired. */
65
72
  export interface Recovery {
@@ -100,6 +107,8 @@ export declare class Harness {
100
107
  applicationPlugins: ReadonlySet<string>;
101
108
  readonly subagents: Subagents;
102
109
  readonly sessions: Sessions;
110
+ /** Tool-free, run-backed questions over a redacted projection of the main conversation. */
111
+ readonly sideQuestions: SideQuestions;
103
112
  /** Web, browser and computer tools (page 13). */
104
113
  readonly world: World;
105
114
  /** Which plugin code in variants a person trusted. */
@@ -111,6 +120,12 @@ export declare class Harness {
111
120
  refineHost: RefineHost | null;
112
121
  readonly terminals: Terminals;
113
122
  readonly doctor: Doctor;
123
+ /** Managed git worktrees sessions can work in (PROP-0016). */
124
+ readonly worktrees: Worktrees;
125
+ /** The terminal's visible browser for capturing page notes (PROP-0017). */
126
+ readonly noteCapture: AnnotationCapture;
127
+ /** Page notes on their way to a session (PROP-0018). */
128
+ readonly pageNotes: NoteDeliveries;
114
129
  /** Resolves when what was loaded in the background at construction is in place. */
115
130
  readonly ready: Promise<void>;
116
131
  recovery: Recovery;
@@ -25,6 +25,7 @@ import { migrateVariantLayout } from "./variant/migrate.js";
25
25
  import { VariantTrust } from "./variant/trust.js";
26
26
  import * as notes from "./wiki/notes.js";
27
27
  import * as pluginApi from "./plugins/api.js";
28
+ import * as pluginInstall from "./plugins/install.js";
28
29
  import * as attachments from "./plugins/attachments.js";
29
30
  import { HARNESS_PLUGINS } from "./plugins/builtin.js";
30
31
  import { World } from "./world/world.js";
@@ -35,9 +36,13 @@ import { Hooks } from "./plugins/hooks.js";
35
36
  import { packageFolders, Plugins } from "./plugins/plugins.js";
36
37
  import { approvalKey, Approvals } from "./sessions/approvals.js";
37
38
  import { RunRegistry } from "./sessions/registry.js";
38
- import { markInterruptedRuns } from "./sessions/runStore.js";
39
+ import { loadRun, markInterruptedRuns } from "./sessions/runStore.js";
39
40
  import { Sessions } from "./sessions/sessions.js";
41
+ import { SideQuestions } from "./sessions/sideQuestions.js";
40
42
  import { Terminals } from "./terminal/terminals.js";
43
+ import { Worktrees } from "./worktrees/service.js";
44
+ import { AnnotationCapture } from "./annotations/capture.js";
45
+ import { NoteDeliveries } from "./annotations/delivery.js";
41
46
  class Env {
42
47
  directory;
43
48
  productName;
@@ -74,6 +79,8 @@ export class Harness {
74
79
  applicationPlugins = new Set();
75
80
  subagents;
76
81
  sessions;
82
+ /** Tool-free, run-backed questions over a redacted projection of the main conversation. */
83
+ sideQuestions;
77
84
  /** Web, browser and computer tools (page 13). */
78
85
  world;
79
86
  /** Which plugin code in variants a person trusted. */
@@ -85,6 +92,12 @@ export class Harness {
85
92
  refineHost;
86
93
  terminals;
87
94
  doctor;
95
+ /** Managed git worktrees sessions can work in (PROP-0016). */
96
+ worktrees;
97
+ /** The terminal's visible browser for capturing page notes (PROP-0017). */
98
+ noteCapture;
99
+ /** Page notes on their way to a session (PROP-0018). */
100
+ pageNotes;
88
101
  /** Resolves when what was loaded in the background at construction is in place. */
89
102
  ready;
90
103
  recovery = { interruptedRuns: 0, interruptedRunIds: [], reindexedPages: 0, migrationError: null };
@@ -146,7 +159,9 @@ export class Harness {
146
159
  reloadTimer.unref?.();
147
160
  });
148
161
  this.describe(options.plugins ?? []);
149
- this.subagents = new Subagents(this.env, this.runs, (input) => this.sessions.run(input));
162
+ this.worktrees = new Worktrees(this.stateDir, options.worktrees);
163
+ this.noteCapture = new AnnotationCapture({ stateDir: this.stateDir });
164
+ this.subagents = new Subagents(this.env, this.runs, (input) => this.sessions.run(input), { descriptors: () => HARNESS_PLUGINS });
150
165
  this.sessions = new Sessions({
151
166
  subagents: this.subagents,
152
167
  env: this.env,
@@ -157,9 +172,11 @@ export class Harness {
157
172
  plugins: this.plugins,
158
173
  resolveCapabilities: (caller, tools, cwd) => capabilities.resolve(this, { agentId: caller.agentId ?? null, projectId: caller.projectId ?? null, cwd, tools }),
159
174
  factory: options.sessionFactory,
160
- trust: this.trust
175
+ trust: this.trust,
176
+ worktrees: this.worktrees
161
177
  });
162
- this.world = new World({ root: () => this.env.root, stateDir: this.stateDir, secrets, browser: this.browserEnabled ? undefined : false });
178
+ this.sideQuestions = new SideQuestions({ env: this.env, sessions: this.sessions, runs: this.runs });
179
+ this.world = new World({ root: () => this.env.root, stateDir: this.stateDir, secrets, browser: this.browserEnabled ? undefined : false, notesChanged: (sessionId, ids) => this.bus.emit({ type: 'annotations.changed', sessionId, ids }) });
163
180
  // The harness's own world tools come first; a tool an application provides under the same name is not added again.
164
181
  this.sessions.provide(this.world.provider());
165
182
  this.doctor = new Doctor({
@@ -169,7 +186,8 @@ export class Harness {
169
186
  const refineRuntime = { root: () => this.env.root, runs: this.runs, run: (input) => this.sessions.run(input) };
170
187
  this.refine = new Refinements({ root: () => this.env.root, bus: this.bus, trust: this.trust, host: () => this.refineHost, runtime: refineRuntime });
171
188
  this.lessons = new Lessons({ root: () => this.env.root, runtime: refineRuntime });
172
- this.terminals = new Terminals({ trust: this.trust, subagents: this.subagents, resolveCapabilities: (caller, tools, cwd) => capabilities.resolve(this, { agentId: caller.agentId ?? null, projectId: caller.projectId ?? null, cwd, tools }), env: this.env, stateDir: this.stateDir, accounts: this.accounts, runs: this.runs, approvals: this.approvals, apps: this.apps, policy: this.policy, bus: this.bus, plugins: this.plugins, pluginControl: { list: (scope) => pluginApi.list(this, scope), attach: (params, on) => pluginApi.attach(this, params, on), trust: (scope) => pluginApi.trust(this, scope), untrust: (scope) => pluginApi.untrust(this, scope), configure: (params) => pluginApi.configure(this, params) }, assets: options.assets, hub: this.hub, world: this.world, doctor: this.doctor });
189
+ this.pageNotes = new NoteDeliveries({ root: () => this.env.root, bus: this.bus, terminals: () => this.terminals ?? null });
190
+ this.terminals = new Terminals({ trust: this.trust, subagents: this.subagents, sideQuestions: this.sideQuestions, resolveCapabilities: (caller, tools, cwd) => capabilities.resolve(this, { agentId: caller.agentId ?? null, projectId: caller.projectId ?? null, cwd, tools }), env: this.env, stateDir: this.stateDir, accounts: this.accounts, runs: this.runs, approvals: this.approvals, apps: this.apps, policy: this.policy, bus: this.bus, plugins: this.plugins, pluginControl: { list: (scope) => pluginApi.list(this, scope), attach: (params, on) => pluginApi.attach(this, params, on), trust: (scope) => pluginApi.trust(this, scope), untrust: (scope) => pluginApi.untrust(this, scope), configure: (params) => pluginApi.configure(this, params), install: params => pluginInstall.install(this, params), remove: params => pluginInstall.remove(this, params) }, assets: options.assets, hub: this.hub, world: this.world, doctor: this.doctor, worktrees: this.worktrees, noteCapture: this.noteCapture, pageNotes: this.pageNotes });
173
191
  this.ready = this.apps.start().catch((error) => log('connected apps', error instanceof Error ? error.message : String(error)));
174
192
  this.runs.listen((event, run) => {
175
193
  this.accounts.onRunEvent(event, run);
@@ -178,6 +196,12 @@ export class Harness {
178
196
  recall.indexRun(this.env.root, event.runId);
179
197
  if (run?.trigger === 'doctor')
180
198
  return;
199
+ const runEnded = event.type === 'completed' || event.type === 'failed' || (event.type === 'status.changed' && event.payload.status === 'interrupted');
200
+ if (runEnded && run?.sessionId) {
201
+ // Notes sent with "Proceed now" while this session was working go out now; the terminal
202
+ // needs a moment to see its run as ended.
203
+ setTimeout(() => void this.pageNotes.runEnded(run.sessionId).catch((error) => log('page notes', error instanceof Error ? error.message : String(error))), 250).unref?.();
204
+ }
181
205
  if ((event.type === 'completed' || event.type === 'failed') && run) {
182
206
  if (event.type === 'completed')
183
207
  this.lessons.consider(run);
@@ -246,6 +270,19 @@ export class Harness {
246
270
  let interruptedRunIds = [];
247
271
  try {
248
272
  interruptedRunIds = markInterruptedRuns(root, `${this.env.productName} stopped before the run settled.`);
273
+ const recoveredSessions = new Set();
274
+ for (const runId of interruptedRunIds) {
275
+ try {
276
+ const sessionId = loadRun(root, runId).sessionId;
277
+ if (!sessionId || recoveredSessions.has(sessionId))
278
+ continue;
279
+ recoveredSessions.add(sessionId);
280
+ setTimeout(() => void this.pageNotes.runEnded(sessionId).catch((error) => log('page notes', error instanceof Error ? error.message : String(error))), 250).unref?.();
281
+ }
282
+ catch {
283
+ // A malformed run record does not prevent the rest of recovery.
284
+ }
285
+ }
249
286
  }
250
287
  catch (error) {
251
288
  log('run recovery', error instanceof Error ? error.message : String(error));
@@ -274,6 +311,8 @@ export class Harness {
274
311
  await this.refine.close();
275
312
  await this.lessons.close();
276
313
  await this.terminals.close();
314
+ await this.noteCapture.close();
315
+ await this.sideQuestions.close();
277
316
  await this.runs.interruptAll();
278
317
  await this.world.close();
279
318
  await this.hooks.settled().catch(() => undefined);
@@ -32,11 +32,11 @@ export function passages(run, events) {
32
32
  return found;
33
33
  }
34
34
  function writeRun(database, run, found) {
35
- execute(database, 'DELETE FROM recall_fts WHERE run_id = ?1', run.id);
35
+ execute(database, 'DELETE FROM recall_fts WHERE run_id = ?', run.id);
36
36
  found.forEach((passage, seq) => {
37
- execute(database, 'INSERT INTO recall_fts (run_id, seq, role, at, mode, session_id, project_id, text) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)', run.id, seq, passage.role, passage.at, run.mode, run.sessionId, run.projectId, passage.text);
37
+ execute(database, 'INSERT INTO recall_fts (run_id, seq, role, at, mode, session_id, project_id, text) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', run.id, seq, passage.role, passage.at, run.mode, run.sessionId, run.projectId, passage.text);
38
38
  });
39
- execute(database, 'INSERT OR REPLACE INTO recall_runs (run_id, passages, status) VALUES (?1, ?2, ?3)', run.id, found.length, run.status);
39
+ execute(database, 'INSERT OR REPLACE INTO recall_runs (run_id, passages, status) VALUES (?, ?, ?)', run.id, found.length, run.status);
40
40
  }
41
41
  function eventsOf(root, runId) {
42
42
  try {
@@ -93,13 +93,13 @@ export function search(root, query, scope, limit) {
93
93
  if (!fts)
94
94
  return [];
95
95
  return withIndex(root, (database) => all(database, `SELECT run_id, seq, role, at, mode, session_id, project_id, snippet(recall_fts, 7, '', '', '…', 40) AS snippet
96
- FROM recall_fts WHERE recall_fts MATCH ?1
97
- AND (?2 IS NULL OR session_id = ?2) AND (?3 IS NULL OR mode = ?3)
98
- AND (?4 IS NULL OR project_id = ?4) AND (?5 IS NULL OR at >= ?5)
99
- ORDER BY bm25(recall_fts), at DESC LIMIT ?6`, fts, scope.sessionId ?? null, scope.mode ?? null, scope.projectId ?? null, scope.since ?? null, Math.min(Math.max(limit, 1), SEARCH_MAX)).map((row) => ({ runId: row.run_id, seq: Number(row.seq), role: row.role, at: row.at ?? '', mode: row.mode ?? '', sessionId: row.session_id ?? '', projectId: row.project_id, snippet: row.snippet })));
96
+ FROM recall_fts WHERE recall_fts MATCH ?
97
+ AND (? IS NULL OR session_id = ?) AND (? IS NULL OR mode = ?)
98
+ AND (? IS NULL OR project_id = ?) AND (? IS NULL OR at >= ?)
99
+ ORDER BY bm25(recall_fts), at DESC LIMIT ?`, fts, scope.sessionId ?? null, scope.sessionId ?? null, scope.mode ?? null, scope.mode ?? null, scope.projectId ?? null, scope.projectId ?? null, scope.since ?? null, scope.since ?? null, Math.min(Math.max(limit, 1), SEARCH_MAX)).map((row) => ({ runId: row.run_id, seq: Number(row.seq), role: row.role, at: row.at ?? '', mode: row.mode ?? '', sessionId: row.session_id ?? '', projectId: row.project_id, snippet: row.snippet })));
100
100
  }
101
101
  export function read(root, runId) {
102
- return withIndex(root, (database) => all(database, 'SELECT seq, role, at, text FROM recall_fts WHERE run_id = ?1 ORDER BY CAST(seq AS INTEGER)', runId).map((row) => ({ seq: Number(row.seq), role: row.role, at: row.at ?? '', text: row.text })));
102
+ return withIndex(root, (database) => all(database, 'SELECT seq, role, at, text FROM recall_fts WHERE run_id = ? ORDER BY CAST(seq AS INTEGER)', runId).map((row) => ({ seq: Number(row.seq), role: row.role, at: row.at ?? '', text: row.text })));
103
103
  }
104
104
  function textParam(params, key) {
105
105
  const value = params?.[key];
@@ -0,0 +1,3 @@
1
+ import type { Harness } from "../harness.js";
2
+ import type { Handler } from "../server/rpc.js";
3
+ export declare function historyServerMethods(harness: Harness): Record<string, Handler>;
@@ -0,0 +1,8 @@
1
+ import * as recall from "./recall.js";
2
+ import { text } from "../server/params.js";
3
+ export function historyServerMethods(harness) {
4
+ return {
5
+ 'recall.search': (params) => recall.searchAll(harness.env, typeof params.query === 'string' ? params.query : '', params.filter ?? {}),
6
+ 'recall.read': (params) => recall.read(harness.env.root, text(params, 'runId'))
7
+ };
8
+ }
@@ -1,5 +1,5 @@
1
1
  import { Harness, type HarnessOptions } from "../harness.js";
2
- export declare const HUB_USAGE = "Usage: xharness hub <list | show | install> [id@version] [options]\n\n list [--refresh] Browse the curated catalog and locally installed versions\n show <id@version> Show publisher, license, source, and delivery details\n install <id@version> Install the exact reviewed version, disabled and untrusted\n\n --data <dir> Target data library (default: ~/.xingular-harness/data)\n --state <dir> Target device state (default: ~/.xingular-harness/state)\n --refresh Refresh the public catalog; cached items remain usable offline\n";
2
+ export declare const HUB_USAGE = "Usage: xagent hub <list | show | install> [id@version] [options]\n\n list [--refresh] Browse the curated catalog and locally installed versions\n show <id@version> Show publisher, license, source, and delivery details\n install <id@version> Install the exact reviewed version, disabled and untrusted\n\n --data <dir> Target data library (default: ~/.xingular-harness/data)\n --state <dir> Target device state (default: ~/.xingular-harness/state)\n --refresh Refresh the public catalog; cached items remain usable offline\n";
3
3
  export declare function runHubCli(argv: string[], dependencies?: {
4
4
  home?: string;
5
5
  write?: (text: string) => void;
@@ -1,7 +1,7 @@
1
1
  import { homedir } from 'node:os';
2
2
  import { join, resolve } from 'node:path';
3
3
  import { Harness } from "../harness.js";
4
- export const HUB_USAGE = `Usage: xharness hub <list | show | install> [id@version] [options]
4
+ export const HUB_USAGE = `Usage: xagent hub <list | show | install> [id@version] [options]
5
5
 
6
6
  list [--refresh] Browse the curated catalog and locally installed versions
7
7
  show <id@version> Show publisher, license, source, and delivery details
@@ -263,9 +263,9 @@ export declare const hubOverviewSchema: z.ZodObject<{
263
263
  }>>;
264
264
  }, z.core.$strict>>;
265
265
  origin: z.ZodEnum<{
266
- remote: "remote";
267
266
  bundled: "bundled";
268
267
  cached: "cached";
268
+ remote: "remote";
269
269
  }>;
270
270
  refreshedAt: z.ZodNullable<z.ZodString>;
271
271
  error: z.ZodNullable<z.ZodString>;
@@ -0,0 +1,3 @@
1
+ import type { Harness } from "../harness.js";
2
+ import type { Handler } from "../server/rpc.js";
3
+ export declare function hubServerMethods(harness: Harness): Record<string, Handler>;
@@ -0,0 +1,11 @@
1
+ import { text } from "../server/params.js";
2
+ export function hubServerMethods(harness) {
3
+ return {
4
+ 'hub.list': (params) => harness.hub.list(params.refresh === true),
5
+ 'hub.get': (params) => harness.hub.get(text(params, 'id'), text(params, 'version'), params.preview === true),
6
+ 'hub.install': (params) => harness.hub.install(text(params, 'id'), text(params, 'version')),
7
+ 'hub.preview': (params) => harness.hub.preview(text(params, 'id'), text(params, 'version'), typeof params.offset === 'number' ? params.offset : 0),
8
+ 'hub.uninstall': (params) => harness.hub.uninstall(text(params, 'id'), text(params, 'version')),
9
+ 'hub.readDocument': (params) => harness.hub.readDocument(text(params, 'id'), text(params, 'version'))
10
+ };
11
+ }
@@ -21,11 +21,14 @@ export type { ApprovalFacts, ApprovalPolicy, AutomaticReason } from "./sessions/
21
21
  export type { ScopeDecision, ToolCall, ToolPolicy } from "./sessions/policy.js";
22
22
  export type { ActiveRun, ApprovalDecision, ApprovalInput, QuestionAnswer, QuestionInput } from "./sessions/registry.js";
23
23
  export type { RunFields, SessionRunInput } from "./sessions/sessions.js";
24
+ export { SideQuestions, SIDE_QUESTION_INSTRUCTION, buildSideQuestionText, type SideQuestionContext, type SideQuestionRead, type SideQuestionStartInput } from "./sessions/sideQuestions.js";
24
25
  export type { Effort } from "./sessions/runner.js";
25
26
  export { describeTool, HARNESS_TOOLS, DEFAULT_TOOL_NAMES, WIKI_WRITE, type HarnessCall, type HarnessToolDefinition } from "./tools/definitions.js";
26
27
  export type { ToolProvider, ToolResult, ToolSession, ToolSpec } from "./tools/types.js";
27
28
  export { SPAWN_SUBAGENT, SUBAGENT_RELATION } from "./tools/spawnSubagent.js";
28
29
  export { CHECKLIST_WRITE, checklistOf } from "./tools/checklist.js";
30
+ export { BUILTIN_PROFILES } from "./agents/builtin.js";
31
+ export { PROFILE_COLORS, createProfile, listProfiles, parseProfile, profileDirs, removeProfile, resolveProfile, type AgentProfile, type ProfileColor, type ProfileListing, type ProfileProblem, type ProfileScope, type ProfileSource, type ProfileVariant } from "./agents/profiles.js";
29
32
  export { continuationOf, type Continuation, type ContinuationBlock } from "./sessions/continuation.js";
30
33
  export { webTools, WEB_PLUGIN, WEB_SEARCH_KEY, type WebToolOptions } from "./web/tools.js";
31
34
  export { safeRequest, type WebRequest, type WebResponse } from "./web/safeRequest.js";
@@ -60,4 +63,11 @@ export { PROTOCOL_VERSION, RpcPeer, stdioTransport, type Handler, type Transport
60
63
  export { attach, serve, type Served } from "./server/serve.js";
61
64
  export { DEFAULT_SUBAGENT_DEPTH, MAX_SUBAGENT_DEPTH, type SubagentOptions, type SubagentInput } from "./sessions/subagents.js";
62
65
  export { Subagents } from "./sessions/subagentService.js";
66
+ export { Worktrees } from "./worktrees/service.js";
67
+ export type { CreateWorktreeInput, CreateWorktreeResult, HandoffResult, WorktreeRecord, WorktreeStatus, WorktreesOptions, WorktreeKind } from "./worktrees/types.js";
63
68
  export { WAIT_SUBAGENTS, type SubagentResult, type SubagentSelection } from "./sessions/subagents.js";
69
+ export * as pageNotes from "./annotations/store.js";
70
+ export type { Note as PageNote, NoteApp as PageNoteApp, NoteDelivery as PageNoteDelivery, NoteInput as PageNoteInput, NoteSource as PageNoteSource, NoteTarget as PageNoteTarget } from "./annotations/store.js";
71
+ export { PAGE_SCRIPT } from "./annotations/pageScript.js";
72
+ export { NoteDeliveries, type SendResult } from "./annotations/delivery.js";
73
+ export type { CaptureTargetSpec, CaptureWindow, InspectState } from "./annotations/capture.js";
package/dist/sdk/index.js CHANGED
@@ -20,9 +20,12 @@ export { fileSecretStore, memorySecretStore, secretKey } from "./accounts/secret
20
20
  export { dispatch as harnessCall } from "./tools/dispatch.js";
21
21
  export { compile, render, SECTION_ORDER, CONTEXT_MESSAGE_TYPE, CONTEXT_SNAPSHOT_KEY } from "./prompt/compile.js";
22
22
  export { runEventSchema, runRecordSchema, isActiveStatus } from "./sessions/contracts.js";
23
+ export { SideQuestions, SIDE_QUESTION_INSTRUCTION, buildSideQuestionText } from "./sessions/sideQuestions.js";
23
24
  export { describeTool, HARNESS_TOOLS, DEFAULT_TOOL_NAMES, WIKI_WRITE } from "./tools/definitions.js";
24
25
  export { SPAWN_SUBAGENT, SUBAGENT_RELATION } from "./tools/spawnSubagent.js";
25
26
  export { CHECKLIST_WRITE, checklistOf } from "./tools/checklist.js";
27
+ export { BUILTIN_PROFILES } from "./agents/builtin.js";
28
+ export { PROFILE_COLORS, createProfile, listProfiles, parseProfile, profileDirs, removeProfile, resolveProfile } from "./agents/profiles.js";
26
29
  export { continuationOf } from "./sessions/continuation.js";
27
30
  export { webTools, WEB_PLUGIN, WEB_SEARCH_KEY } from "./web/tools.js";
28
31
  export { safeRequest } from "./web/safeRequest.js";
@@ -45,4 +48,8 @@ export { PROTOCOL_VERSION, RpcPeer, stdioTransport } from "./server/rpc.js";
45
48
  export { attach, serve } from "./server/serve.js";
46
49
  export { DEFAULT_SUBAGENT_DEPTH, MAX_SUBAGENT_DEPTH } from "./sessions/subagents.js";
47
50
  export { Subagents } from "./sessions/subagentService.js";
51
+ export { Worktrees } from "./worktrees/service.js";
48
52
  export { WAIT_SUBAGENTS } from "./sessions/subagents.js";
53
+ export * as pageNotes from "./annotations/store.js";
54
+ export { PAGE_SCRIPT } from "./annotations/pageScript.js";
55
+ export { NoteDeliveries } from "./annotations/delivery.js";
@@ -449,11 +449,12 @@ export class Connections {
449
449
  async launch(connection) {
450
450
  if (connection.kind === 'remote') {
451
451
  const headers = {};
452
- for (const key of connection.headerKeys) {
453
- const value = await this.secret(connection.id, headerItem(key));
454
- if (value !== null)
455
- headers[key] = value;
456
- }
452
+ if (connection.auth === 'headers')
453
+ for (const key of connection.headerKeys) {
454
+ const value = await this.secret(connection.id, headerItem(key));
455
+ if (value !== null)
456
+ headers[key] = value;
457
+ }
457
458
  return { id: connection.id, name: connection.name, transport: 'http', url: connection.url ?? '', headers, auth: connection.auth, scopes: connection.oauthScopes };
458
459
  }
459
460
  let command = findBinary(connection.command) ?? (isAbsolute(connection.command) ? connection.command : null);
@@ -573,7 +574,13 @@ export class Connections {
573
574
  throw scopeMigrationError();
574
575
  }
575
576
  connection.envKeys = [...(existing?.envKeys ?? [])];
576
- connection.headerKeys = [...(existing?.headerKeys ?? [])];
577
+ const keepHeaders = remote && connection.auth === 'headers' && existing?.kind === 'remote'
578
+ && existing.url === connection.url && existing.auth === 'headers';
579
+ connection.headerKeys = keepHeaders ? [...existing.headerKeys] : [];
580
+ if (!keepHeaders) {
581
+ for (const key of existing?.headerKeys ?? [])
582
+ await this.deleteSecret(connection.id, headerItem(key));
583
+ }
577
584
  // A sign-in belongs to one server and one client; never carry it to another.
578
585
  const sameServer = (existing?.url ?? null) === connection.url && (existing?.auth ?? '') === connection.auth && existing?.oauthClient === connection.oauthClient && existing?.catalogId === connection.catalogId && !oauthCredentials;
579
586
  if (!sameServer || connection.auth !== 'oauth')
@@ -588,7 +595,7 @@ export class Connections {
588
595
  else
589
596
  await this.deleteSecret(connection.id, OAUTH_CLIENT_SECRET);
590
597
  }
591
- for (const [key, value] of Object.entries(headers ?? {})) {
598
+ for (const [key, value] of Object.entries(remote && connection.auth === 'headers' ? headers ?? {} : {})) {
592
599
  if (!/^[A-Za-z0-9_-]+$/.test(key) || /[\r\n\0]/.test(value))
593
600
  throw invalid('Invalid header name or value.');
594
601
  await this.setSecret(connection.id, headerItem(key), value);
@@ -0,0 +1,3 @@
1
+ import type { Harness } from "../harness.js";
2
+ import type { Handler } from "../server/rpc.js";
3
+ export declare function connectedAppServerMethods(harness: Harness): Record<string, Handler>;
@@ -0,0 +1,22 @@
1
+ import { validateScopeIds } from "./connections.js";
2
+ import { optional, text } from "../server/params.js";
3
+ export function connectedAppServerMethods(harness) {
4
+ const apps = harness.apps;
5
+ return {
6
+ 'connections.list': () => apps.connections.list(),
7
+ 'connections.forScope': (params) => apps.connections.forScope({ agentId: optional(params, 'agentId'), projectId: optional(params, 'projectId') }),
8
+ 'connections.save': (params) => apps.save(params.input),
9
+ 'connections.allowTool': (params) => apps.connections.allowTool(text(params, 'connectionId'), text(params, 'tool')),
10
+ 'connections.remove': (params) => apps.remove(text(params, 'connectionId')),
11
+ 'connections.test': (params) => apps.test(text(params, 'connectionId')),
12
+ 'connections.signIn': (params) => apps.signIn(text(params, 'connectionId')),
13
+ 'connections.signOut': (params) => apps.signOut(text(params, 'connectionId')),
14
+ 'connections.runContext': (params) => apps.runContext(text(params, 'runId')),
15
+ 'connections.refreshSource': (params) => apps.refreshSource(text(params, 'runId'), text(params, 'traceId'), text(params, 'sourceUri')),
16
+ 'connections.setAgentScope': (params) => apps.setAgentScope(params.creator ?? {}, text(params, 'agentId'), Array.isArray(params.ids) ? params.ids : []),
17
+ 'connections.removeAgentScope': (params) => apps.removeAgentScope(text(params, 'agentId')),
18
+ 'connections.revision': () => apps.connections.revision(),
19
+ 'connections.appClients': () => apps.appClients(),
20
+ 'connections.validateScope': (params) => validateScopeIds(apps.connections.list(), { agentId: optional(params, 'agentId'), projectId: optional(params, 'projectId') }, Array.isArray(params.ids) ? params.ids : [])
21
+ };
22
+ }
@@ -3,4 +3,5 @@ import type { PluginDescriptor } from "./types.js";
3
3
  export declare const PI_PLUGIN: PluginDescriptor;
4
4
  /** The variant: the eight components that shape what an agent is told and can do. */
5
5
  export declare const VARIANT_PLUGIN: PluginDescriptor;
6
+ export declare const BUILTIN_PLUGIN_REGISTRY: import("./builtinRegistry.js").BuiltinPluginRegistry;
6
7
  export declare const HARNESS_PLUGINS: readonly PluginDescriptor[];
@@ -1,21 +1,45 @@
1
- // The harness's own parts, described as plugins. Each is a description of code that already
2
- // exists; none of it is loaded through Pi's extension system.
1
+ // Built-in feature modules register their contributions here; this registry never loads Pi code.
3
2
  import { CONNECTION_TOOLS, NATIVE_GROUP } from "../prompt/compile.js";
4
- import { ASK_USER } from "../tools/askUser.js";
5
- import { CHECKLIST_WRITE } from "../tools/checklist.js";
3
+ import { ASK_USER, askUserTool } from "../tools/askUser.js";
4
+ import { CHECKLIST_WRITE, checklistTool } from "../tools/checklist.js";
6
5
  import { SPAWN_SUBAGENT, WAIT_SUBAGENTS } from "../sessions/subagents.js";
6
+ import { subagentTools } from "../tools/spawnSubagent.js";
7
+ import { SESSION_NAME } from "../tools/sessionName.js";
7
8
  import { RECALL_TOOLS, SKILL_TOOLS, TRAJECTORY_TOOLS, WIKI_READ_TOOLS, WIKI_WRITE_TOOLS } from "../tools/definitions.js";
8
9
  import { FIRST_PARTY_PLUGINS } from "./firstParty.js";
9
10
  import { WEB_PLUGIN } from "../web/tools.js";
10
- import { BROWSER_PLUGIN } from "../browser/index.js";
11
+ import { KEYLESS_SEARCH, webPart } from "../web/part.js";
12
+ import { BROWSER_PLUGIN, browserPluginPart } from "../browser/index.js";
13
+ import { ANNOTATIONS_PLUGIN } from "../annotations/part.js";
14
+ import { annotationsPart } from "../annotations/part.js";
15
+ import { webServerMethods } from "../web/serverMethods.js";
16
+ import { browserServerMethods } from "../browser/serverMethods.js";
17
+ import { annotationServerMethods } from "../annotations/serverMethods.js";
18
+ import { doctorServerMethods } from "../doctor/serverMethods.js";
19
+ import { worktreeServerMethods } from "../worktrees/serverMethods.js";
20
+ import { hubServerMethods } from "../hub/serverMethods.js";
21
+ import { refinementServerMethods } from "../refine/serverMethods.js";
22
+ import { wikiServerMethods } from "../wiki/serverMethods.js";
23
+ import { historyServerMethods } from "../history/serverMethods.js";
24
+ import { subagentServerMethods } from "../sessions/subagentServerMethods.js";
25
+ import { sideQuestionServerMethods } from "../sessions/sideQuestionServerMethods.js";
26
+ import { connectedAppServerMethods } from "../mcp/serverMethods.js";
27
+ import { harnessTools } from "../tools/harnessTools.js";
28
+ import { createBuiltinPluginRegistry } from "./builtinRegistry.js";
11
29
  const tools = (definitions) => definitions.map((tool) => ({ name: tool.name, description: tool.label }));
30
+ const toolContribution = (definitions, toolOrder) => ({
31
+ toolOrder,
32
+ tools: (context) => context.harnessTools
33
+ ? harnessTools(context.env, context.caller, { variantDir: context.variantDir, definitions })
34
+ : []
35
+ });
12
36
  /** Pi itself. Its command's built-in extensions are added to it as they are found. */
13
37
  export const PI_PLUGIN = {
14
38
  id: 'pi',
15
39
  title: 'Pi',
16
40
  description: 'The engine every session runs on: its base prompt, its file and shell tools, and the extensions built into its command.',
17
41
  required: 'Pi runs every session.',
18
- tools: NATIVE_GROUP.map((name) => ({ name })),
42
+ tools: [...NATIVE_GROUP.map((name) => ({ name })), { name: SESSION_NAME }],
19
43
  toolGroups: ['Native']
20
44
  };
21
45
  /** The variant: the eight components that shape what an agent is told and can do. */
@@ -25,6 +49,7 @@ export const VARIANT_PLUGIN = {
25
49
  description: 'The agent\'s own variant: system prompt, guardrails, memory and tool guidance in every prompt; skills, scripts and subagents on demand; plugins in the tool loop. Versioned, and refined from evidence.',
26
50
  required: 'Every agent runs on a variant.',
27
51
  sections: [
52
+ { id: 'harness-identity', name: 'Harness identity', description: 'Fixed foundation identity; not part of the variant.' },
28
53
  { id: 'system', name: 'System prompt', description: 'c1: role and procedure.' },
29
54
  { id: 'guardrails', name: 'Guardrails', description: 'c2: standing rules.' },
30
55
  { id: 'memory', name: 'Memory', description: 'c3: durable facts about this kind of work.' },
@@ -34,35 +59,105 @@ export const VARIANT_PLUGIN = {
34
59
  ],
35
60
  tools: tools(SKILL_TOOLS)
36
61
  };
37
- export const HARNESS_PLUGINS = [
38
- PI_PLUGIN,
39
- { id: 'subagents', title: 'Subagents', description: 'Isolated child runs of the same caller, bounded by parent capabilities and nesting depth.', required: 'Availability is selected by the session host; child runs retain the parent permission boundary.', tools: [{ name: SPAWN_SUBAGENT, description: 'Start a subagent' }, { name: WAIT_SUBAGENTS, description: 'Wait for subagents' }] },
40
- VARIANT_PLUGIN,
62
+ const SUBAGENTS_PLUGIN = { id: 'subagents', title: 'Subagents', description: 'Isolated child runs of the same caller, bounded by parent capabilities and nesting depth.', required: 'Availability is selected by the session host; child runs retain the parent permission boundary.', tools: [{ name: SPAWN_SUBAGENT, description: 'Start a subagent' }, { name: WAIT_SUBAGENTS, description: 'Wait for subagents' }] };
63
+ const WIKI_PLUGIN = {
64
+ id: 'wiki',
65
+ title: 'Wiki',
66
+ description: 'Notes about projects, linked to each other, that agents search and read.',
67
+ required: 'Project notes are reached through it.',
68
+ tools: tools([...WIKI_READ_TOOLS, ...WIKI_WRITE_TOOLS]),
69
+ sections: [
70
+ { id: 'wiki', name: 'Wiki', description: 'The current project\'s notes.' },
71
+ { id: 'references', name: 'Selected references', description: 'Notes pinned to one request.' }
72
+ ],
73
+ toolGroups: ['Wiki']
74
+ };
75
+ const HISTORY_PLUGIN = { id: 'history', title: 'History', description: 'Search earlier conversations and inspect recorded run trajectories.', required: 'Earlier runs are how an agent picks up where work stopped.', tools: tools([...RECALL_TOOLS, ...TRAJECTORY_TOOLS]), toolGroups: ['History'] };
76
+ const ASK_USER_PLUGIN = { id: 'ask-user', title: 'Ask the user', description: 'Lets the agent stop and ask a question with choices.', required: 'An agent must be able to stop and ask you.', tools: [{ name: ASK_USER }] };
77
+ const CHECKLIST_PLUGIN = { id: 'checklist', title: 'Checklist', description: 'Lets the agent keep a checklist of the steps of its work, recorded with the run so progress can be shown and picked up again.', required: 'Progress of longer work is kept through it.', tools: [{ name: CHECKLIST_WRITE, description: 'Write the checklist' }] };
78
+ const CONNECTED_APPS_PLUGIN = {
79
+ id: 'connected-apps',
80
+ title: 'Connected apps',
81
+ description: 'Outside services reached over MCP, behind two tools that list and call what is connected.',
82
+ required: 'Connected apps are reached only through it. Each app is switched on its own.',
83
+ manage: 'connections',
84
+ tools: CONNECTION_TOOLS.map((name) => ({ name })),
85
+ toolGroups: ['Connections']
86
+ };
87
+ const DOCTOR_PLUGIN = { id: 'doctor', title: 'Doctor', description: 'Local readiness checks and explicitly confirmed live probes for accounts, models and tools.', required: 'Every local terminal has readiness diagnostics.' };
88
+ const WORKTREES_PLUGIN = { id: 'worktrees', title: 'Worktrees', description: 'Create and manage isolated git worktrees, move changes between them, and restore removed worktrees.', required: 'Managed worktrees are available to every local terminal.' };
89
+ const HUB_PLUGIN = { id: 'hub', title: 'Hub', description: 'Browse and install reviewed skills, plugins and documents from configured registries.', required: 'Reviewed Harness resources are available to every host.' };
90
+ const REFINEMENT_PLUGIN = { id: 'refinement', title: 'Refinement', description: 'Improve variants from recorded evidence through isolated refinement runs and reviewed candidates.', required: 'Variant refinement is available to every host.' };
91
+ const SIDE_QUESTIONS_PLUGIN = {
92
+ id: 'side-questions',
93
+ title: 'Side questions',
94
+ description: 'Separate, tool-free answers that use a redacted projection of the main conversation.',
95
+ required: 'Every host can ask a side question without steering its main run.',
96
+ sections: [{ id: 'side-question', name: 'Side-question role', description: 'Fixed instructions for an independent, tool-free side run.' }]
97
+ };
98
+ export const BUILTIN_PLUGIN_REGISTRY = createBuiltinPluginRegistry([
41
99
  {
42
- id: 'wiki',
43
- title: 'Wiki',
44
- description: 'Notes about projects, linked to each other, that agents search and read.',
45
- required: 'Project notes are reached through it.',
46
- tools: tools([...WIKI_READ_TOOLS, ...WIKI_WRITE_TOOLS]),
47
- sections: [
48
- { id: 'wiki', name: 'Wiki', description: 'The current project\'s notes.' },
49
- { id: 'references', name: 'Selected references', description: 'Notes pinned to one request.' }
100
+ descriptor: PI_PLUGIN,
101
+ toolOrder: 60,
102
+ tools: context => context.harnessTools ? [context.sessionName] : []
103
+ },
104
+ {
105
+ descriptor: SUBAGENTS_PLUGIN,
106
+ toolOrder: 70,
107
+ tools: context => context.allowedTools.includes(SPAWN_SUBAGENT)
108
+ ? subagentTools(context.subagents, context.runId).filter(tool => context.allowedTools.includes(tool.name))
109
+ : [],
110
+ commands: [{ name: 'subagents', description: 'Watch current subagent tasks and activity' }, { name: 'agents', description: 'Browse and edit agent profiles' }],
111
+ serverMethods: subagentServerMethods
112
+ },
113
+ { descriptor: VARIANT_PLUGIN, ...toolContribution(SKILL_TOOLS, 30) },
114
+ { descriptor: WIKI_PLUGIN, ...toolContribution([...WIKI_READ_TOOLS, ...WIKI_WRITE_TOOLS], 20), serverMethods: wikiServerMethods },
115
+ {
116
+ descriptor: HISTORY_PLUGIN,
117
+ ...toolContribution([...RECALL_TOOLS, ...TRAJECTORY_TOOLS], 10),
118
+ commands: [{ name: 'trajectory', description: 'Inspect the recorded runs of this session' }],
119
+ serverMethods: historyServerMethods
120
+ },
121
+ { descriptor: ASK_USER_PLUGIN, toolOrder: 40, tools: context => context.harnessTools && context.allowedTools.includes(ASK_USER) ? [askUserTool(context.run)] : [] },
122
+ { descriptor: CHECKLIST_PLUGIN, toolOrder: 50, tools: context => context.harnessTools ? [checklistTool(context.run)] : [] },
123
+ {
124
+ descriptor: CONNECTED_APPS_PLUGIN,
125
+ toolOrder: 0,
126
+ tools: context => context.connectionTools?.() ?? [],
127
+ serverMethods: connectedAppServerMethods
128
+ },
129
+ { descriptor: HUB_PLUGIN, serverMethods: hubServerMethods },
130
+ { descriptor: REFINEMENT_PLUGIN, serverMethods: refinementServerMethods },
131
+ { descriptor: SIDE_QUESTIONS_PLUGIN, serverMethods: sideQuestionServerMethods },
132
+ {
133
+ descriptor: WEB_PLUGIN,
134
+ createWorldPart: ({ options, setting }) => webPart({ secrets: options.secrets, request: options.request, keyless: () => setting(WEB_PLUGIN.id, KEYLESS_SEARCH) === 1 }),
135
+ serverMethods: webServerMethods
136
+ },
137
+ {
138
+ descriptor: BROWSER_PLUGIN,
139
+ createWorldPart: ({ browserEngine }) => browserEngine ? browserPluginPart(browserEngine) : null,
140
+ serverMethods: browserServerMethods
141
+ },
142
+ {
143
+ descriptor: ANNOTATIONS_PLUGIN,
144
+ commands: [
145
+ { name: 'notes', description: 'Page notes: keep notes on web pages and share them with this session' },
146
+ { name: 'annotate', description: 'Inspect a page or app, comment on components, send fixes to the agent: /plugin:annotate [url | --app <devtools address> | --bridge]' }
50
147
  ],
51
- toolGroups: ['Wiki']
148
+ createWorldPart: ({ options }) => annotationsPart({ root: options.root, changed: options.notesChanged }),
149
+ serverMethods: annotationServerMethods
150
+ },
151
+ {
152
+ descriptor: DOCTOR_PLUGIN,
153
+ commands: [{ name: 'doctor', description: 'Check account, model, tools and runtime readiness' }],
154
+ serverMethods: doctorServerMethods
52
155
  },
53
- { id: 'history', title: 'History', description: 'Search earlier conversations and inspect recorded run trajectories.', required: 'Earlier runs are how an agent picks up where work stopped.', tools: tools([...RECALL_TOOLS, ...TRAJECTORY_TOOLS]), toolGroups: ['History'] },
54
- { id: 'ask-user', title: 'Ask the user', description: 'Lets the agent stop and ask a question with choices.', required: 'An agent must be able to stop and ask you.', tools: [{ name: ASK_USER }] },
55
- { id: 'checklist', title: 'Checklist', description: 'Lets the agent keep a checklist of the steps of its work, recorded with the run so progress can be shown and picked up again.', required: 'Progress of longer work is kept through it.', tools: [{ name: CHECKLIST_WRITE, description: 'Write the checklist' }] },
56
156
  {
57
- id: 'connected-apps',
58
- title: 'Connected apps',
59
- description: 'Outside services reached over MCP, behind two tools that list and call what is connected.',
60
- required: 'Connected apps are reached only through it. Each app is switched on its own.',
61
- manage: 'connections',
62
- tools: CONNECTION_TOOLS.map((name) => ({ name })),
63
- toolGroups: ['Connections']
157
+ descriptor: WORKTREES_PLUGIN,
158
+ commands: [{ name: 'worktree', description: 'Work in a managed git worktree: new, switch, hand off, remove' }],
159
+ serverMethods: worktreeServerMethods
64
160
  },
65
- WEB_PLUGIN,
66
- BROWSER_PLUGIN,
67
- ...FIRST_PARTY_PLUGINS
68
- ];
161
+ ...FIRST_PARTY_PLUGINS.map(descriptor => ({ descriptor }))
162
+ ]);
163
+ export const HARNESS_PLUGINS = BUILTIN_PLUGIN_REGISTRY.descriptors();