@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,53 @@
1
+ import type { Harness } from "../harness.js";
2
+ import type { Handler } from "../server/rpc.js";
3
+ import type { WorldOptions, WorldPart } from "../world/world.js";
4
+ import type { BrowserEngine } from "../browser/index.js";
5
+ import type { CallerContext } from "../core/caller.js";
6
+ import type { DispatchEnv } from "../tools/dispatch.js";
7
+ import type { ToolSpec } from "../tools/types.js";
8
+ import type { Subagents } from "../sessions/subagentService.js";
9
+ import type { ActiveRun } from "../sessions/registry.js";
10
+ import type { PluginDescriptor } from "./types.js";
11
+ export interface BuiltinPluginCommand {
12
+ pluginId: string;
13
+ name: string;
14
+ description: string;
15
+ namespace: 'unprefixed' | 'plugin';
16
+ }
17
+ export interface BuiltinWorldContext {
18
+ options: WorldOptions;
19
+ setting(pluginId: string, key: string): number;
20
+ browserEngine: BrowserEngine | null;
21
+ }
22
+ export interface BuiltinToolContext {
23
+ env: DispatchEnv;
24
+ caller: CallerContext;
25
+ variantDir: string | null;
26
+ run: ActiveRun;
27
+ runId: string;
28
+ allowedTools: readonly string[];
29
+ harnessTools: boolean;
30
+ subagents: Subagents;
31
+ sessionName: ToolSpec;
32
+ connectionTools?: () => ToolSpec[];
33
+ }
34
+ export interface BuiltinPluginModule {
35
+ descriptor: PluginDescriptor;
36
+ /** Stable order for model tools, independent of descriptor display order. */
37
+ toolOrder?: number;
38
+ tools?: (context: BuiltinToolContext) => ToolSpec[];
39
+ commands?: readonly Omit<BuiltinPluginCommand, 'pluginId' | 'namespace'>[];
40
+ createWorldPart?: (context: BuiltinWorldContext) => WorldPart | null;
41
+ serverMethods?: (harness: Harness) => Record<string, Handler>;
42
+ }
43
+ export interface BuiltinPluginRegistry {
44
+ readonly modules: readonly BuiltinPluginModule[];
45
+ descriptors(): PluginDescriptor[];
46
+ commands(): BuiltinPluginCommand[];
47
+ command(name: string): BuiltinPluginCommand | undefined;
48
+ tools(context: BuiltinToolContext): ToolSpec[];
49
+ worldParts(context: BuiltinWorldContext): WorldPart[];
50
+ serverMethods(harness: Harness): Record<string, Handler>;
51
+ }
52
+ /** One checked registry for the Harness-owned plugin modules and their surface contributions. */
53
+ export declare function createBuiltinPluginRegistry(modules: readonly BuiltinPluginModule[]): BuiltinPluginRegistry;
@@ -0,0 +1,49 @@
1
+ /** One checked registry for the Harness-owned plugin modules and their surface contributions. */
2
+ export function createBuiltinPluginRegistry(modules) {
3
+ const pluginIds = new Set();
4
+ const commandNames = new Set();
5
+ for (const module of modules) {
6
+ const id = module.descriptor.id;
7
+ if (pluginIds.has(id))
8
+ throw new Error(`Duplicate built-in plugin id: ${id}`);
9
+ pluginIds.add(id);
10
+ for (const command of module.commands ?? []) {
11
+ if (!/^[a-z][a-z0-9-]*$/i.test(command.name))
12
+ throw new Error(`Invalid built-in plugin command: ${command.name}`);
13
+ if (commandNames.has(command.name))
14
+ throw new Error(`Duplicate built-in plugin command: ${command.name}`);
15
+ commandNames.add(command.name);
16
+ }
17
+ }
18
+ const commands = modules.flatMap(module => (module.commands ?? []).map(command => ({
19
+ pluginId: module.descriptor.id,
20
+ ...command,
21
+ namespace: module.descriptor.required ? 'unprefixed' : 'plugin'
22
+ })));
23
+ const byCommand = new Map(commands.map(command => [command.name, command]));
24
+ const toolModules = modules.map((module, index) => ({ module, index })).filter(({ module }) => module.tools)
25
+ .sort((left, right) => (left.module.toolOrder ?? left.index) - (right.module.toolOrder ?? right.index));
26
+ return {
27
+ modules,
28
+ descriptors: () => modules.map(module => module.descriptor),
29
+ commands: () => [...commands],
30
+ command: name => byCommand.get(name),
31
+ tools: context => toolModules.flatMap(({ module }) => module.tools(context)),
32
+ worldParts: context => modules.flatMap(module => {
33
+ const part = module.createWorldPart?.(context);
34
+ return part ? [part] : [];
35
+ }),
36
+ serverMethods: harness => {
37
+ const methods = {};
38
+ for (const module of modules) {
39
+ const contributed = module.serverMethods?.(harness) ?? {};
40
+ for (const [name, handler] of Object.entries(contributed)) {
41
+ if (name in methods)
42
+ throw new Error(`Duplicate built-in server method: ${name}`);
43
+ methods[name] = handler;
44
+ }
45
+ }
46
+ return methods;
47
+ }
48
+ };
49
+ }
@@ -1,5 +1,5 @@
1
1
  import { Harness, type HarnessOptions } from "../harness.js";
2
- export declare const PLUGINS_USAGE = "Usage: xharness plugins <command> [options]\n\n list Every plugin and whether it is on\n show <plugin id> One plugin: what it brings, where it is on, its code\n install <source> A folder, an npm name, or a git address. It arrives off and untrusted\n remove <plugin id> Remove a plugin you installed\n update [plugin id] Fetch a newer version of one installed plugin, or of all\n attach <id> Switch a plugin, or one thing it brings, on\n detach <id> Switch it off\n trust <plugin id> [--yes] Show the files of its code, then trust them\n untrust <plugin id> Stop trusting its code\n\nOptions:\n --data <dir> Data directory (default: ~/.xingular-harness/data)\n --state <dir> State directory (default: ~/.xingular-harness/state)\n --cwd <dir> Also list what this working folder brings\n --project <id> attach/detach for one project instead of everywhere\n --agent <id> attach/detach for one agent instead of everywhere\n";
2
+ export declare const PLUGINS_USAGE = "Usage: xagent plugins <command> [options]\n\n list Every plugin and whether it is on\n show <plugin id> One plugin: what it brings, where it is on, its code\n install <source> A folder, an npm name, or a git address. It arrives off and untrusted\n remove <plugin id> Remove a plugin you installed\n update [plugin id] Fetch a newer version of one installed plugin, or of all\n attach <id> Switch a plugin, or one thing it brings, on\n detach <id> Switch it off\n trust <plugin id> [--yes] Show the files of its code, then trust them\n untrust <plugin id> Stop trusting its code\n\nOptions:\n --data <dir> Data directory (default: ~/.xingular-harness/data)\n --state <dir> State directory (default: ~/.xingular-harness/state)\n --cwd <dir> Also list what this working folder brings\n --project <id> attach/detach for one project instead of everywhere\n --agent <id> attach/detach for one agent instead of everywhere\n";
3
3
  export interface PluginsCliDependencies {
4
4
  home?: string;
5
5
  cwd?: string;
@@ -1,12 +1,13 @@
1
- // `xharness plugins …`: plugin control for a person using the harness without an
1
+ // `xagent plugins …`: plugin control for a person using the harness without an
2
2
  // application. It runs outside any session, as the person who typed it, and acts directly.
3
3
  import { homedir } from 'node:os';
4
4
  import { join, resolve } from 'node:path';
5
5
  import { createInterface } from 'node:readline/promises';
6
+ import { DEFAULT_PRODUCT_NAME } from "../core/env.js";
6
7
  import { Harness } from "../harness.js";
7
8
  import * as api from "./api.js";
8
9
  import * as install from "./install.js";
9
- export const PLUGINS_USAGE = `Usage: xharness plugins <command> [options]
10
+ export const PLUGINS_USAGE = `Usage: xagent plugins <command> [options]
10
11
 
11
12
  list Every plugin and whether it is on
12
13
  show <plugin id> One plugin: what it brings, where it is on, its code
@@ -81,7 +82,7 @@ export async function runPluginsCli(argv, dependencies = {}) {
81
82
  }
82
83
  const base = dependencies.cwd ?? process.cwd();
83
84
  const root = join(dependencies.home ?? homedir(), '.xingular-harness');
84
- const options = { dataRoot: parsed.values.data ? resolve(base, parsed.values.data) : join(root, 'data'), stateDir: parsed.values.state ? resolve(base, parsed.values.state) : join(root, 'state'), productName: 'Xingular Harness' };
85
+ const options = { dataRoot: parsed.values.data ? resolve(base, parsed.values.data) : join(root, 'data'), stateDir: parsed.values.state ? resolve(base, parsed.values.state) : join(root, 'state'), productName: DEFAULT_PRODUCT_NAME };
85
86
  // Built without opening the data root: a plugin command must not settle runs or migrate a Wiki
86
87
  // that another process may be using.
87
88
  const harness = dependencies.createHarness ? dependencies.createHarness(options) : new Harness(options);
@@ -4,6 +4,7 @@ import type { Materialised } from "../variant/materialise.js";
4
4
  export declare const REFERENCES_BUDGET = 12000;
5
5
  /** Where the harness's own sections sit; an application's sections are placed among them by `order`. */
6
6
  export declare const SECTION_ORDER: {
7
+ readonly harnessIdentity: 5;
7
8
  readonly system: 25;
8
9
  readonly guardrails: 40;
9
10
  readonly memory: 45;
@@ -21,7 +22,7 @@ export interface Source {
21
22
  revision?: string | null;
22
23
  }
23
24
  export interface Section {
24
- /** `identity`, `role`, `system`, `guardrails`, `memory`, `references`, `subagents`, `scripts`, `wiki`, `tools`, or an application's own. */
25
+ /** `harness-identity`, `identity`, `role`, `system`, `guardrails`, `memory`, `references`, `subagents`, `scripts`, `wiki`, `tools`, or an application's own. */
25
26
  id: string;
26
27
  title: string;
27
28
  body: string;
@@ -105,6 +106,8 @@ export interface Launch {
105
106
  toolGroups?: HostToolGroup[];
106
107
  /** Appended to the tools section when it is cut. */
107
108
  toolsPointer?: string;
109
+ /** Suppresses tool guidance for a run that deliberately has no callable tools. */
110
+ hideToolGuidance?: boolean;
108
111
  /** Recorded with the pack: the application's name for who it is for. Defaults to `agent` or `session`. */
109
112
  profile?: string;
110
113
  }
@@ -129,7 +132,7 @@ export interface OrderedGroup extends ToolGroup {
129
132
  * The caller's tools by group, in a fixed order. History and the Wiki are always there; the rest
130
133
  * follows the switches in `launch.tools` and what the application provides.
131
134
  */
132
- export declare function toolGroups(context: CallerContext, launch: Launch): OrderedGroup[];
135
+ export declare function toolGroups(context: CallerContext, launch: Launch, dataRoot?: string): OrderedGroup[];
133
136
  /** Compiles the pack for a caller. It only reads the data root. */
134
137
  export declare function compile(env: HarnessEnv, context: CallerContext, launch?: Launch, variant?: Materialised | null): ContextPack;
135
138
  /** The pack as the one string a session receives. */
@@ -4,8 +4,8 @@
4
4
  // A pack is an ordered list of sections, each with its own character budget. The harness writes
5
5
  // the sections that come from the session's variant (instructions, guardrails, memory, tool
6
6
  // guidance, and the indexes of its subagents and scripts), the selected references, the Wiki
7
- // pointer, and the tools; the embedding application supplies the others (who the model is, its
8
- // role, what it is working on). Each run's pack is recorded in `runs/<id>/context.json`.
7
+ // pointer, and the tools. The foundation supplies its identity; the embedding application supplies
8
+ // any additional identity, role, and current work. Each run's pack is recorded in `runs/<id>/context.json`.
9
9
  import { readdirSync, readFileSync, statSync } from 'node:fs';
10
10
  import { join } from 'node:path';
11
11
  import { isSession, projectOf, usesCli } from "../core/caller.js";
@@ -13,7 +13,9 @@ import { isDir, now, readJson, safeId, writeJson } from "../core/fs.js";
13
13
  import { charCount, compareText, lines, takeChars } from "../core/text.js";
14
14
  import * as notes from "../wiki/notes.js";
15
15
  import { isWorldTool, WORLD_GUIDANCE } from "../world/untrusted.js";
16
+ import { CHECKLIST_WRITE } from "../tools/checklist.js";
16
17
  import { SESSION_NAME } from "../tools/sessionName.js";
18
+ const IDENTITY_BUDGET = 1000;
17
19
  const WIKI_BUDGET = 2500;
18
20
  const TOOLS_BUDGET = 8000;
19
21
  const REPORTING_BUDGET = 2000;
@@ -22,7 +24,7 @@ export const REFERENCES_BUDGET = 12000;
22
24
  const VARIANT_BUDGET = 30_000;
23
25
  const NOTES_LISTED = 25;
24
26
  /** Where the harness's own sections sit; an application's sections are placed among them by `order`. */
25
- export const SECTION_ORDER = { system: 25, guardrails: 40, memory: 45, references: 50, subagents: 64, scripts: 66, wiki: 80, tools: 90, reporting: 95 };
27
+ export const SECTION_ORDER = { harnessIdentity: 5, system: 25, guardrails: 40, memory: 45, references: 50, subagents: 64, scripts: 66, wiki: 80, tools: 90, reporting: 95 };
26
28
  /** How a run's final reply is shaped. Fixed text: it sits in the stable prefix of every pack. */
27
29
  export const REPORTING_GUIDANCE = `When you finish work, write the final reply for a person who did not watch you work.
28
30
 
@@ -100,12 +102,13 @@ function wiki(root, context) {
100
102
  * The caller's tools by group, in a fixed order. History and the Wiki are always there; the rest
101
103
  * follows the switches in `launch.tools` and what the application provides.
102
104
  */
103
- export function toolGroups(context, launch) {
105
+ export function toolGroups(context, launch, dataRoot) {
104
106
  const enabled = launch.tools ?? [];
105
107
  const session = isSession(context);
106
108
  const cli = usesCli(context);
107
109
  const native = NATIVE_GROUP.filter((name) => enabled.includes(name));
108
110
  const wikiTools = WIKI_GROUP.filter((name) => name !== 'wiki_write' || enabled.includes(name));
111
+ const readGuidance = `Read-only file tools accept absolute paths and paths beginning with ~ anywhere this process has operating-system read access. The working folder is a default location, not a repository boundary.${dataRoot ? ` The Harness data root is ${dataRoot}.` : ''}`;
109
112
  const groups = [
110
113
  {
111
114
  group: 'History',
@@ -127,12 +130,20 @@ export function toolGroups(context, launch) {
127
130
  group: 'Native',
128
131
  tools: native,
129
132
  order: GROUP_ORDER.Native,
130
- guidance: session ? "Files and shell: your harness's own tools, within this session's working folder and access level." : `Files (${native.join(', ')}): work in your own folder; edits and commands ask the user first unless already allowed.`
133
+ guidance: session
134
+ ? `Files and shell: ${readGuidance} Edits and commands still pass through the Harness gate.`
135
+ : `Files (${native.join(', ')}): ${readGuidance} Edits and commands still pass through the current gate.`
131
136
  },
132
137
  // Web, browser and computer tools: the harness's own, and their results are untrusted (world/untrusted.ts).
133
138
  { group: 'World', tools: enabled.filter((name) => isWorldTool(name) && !(launch.toolGroups ?? []).some((group) => group.tools.includes(name))), order: GROUP_ORDER.World, guidance: `Web, browser and screen: ${WORLD_GUIDANCE}` },
134
139
  ...(launch.toolGroups ?? []).map((group) => ({ group: group.group, tools: group.tools, order: group.order, guidance: group.guidance }))
135
140
  ];
141
+ if (enabled.includes(CHECKLIST_WRITE)) {
142
+ groups.push({
143
+ group: 'Progress', tools: [CHECKLIST_WRITE], order: 87,
144
+ guidance: 'Progress: for work with several steps, write a checklist early with checklist_write and keep it current. The user follows your work from it, and a subagent\'s checklist shows as its progress in the user\'s list of subagents.'
145
+ });
146
+ }
136
147
  if (enabled.includes(SESSION_NAME)) {
137
148
  groups.push({
138
149
  group: 'Session', tools: [SESSION_NAME], order: 88,
@@ -145,8 +156,10 @@ export function toolGroups(context, launch) {
145
156
  const present = groups.filter((group) => group.tools.length);
146
157
  // A session's file and shell tools are Pi's own, whatever the switches say.
147
158
  if (session && !present.some((group) => group.group === 'Native')) {
148
- present.push({ group: 'Native', tools: ['harness file and shell tools'], order: GROUP_ORDER.placeholder, guidance: "Files and shell: your harness's own tools, within this session's working folder and access level." });
159
+ present.push({ group: 'Native', tools: ['harness file and shell tools'], order: GROUP_ORDER.placeholder, guidance: `Files and shell: ${readGuidance} Edits and commands still pass through the Harness gate.` });
149
160
  }
161
+ if (launch.hideToolGuidance)
162
+ return [];
150
163
  return present.map((group, index) => ({ group, index })).sort((left, right) => left.group.order - right.group.order || left.index - right.index).map(({ group }) => group);
151
164
  }
152
165
  /** One paragraph per tool group the caller has, then the harness's own tool guidance. */
@@ -207,13 +220,17 @@ function hostSection(host) {
207
220
  /** Compiles the pack for a caller. It only reads the data root. */
208
221
  export function compile(env, context, launch = {}, variant = null) {
209
222
  const { root } = env;
210
- const groups = toolGroups(context, launch);
223
+ const groups = toolGroups(context, launch, root);
211
224
  const variantSource = variant ? [{ id: variant.scope ?? variant.dir, kind: 'variant', revision: variant.fingerprint }] : [];
212
225
  const built = [
226
+ {
227
+ order: SECTION_ORDER.harnessIdentity,
228
+ section: section('harness-identity', 'Harness identity', `You are an AI agent running in ${env.productName}.`, IDENTITY_BUDGET, '', [])
229
+ },
213
230
  ...(variant?.sections ?? []).map((entry) => ({ order: entry.order, section: section(entry.id, entry.title, entry.body, VARIANT_BUDGET, '', variantSource) })),
214
231
  { order: SECTION_ORDER.references, section: selectedReferences(root, launch) },
215
232
  { order: SECTION_ORDER.wiki, section: wiki(root, context) },
216
- { order: SECTION_ORDER.tools, section: tools(groups, variant?.toolGuidance ?? '', launch.toolsPointer ?? '') },
233
+ { order: SECTION_ORDER.tools, section: launch.hideToolGuidance ? null : tools(groups, variant?.toolGuidance ?? '', launch.toolsPointer ?? '') },
217
234
  { order: SECTION_ORDER.reporting, section: section('reporting', 'Reporting your work', REPORTING_GUIDANCE, REPORTING_BUDGET, '', []) },
218
235
  ...(launch.sections ?? []).map((host) => ({ order: host.order, section: hostSection(host) }))
219
236
  ].map((entry, index) => ({ ...entry, index }));
@@ -1,4 +1,4 @@
1
- export declare const REFINE_USAGE = "Usage: xharness refine <demo | run | matrix> [options]\n\n demo [--k N] [--mode parallel|sequential] [--out <dir>]\n Run the deterministic demo: no model, no credentials.\n run --variant <dir> --task <file.md> [--source <dir>] [--scope <id>] [--k N]\n [--mode parallel|sequential] [--data <dir>] [--state <dir>] [--model <id>]\n [--executor-model <id>] [--retain-workspaces]\n Refine a harness on one task with the default account. Costs about\n 4k executions and 3k improver sessions. --model sets the improver's\n model, --executor-model the executions' model. --retain-workspaces\n keeps each execution's files for grading afterwards.\n matrix --suite <file.json> [--data <dir>] [--out <file.json>]\n Evaluate variants on a task set with the demo executor: no model.\n Versions come from a variant directory ({ label, dir }) or a committed\n scope ({ label, scope, version? }); tasks are RefineTask records.\n Writes <data>/refine/<id>/matrix.json and --out's copy when given.\n";
1
+ export declare const REFINE_USAGE = "Usage: xagent refine <demo | run | matrix> [options]\n\n demo [--k N] [--mode parallel|sequential] [--out <dir>]\n Run the deterministic demo: no model, no credentials.\n run --variant <dir> --task <file.md> [--source <dir>] [--scope <id>] [--k N]\n [--mode parallel|sequential] [--data <dir>] [--state <dir>] [--model <id>]\n [--executor-model <id>] [--retain-workspaces]\n Refine a harness on one task with the default account. Costs about\n 4k executions and 3k improver sessions. --model sets the improver's\n model, --executor-model the executions' model. --retain-workspaces\n keeps each execution's files for grading afterwards.\n matrix --suite <file.json> [--data <dir>] [--out <file.json>]\n Evaluate variants on a task set with the demo executor: no model.\n Versions come from a variant directory ({ label, dir }) or a committed\n scope ({ label, scope, version? }); tasks are RefineTask records.\n Writes <data>/refine/<id>/matrix.json and --out's copy when given.\n";
2
2
  export declare function runRefineCli(argv: string[], io?: {
3
3
  write: (text: string) => void;
4
4
  }): Promise<number>;
@@ -1,4 +1,4 @@
1
- // `xharness refine`: refinement from a terminal.
1
+ // `xagent refine`: refinement from a terminal.
2
2
  //
3
3
  // refine demo [--k N] [--mode parallel|sequential] [--out <dir>]
4
4
  // Runs the deterministic ledger demo end to end with no model and no credentials.
@@ -23,7 +23,7 @@ import { DemoExecutor, DemoImprover, demoBaseVariant, demoTasks } from "./demo.j
23
23
  import { Library } from "./library.js";
24
24
  import { runRefinement } from "./runner.js";
25
25
  import { runMatrix } from "./suite.js";
26
- export const REFINE_USAGE = `Usage: xharness refine <demo | run | matrix> [options]
26
+ export const REFINE_USAGE = `Usage: xagent refine <demo | run | matrix> [options]
27
27
 
28
28
  demo [--k N] [--mode parallel|sequential] [--out <dir>]
29
29
  Run the deterministic demo: no model, no credentials.
@@ -0,0 +1,3 @@
1
+ import type { Harness } from "../harness.js";
2
+ import type { Handler } from "../server/rpc.js";
3
+ export declare function refinementServerMethods(harness: Harness): Record<string, Handler>;
@@ -0,0 +1,20 @@
1
+ import { optional, text } from "../server/params.js";
2
+ import { readSummary } from "./runner.js";
3
+ export function refinementServerMethods(harness) {
4
+ const root = () => harness.env.root;
5
+ return {
6
+ 'refine.start': (params) => harness.refine.start({ task: params.task, k: typeof params.k === 'number' ? params.k : 1, mode: params.mode === 'sequential' ? 'sequential' : 'parallel', improver: params.improver ?? {}, executor: params.executor ?? undefined, baseVersion: optional(params, 'baseVersion'), retainWorkspaces: params.retainWorkspaces === true, id: optional(params, 'id') ?? undefined }),
7
+ 'refine.get': (params) => harness.refine.get(text(params, 'id')),
8
+ 'refine.list': (params) => harness.refine.list({ scope: optional(params, 'scope'), limit: typeof params.limit === 'number' ? params.limit : undefined }),
9
+ 'refine.running': () => harness.refine.running(),
10
+ 'refine.cancel': async (params) => {
11
+ await harness.refine.cancel(text(params, 'id'));
12
+ return readSummary(root(), text(params, 'id'));
13
+ },
14
+ 'refine.candidates': (params) => harness.refine.candidates(text(params, 'id')),
15
+ 'refine.adopt': (params) => harness.refine.adopt(text(params, 'id'), { label: optional(params, 'label') }),
16
+ 'library.list': (params) => harness.refine.library(text(params, 'scope')),
17
+ 'library.read': (params) => harness.refine.libraryEntry(text(params, 'scope'), text(params, 'area'), text(params, 'task')),
18
+ 'library.forget': (params) => harness.refine.forget(text(params, 'scope'), text(params, 'area'), Array.isArray(params.tasks) ? params.tasks : [])
19
+ };
20
+ }
@@ -7,12 +7,10 @@ import { HarnessError, invalid } from "../core/errors.js";
7
7
  import * as capabilities from "../capabilities/api.js";
8
8
  import { loadRun as loadCapabilitySnapshot } from "../capabilities/runSnapshot.js";
9
9
  import { capabilitySet } from "../capabilities/runSnapshot.js";
10
- import { validateScopeIds } from "../mcp/connections.js";
11
- import * as recall from "../history/recall.js";
12
10
  import * as plugins from "../plugins/api.js";
11
+ import { BUILTIN_PLUGIN_REGISTRY } from "../plugins/builtin.js";
13
12
  import * as install from "../plugins/install.js";
14
13
  import { compile, load, render } from "../prompt/compile.js";
15
- import { readSummary } from "../refine/runner.js";
16
14
  import { continuationOf } from "../sessions/continuation.js";
17
15
  import { selectHistory } from "../sessions/history.js";
18
16
  import { runsSince } from "../sessions/runIndex.js";
@@ -25,14 +23,7 @@ import { seedVariant } from "../variant/seed.js";
25
23
  import * as variants from "../variant/store.js";
26
24
  import { PI_SURFACE } from "../variant/surface.js";
27
25
  import { applyEdit, autotrigger, VariantError, validate } from "../variant/variant.js";
28
- import * as notes from "../wiki/notes.js";
29
- const text = (params, name) => {
30
- const value = params[name];
31
- if (typeof value !== 'string' || !value)
32
- throw invalid(`${name} is required.`);
33
- return value;
34
- };
35
- const optional = (params, name) => (typeof params[name] === 'string' && params[name] ? params[name] : null);
26
+ import { optional, text } from "./params.js";
36
27
  const questionAnswer = (value) => {
37
28
  const answer = value && typeof value === 'object' && !Array.isArray(value) ? value : {};
38
29
  return {
@@ -64,32 +55,16 @@ function check(variant) {
64
55
  return { ok: false, problems: error instanceof VariantError ? error.problems : [error instanceof Error ? error.message : String(error)] };
65
56
  }
66
57
  }
67
- /** Only the named fields of a parameter object. */
68
- function pick(params, keys) {
69
- return Object.fromEntries(keys.filter((key) => key in params).map((key) => [key, params[key]]));
70
- }
71
58
  export function methods(harness) {
72
- const { accounts, approvals, apps, runs, sessions, terminals, env } = harness;
59
+ const { accounts, approvals, runs, sessions, terminals, env } = harness;
73
60
  const root = () => env.root;
74
61
  const variantsChanged = (scope) => harness.bus.emit({ type: 'variants.changed', scope });
75
- return {
62
+ const builtins = BUILTIN_PLUGIN_REGISTRY.serverMethods(harness);
63
+ const foundation = {
76
64
  // ---- The harness itself --------------------------------------------------------------------
77
- 'web.status': () => harness.world.searchStatus(),
78
- 'web.setKey': (params) => harness.world.setSearchKey(text(params, 'provider'), typeof params.key === 'string' ? params.key : null),
79
- 'browser.config': () => harness.world.browserConfig(),
80
- 'browser.configure': (params) => harness.world.configureBrowser(pick(params, ['headed', 'cdpUrl', 'executablePath', 'allowPrivate', 'allowedDomains'])),
81
65
  'harness.status': () => ({ dataRoot: harness.dataRoot, productName: env.productName, recovery: harness.recovery, activeRuns: runs.activeRunIds() }),
82
- 'doctor.start': (params) => harness.doctor.start(params),
83
- 'doctor.read': (params) => harness.doctor.read(text(params, 'id')),
84
- 'doctor.cancel': (params) => harness.doctor.cancel(text(params, 'id')),
85
66
  'harness.open': (params) => harness.open(text(params, 'dataRoot'), Array.isArray(params.agentIds) ? params.agentIds : []),
86
67
  'harness.rebuildIndex': () => harness.rebuildIndex(),
87
- 'hub.list': (params) => harness.hub.list(params.refresh === true),
88
- 'hub.get': (params) => harness.hub.get(text(params, 'id'), text(params, 'version'), params.preview === true),
89
- 'hub.install': (params) => harness.hub.install(text(params, 'id'), text(params, 'version')),
90
- 'hub.preview': (params) => harness.hub.preview(text(params, 'id'), text(params, 'version'), typeof params.offset === 'number' ? params.offset : 0),
91
- 'hub.uninstall': (params) => harness.hub.uninstall(text(params, 'id'), text(params, 'version')),
92
- 'hub.readDocument': (params) => harness.hub.readDocument(text(params, 'id'), text(params, 'version')),
93
68
  // ---- Accounts --------------------------------------------------------------------------------
94
69
  'accounts.list': () => accounts.overview(),
95
70
  'accounts.find': (params) => accounts.find(text(params, 'accountId')),
@@ -129,22 +104,6 @@ export function methods(harness) {
129
104
  'approvals.remember': (params) => approvals.remember(text(params, 'sessionId'), text(params, 'target')),
130
105
  'approvals.forget': (params) => approvals.forget(text(params, 'sessionId'), text(params, 'target')),
131
106
  'approvals.forgetSession': (params) => approvals.forgetSession(text(params, 'sessionId')),
132
- // ---- Connected apps --------------------------------------------------------------------------
133
- 'connections.list': () => apps.connections.list(),
134
- 'connections.forScope': (params) => apps.connections.forScope({ agentId: optional(params, 'agentId'), projectId: optional(params, 'projectId') }),
135
- 'connections.save': (params) => apps.save(params.input),
136
- 'connections.allowTool': (params) => apps.connections.allowTool(text(params, 'connectionId'), text(params, 'tool')),
137
- 'connections.remove': (params) => apps.remove(text(params, 'connectionId')),
138
- 'connections.test': (params) => apps.test(text(params, 'connectionId')),
139
- 'connections.signIn': (params) => apps.signIn(text(params, 'connectionId')),
140
- 'connections.signOut': (params) => apps.signOut(text(params, 'connectionId')),
141
- 'connections.runContext': (params) => apps.runContext(text(params, 'runId')),
142
- 'connections.refreshSource': (params) => apps.refreshSource(text(params, 'runId'), text(params, 'traceId'), text(params, 'sourceUri')),
143
- 'connections.setAgentScope': (params) => apps.setAgentScope(params.creator ?? {}, text(params, 'agentId'), Array.isArray(params.ids) ? params.ids : []),
144
- 'connections.removeAgentScope': (params) => apps.removeAgentScope(text(params, 'agentId')),
145
- 'connections.revision': () => apps.connections.revision(),
146
- 'connections.appClients': () => apps.appClients(),
147
- 'connections.validateScope': (params) => validateScopeIds(apps.connections.list(), { agentId: optional(params, 'agentId'), projectId: optional(params, 'projectId') }, Array.isArray(params.ids) ? params.ids : []),
148
107
  // ---- Variants: the eight components that shape an agent ---------------------------------
149
108
  'variants.surface': () => PI_SURFACE,
150
109
  'variants.list': () => variants.scopes(root()).map((scope) => {
@@ -227,45 +186,8 @@ export function methods(harness) {
227
186
  const built = materialise(root(), params.variant, { trust: harness.trust });
228
187
  return { ...built, files: undefined };
229
188
  },
230
- // ---- Refinement: improving a harness from evidence -------------------------------------------
231
- 'refine.start': (params) => harness.refine.start({ task: params.task, k: typeof params.k === 'number' ? params.k : 1, mode: params.mode === 'sequential' ? 'sequential' : 'parallel', improver: params.improver ?? {}, executor: params.executor ?? undefined, baseVersion: optional(params, 'baseVersion'), retainWorkspaces: params.retainWorkspaces === true, id: optional(params, 'id') ?? undefined }),
232
- 'refine.get': (params) => harness.refine.get(text(params, 'id')),
233
- 'refine.list': (params) => harness.refine.list({ scope: optional(params, 'scope'), limit: typeof params.limit === 'number' ? params.limit : undefined }),
234
- 'refine.running': () => harness.refine.running(),
235
- 'refine.cancel': async (params) => {
236
- await harness.refine.cancel(text(params, 'id'));
237
- return readSummary(root(), text(params, 'id'));
238
- },
239
- 'refine.candidates': (params) => harness.refine.candidates(text(params, 'id')),
240
- 'refine.adopt': (params) => harness.refine.adopt(text(params, 'id'), { label: optional(params, 'label') }),
241
- 'library.list': (params) => harness.refine.library(text(params, 'scope')),
242
- 'library.read': (params) => harness.refine.libraryEntry(text(params, 'scope'), text(params, 'area'), text(params, 'task')),
243
- 'library.forget': (params) => harness.refine.forget(text(params, 'scope'), text(params, 'area'), Array.isArray(params.tasks) ? params.tasks : []),
244
- // ---- The Wiki: project notes ------------------------------------------------------------------
245
- 'wiki.list': (params) => notes.list(root(), params.project === undefined ? {} : { project: optional(params, 'project') }),
246
- 'wiki.read': (params) => notes.read(root(), text(params, 'path')),
247
- 'wiki.write': (params) => {
248
- const note = notes.write(root(), text(params, 'path'), typeof params.text === 'string' ? params.text : '', { expectedUpdatedAt: optional(params, 'expectedUpdatedAt') });
249
- harness.bus.emit({ type: 'wiki.changed' });
250
- return note;
251
- },
252
- 'wiki.remove': (params) => {
253
- notes.remove(root(), text(params, 'path'));
254
- harness.bus.emit({ type: 'wiki.changed' });
255
- },
256
- 'wiki.search': (params) => notes.search(root(), typeof params.query === 'string' ? params.query : '', { project: optional(params, 'project'), limit: typeof params.limit === 'number' ? params.limit : undefined }),
257
- 'wiki.graph': () => notes.graph(root()),
258
- 'wiki.versions': (params) => notes.versions(root(), text(params, 'path')),
259
- 'wiki.version': (params) => notes.version(root(), text(params, 'path'), text(params, 'id')),
260
- 'wiki.ensureProject': () => {
261
- notes.ensureLayout(root(), env.productName);
262
- harness.bus.emit({ type: 'wiki.changed' });
263
- },
264
189
  // ---- What a session calls, on behalf of a caller the host names -------------------------------
265
190
  'harness.call': (params) => dispatch(env, (params.context ?? {}), text(params, 'call'), params.params ?? {}, { variantDir: optional(params, 'variantDir') }),
266
- // ---- Recall ------------------------------------------------------------------------------------
267
- 'recall.search': (params) => recall.searchAll(env, typeof params.query === 'string' ? params.query : '', params.filter ?? {}),
268
- 'recall.read': (params) => recall.read(root(), text(params, 'runId')),
269
191
  // ---- The compiled prompt -----------------------------------------------------------------------
270
192
  'context.compile': (params) => {
271
193
  const variant = params.variant ? materialise(root(), params.variant, { trust: harness.trust }) : null;
@@ -354,9 +276,6 @@ export function methods(harness) {
354
276
  runs.answerQuestions(text(params, 'runId'), text(params, 'batchId'), params.answers.map((item) => ({ questionId: text(item, 'questionId'), answer: questionAnswer(item.answer) })));
355
277
  },
356
278
  // ---- Sessions ----------------------------------------------------------------------------------
357
- 'subagents.spawn': (params) => harness.subagents.spawn(params),
358
- 'subagents.wait': (params) => harness.subagents.wait(params),
359
- 'subagents.results': (params) => harness.subagents.results(params),
360
279
  'sessions.spawnSubagent': (params) => sessions.spawnSubagent(params),
361
280
  'sessions.run': (params) => sessions.run(params),
362
281
  'sessions.history': (params) => ({ sessionFile: selectHistory({ sessionDir: text(params, 'sessionDir'), resume: params.resume ?? null }) }),
@@ -367,4 +286,8 @@ export function methods(harness) {
367
286
  'terminal.state': (params) => terminals.state(text(params, 'sessionId')),
368
287
  'terminal.dispose': (params) => terminals.dispose(text(params, 'sessionId'))
369
288
  };
289
+ const overlap = Object.keys(foundation).find(name => name in builtins);
290
+ if (overlap)
291
+ throw new Error(`Server method ${overlap} is owned by both Foundation and a built-in plugin.`);
292
+ return { ...builtins, ...foundation };
370
293
  }
@@ -0,0 +1,4 @@
1
+ export type Params = Record<string, any>;
2
+ export declare const text: (params: Params, name: string) => string;
3
+ export declare const optional: (params: Params, name: string) => string | null;
4
+ export declare function pick<T extends string>(params: Params, keys: readonly T[]): Record<T, unknown>;
@@ -0,0 +1,11 @@
1
+ import { invalid } from "../core/errors.js";
2
+ export const text = (params, name) => {
3
+ const value = params[name];
4
+ if (typeof value !== 'string' || !value)
5
+ throw invalid(`${name} is required.`);
6
+ return value;
7
+ };
8
+ export const optional = (params, name) => (typeof params[name] === 'string' && params[name] ? params[name] : null);
9
+ export function pick(params, keys) {
10
+ return Object.fromEntries(keys.filter(key => key in params).map(key => [key, params[key]]));
11
+ }
@@ -50,10 +50,19 @@ export declare const toolAttachmentRefsSchema: z.ZodArray<z.ZodObject<{
50
50
  mimeType: z.ZodString;
51
51
  }, z.core.$strict>>;
52
52
  export type ToolAttachmentRef = z.infer<typeof toolAttachmentRefSchema>;
53
+ export declare const QUESTION_SKETCH_MAX_LINES = 24;
54
+ export declare const QUESTION_SKETCH_MAX_COLUMNS = 80;
55
+ export declare const QUESTION_SKETCH_MAX_LENGTH: number;
56
+ export declare const QUESTION_SKETCH_PATTERN = "^(?:[\\x20-\\x7e]{0,80})(?:\\n[\\x20-\\x7e]{0,80}){0,23}$";
57
+ export declare const questionSketchSchema: z.ZodString;
58
+ export declare function questionSketchError(uiSketch: unknown, options: readonly {
59
+ sketch?: unknown;
60
+ }[]): string | undefined;
53
61
  export declare const questionOptionSchema: z.ZodObject<{
54
62
  label: z.ZodString;
55
63
  description: z.ZodOptional<z.ZodString>;
56
64
  recommended: z.ZodOptional<z.ZodBoolean>;
65
+ sketch: z.ZodOptional<z.ZodString>;
57
66
  }, z.core.$strip>;
58
67
  export type QuestionOption = z.infer<typeof questionOptionSchema>;
59
68
  /** One line of the run's checklist. The list has no ids: every update replaces all of it. */
@@ -277,10 +286,12 @@ export declare const runEventPayloadSchemas: {
277
286
  questionId: z.ZodString;
278
287
  question: z.ZodString;
279
288
  summary: z.ZodOptional<z.ZodString>;
289
+ uiSketch: z.ZodOptional<z.ZodLiteral<true>>;
280
290
  options: z.ZodArray<z.ZodObject<{
281
291
  label: z.ZodString;
282
292
  description: z.ZodOptional<z.ZodString>;
283
293
  recommended: z.ZodOptional<z.ZodBoolean>;
294
+ sketch: z.ZodOptional<z.ZodString>;
284
295
  }, z.core.$strip>>;
285
296
  multiSelect: z.ZodBoolean;
286
297
  allowText: z.ZodBoolean;
@@ -500,6 +511,10 @@ export interface RunRecord {
500
511
  /** The variant the run used: its scope and version fingerprint. */
501
512
  variantScope?: string | null;
502
513
  variantVersion?: string | null;
514
+ /** The agent profile a subagent started from (PROP-0029); null for a copy of its parent. */
515
+ profile?: string | null;
516
+ /** The short name a parent gave a subagent, shown in the roster (PROP-0030). */
517
+ title?: string | null;
503
518
  }
504
519
  export declare const runRecordSchema: z.ZodObject<{
505
520
  resumedFromRunId: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>;
@@ -527,6 +542,8 @@ export declare const runRecordSchema: z.ZodObject<{
527
542
  trigger: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>;
528
543
  variantScope: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>>;
529
544
  variantVersion: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>>;
545
+ profile: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>>;
546
+ title: z.ZodOptional<z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodTransform<string | null, string | null | undefined>>>;
530
547
  }, z.core.$strip>;
531
548
  /** Records written before PROP-0009 name the variant `harnessScope` and `harnessVersion`. */
532
549
  export declare function upgradeRunRecord(raw: unknown): unknown;
@@ -91,7 +91,39 @@ const approvalRequestedPayload = z.object({
91
91
  automatic: approvalAutomaticSchema.optional()
92
92
  });
93
93
  const approvalResolvedPayload = z.object({ approvalId: z.string(), decision: z.enum(['approved', 'denied', 'native']), automatic: approvalAutomaticSchema.optional() });
94
- export const questionOptionSchema = z.object({ label: z.string().min(1), description: z.string().optional(), recommended: z.boolean().optional() });
94
+ export const QUESTION_SKETCH_MAX_LINES = 24;
95
+ export const QUESTION_SKETCH_MAX_COLUMNS = 80;
96
+ export const QUESTION_SKETCH_MAX_LENGTH = QUESTION_SKETCH_MAX_LINES * QUESTION_SKETCH_MAX_COLUMNS + QUESTION_SKETCH_MAX_LINES - 1;
97
+ export const QUESTION_SKETCH_PATTERN = `^(?:[\\x20-\\x7e]{0,${QUESTION_SKETCH_MAX_COLUMNS}})(?:\\n[\\x20-\\x7e]{0,${QUESTION_SKETCH_MAX_COLUMNS}}){0,${QUESTION_SKETCH_MAX_LINES - 1}}$`;
98
+ export const questionSketchSchema = z.string()
99
+ .min(1)
100
+ .max(QUESTION_SKETCH_MAX_LENGTH)
101
+ .regex(new RegExp(QUESTION_SKETCH_PATTERN), 'Sketches use up to 24 LF-separated lines of at most 80 printable ASCII characters.')
102
+ .refine((sketch) => {
103
+ const lines = sketch.split('\n');
104
+ return lines.length <= QUESTION_SKETCH_MAX_LINES &&
105
+ lines.every((line) => line.length <= QUESTION_SKETCH_MAX_COLUMNS && /^[\x20-\x7e]*$/u.test(line)) &&
106
+ /[^ \n]/u.test(sketch);
107
+ }, 'A sketch needs visible content and must use up to 24 LF-separated lines of at most 80 printable ASCII characters.');
108
+ export function questionSketchError(uiSketch, options) {
109
+ if (uiSketch !== undefined && uiSketch !== true)
110
+ return 'Set uiSketch to true to enable question sketches.';
111
+ const sketches = options.map((option) => option.sketch);
112
+ if (uiSketch === true && sketches.some((sketch) => sketch === undefined))
113
+ return 'Sketch mode requires a sketch for every choice.';
114
+ if (uiSketch !== true && sketches.some((sketch) => sketch !== undefined))
115
+ return 'Choice sketches require uiSketch: true.';
116
+ if (sketches.some((sketch) => sketch !== undefined && !questionSketchSchema.safeParse(sketch).success)) {
117
+ return 'Each choice sketch must use 1–24 lines of at most 80 printable ASCII characters and contain a visible character.';
118
+ }
119
+ return undefined;
120
+ }
121
+ export const questionOptionSchema = z.object({
122
+ label: z.string().min(1),
123
+ description: z.string().optional(),
124
+ recommended: z.boolean().optional(),
125
+ sketch: questionSketchSchema.optional()
126
+ });
95
127
  const questionBatchFields = {
96
128
  batchId: z.string().min(1).optional(),
97
129
  batchIndex: z.number().int().min(1).max(5).optional(),
@@ -110,12 +142,17 @@ const questionRequestedPayload = z.object({
110
142
  questionId: z.string(),
111
143
  question: z.string().min(1),
112
144
  summary: z.string().min(1).max(100).optional(),
145
+ uiSketch: z.literal(true).optional(),
113
146
  options: z.array(questionOptionSchema),
114
147
  multiSelect: z.boolean(),
115
148
  allowText: z.boolean(),
116
149
  userOnly: z.boolean().optional(),
117
150
  ...questionBatchFields
118
- }).superRefine(questionBatchRefinement);
151
+ }).superRefine(questionBatchRefinement).superRefine((value, context) => {
152
+ const error = questionSketchError(value.uiSketch, value.options);
153
+ if (error)
154
+ context.addIssue({ code: z.ZodIssueCode.custom, path: ['options'], message: error });
155
+ });
119
156
  const questionAnsweredPayload = z.object({
120
157
  questionId: z.string(),
121
158
  selected: z.array(z.string()),
@@ -281,7 +318,9 @@ export const runRecordSchema = z.object({
281
318
  agentId: text,
282
319
  trigger: text,
283
320
  variantScope: text.optional(),
284
- variantVersion: text.optional()
321
+ variantVersion: text.optional(),
322
+ profile: text.optional(),
323
+ title: text.optional()
285
324
  });
286
325
  /** Records written before PROP-0009 name the variant `harnessScope` and `harnessVersion`. */
287
326
  export function upgradeRunRecord(raw) {