@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,2 @@
1
+ import type { ProfileDefinition } from "./profiles.js";
2
+ export declare const BUILTIN_PROFILES: readonly ProfileDefinition[];
@@ -0,0 +1,34 @@
1
+ const RECALL = ['history_search', 'history_read', 'trajectory_search', 'trajectory_read', 'wiki_search', 'wiki_read', 'wiki_list', 'skill_load'];
2
+ const PROGRESS = ['checklist_write', 'session_name'];
3
+ const WEB = ['web_search', 'web_fetch'];
4
+ export const BUILTIN_PROFILES = [
5
+ {
6
+ name: 'worker',
7
+ description: 'Carries out a well-defined change: reads, edits and writes files and runs commands.',
8
+ color: 'green',
9
+ instructions: [
10
+ 'You carry out one piece of work from your brief, end to end.',
11
+ '- Read the code you change first and follow the conventions you find.',
12
+ '- Keep a checklist with checklist_write when the brief has more than one step, and name your session with session_name.',
13
+ '- Make the change, then check it: run the relevant tests, type checks or commands.',
14
+ '- Stay inside the brief. If something outside it blocks you, stop and say so instead of widening the change.',
15
+ '- Finish with a short report: what you changed (files), how you checked it, and anything left open.'
16
+ ].join('\n'),
17
+ tools: ['read', 'grep', 'find', 'ls', 'edit', 'write', 'bash', ...PROGRESS, ...RECALL, ...WEB],
18
+ plugins: null, variant: null, model: null, effort: null
19
+ },
20
+ {
21
+ name: 'explorer',
22
+ description: 'Finds and explains code, files and history without changing anything.',
23
+ color: 'cyan',
24
+ instructions: [
25
+ 'You answer a question about the code, files or history in your brief. You never change anything.',
26
+ '- Search broadly first, then read the parts that matter.',
27
+ '- Keep a checklist with checklist_write when the search has several parts, and name your session with session_name.',
28
+ '- Cite what you found as path:line.',
29
+ '- Return conclusions, not file dumps: what you found, where, and how sure you are.'
30
+ ].join('\n'),
31
+ tools: ['read', 'grep', 'find', 'ls', ...PROGRESS, ...RECALL, ...WEB],
32
+ plugins: null, variant: null, model: null, effort: null
33
+ }
34
+ ];
@@ -0,0 +1,13 @@
1
+ import type { CapabilitySet } from "../capabilities/types.js";
2
+ import type { PluginDescriptor } from "../plugins/types.js";
3
+ import type { AgentProfile } from "./profiles.js";
4
+ export interface NarrowedCapabilities {
5
+ capabilities: CapabilitySet;
6
+ /** Tools the profile names that the parent does not have here. */
7
+ unavailable: string[];
8
+ }
9
+ /**
10
+ * The parent's set narrowed by the profile's tools and plugins and by the tools the spawn call
11
+ * asked for. A required plugin always stays; a tool whose plugin was dropped goes with it.
12
+ */
13
+ export declare function narrowForProfile(parent: CapabilitySet, profile: Pick<AgentProfile, 'tools' | 'plugins'> | null, requested: readonly string[] | undefined, descriptors: readonly PluginDescriptor[]): NarrowedCapabilities;
@@ -0,0 +1,29 @@
1
+ function pluginOf(id) {
2
+ return id.split('/')[0] ?? '';
3
+ }
4
+ /**
5
+ * The parent's set narrowed by the profile's tools and plugins and by the tools the spawn call
6
+ * asked for. A required plugin always stays; a tool whose plugin was dropped goes with it.
7
+ */
8
+ export function narrowForProfile(parent, profile, requested, descriptors) {
9
+ let tools = parent.tools.filter((name) => !requested || requested.includes(name));
10
+ let contributions = parent.contributions;
11
+ const unavailable = profile?.tools?.filter((name) => !parent.tools.includes(name)) ?? [];
12
+ if (profile?.tools)
13
+ tools = tools.filter((name) => profile.tools.includes(name));
14
+ if (profile?.plugins) {
15
+ const allowed = new Set([...profile.plugins, ...descriptors.filter((descriptor) => descriptor.required).map((descriptor) => descriptor.id)]);
16
+ contributions = contributions.filter((id) => allowed.has(pluginOf(id)));
17
+ const owner = new Map();
18
+ for (const descriptor of descriptors)
19
+ for (const tool of descriptor.tools ?? [])
20
+ owner.set(tool.name, descriptor.id);
21
+ for (const id of parent.contributions) {
22
+ const [plugin, kind, name] = id.split('/');
23
+ if (kind === 'tool' && plugin && name)
24
+ owner.set(name, plugin);
25
+ }
26
+ tools = tools.filter((name) => !owner.has(name) || allowed.has(owner.get(name)));
27
+ }
28
+ return { capabilities: { ...parent, tools, contributions }, unavailable };
29
+ }
@@ -0,0 +1,80 @@
1
+ import { type Effort } from "../core/effort.js";
2
+ export declare const PROFILE_NAME: RegExp;
3
+ export declare const PROFILE_COLORS: readonly ["blue", "green", "yellow", "red", "magenta", "cyan", "orange", "purple"];
4
+ export type ProfileColor = (typeof PROFILE_COLORS)[number];
5
+ export type ProfileSource = 'builtin' | 'global' | 'project' | 'variant';
6
+ /** Where a person's own profile file lives. */
7
+ export type ProfileScope = 'global' | 'project';
8
+ /** Which variant a profile's subagent runs: a scope's head or version, none, or the parent's (null). */
9
+ export type ProfileVariant = {
10
+ scope: string;
11
+ version: string | null;
12
+ } | 'none' | null;
13
+ export interface AgentProfile {
14
+ name: string;
15
+ description: string;
16
+ color: ProfileColor;
17
+ /** The role instructions: the file's body. */
18
+ instructions: string;
19
+ /** Tool allowlist; null keeps every tool the parent has. */
20
+ tools: string[] | null;
21
+ /** Plugin ids whose contributions the subagent keeps; null keeps all. Required plugins always stay. */
22
+ plugins: string[] | null;
23
+ variant: ProfileVariant;
24
+ model: string | null;
25
+ effort: Effort | null;
26
+ source: ProfileSource;
27
+ /** The file it was read from; null for built-ins. */
28
+ path: string | null;
29
+ }
30
+ export interface ProfileProblem {
31
+ name: string;
32
+ source: ProfileSource;
33
+ path: string;
34
+ error: string;
35
+ }
36
+ export interface ProfileListing {
37
+ profiles: AgentProfile[];
38
+ problems: ProfileProblem[];
39
+ }
40
+ /** The built-in profile definitions, before a source is attached. */
41
+ export type ProfileDefinition = Omit<AgentProfile, 'source' | 'path'>;
42
+ /** Nearest folder above `cwd` that holds `.git`, or `cwd` itself. */
43
+ export declare function projectRoot(cwd: string): string;
44
+ export declare function profileDirs(root: string, cwd?: string | null): {
45
+ global: string;
46
+ project: string | null;
47
+ };
48
+ export declare function parseVariant(value: unknown): ProfileVariant;
49
+ export declare function formatVariant(variant: ProfileVariant): string | null;
50
+ /** Reads one profile file. Throws an `invalid_request` error that names what is wrong. */
51
+ export declare function parseProfile(text: string, name: string, source: ProfileSource, path: string | null): AgentProfile;
52
+ /** The file text for a profile, as `createProfile` writes it. */
53
+ export declare function profileText(profile: ProfileDefinition): string;
54
+ /**
55
+ * Every profile a session in `cwd` can use. An unreadable file is reported in `problems`; it hides
56
+ * nothing, so a broken project file leaves the global or built-in profile of that name in force.
57
+ */
58
+ export declare function listProfiles(root: string, cwd?: string | null, variantDir?: string | null): ProfileListing;
59
+ export declare function resolveProfile(root: string, cwd: string | null | undefined, name: string, variantDir?: string | null): AgentProfile | null;
60
+ /** Writes a new profile file from the template, or a copy of `from`. Refuses to overwrite one. */
61
+ export declare function createProfile(root: string, cwd: string | null | undefined, input: {
62
+ name: string;
63
+ scope: ProfileScope;
64
+ from?: string | null;
65
+ }): AgentProfile;
66
+ /** Deletes a person's own profile file. Built-ins and variant subagents cannot be removed here. */
67
+ export declare function removeProfile(root: string, cwd: string | null | undefined, input: {
68
+ name: string;
69
+ scope: ProfileScope;
70
+ }): void;
71
+ /** Where the profile list sits among the prompt sections: the slot a variant's subagents used. */
72
+ export declare const PROFILE_SECTION_ORDER = 64;
73
+ /** The prompt section that lists the profiles a run can start subagents from. */
74
+ export declare function profileSection(root: string, cwd: string | null | undefined, variantDir?: string | null): {
75
+ id: string;
76
+ title: string;
77
+ body: string;
78
+ budget: number;
79
+ order: number;
80
+ };
@@ -0,0 +1,221 @@
1
+ // Agent profiles (PROP-0029): named bundles a subagent starts from, with its instructions, colour,
2
+ // tools, plugins, variant, model and effort. They come from four places, later ones winning:
3
+ // built-ins in code, `<data root>/agents/*.md`, `<project>/.xingular/agents/*.md`; a variant's own
4
+ // c5 subagents fill in names nobody else took. Profiles only narrow what the parent has.
5
+ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync } from 'node:fs';
6
+ import { dirname, join } from 'node:path';
7
+ import { EFFORTS } from "../core/effort.js";
8
+ import { invalid, notFound } from "../core/errors.js";
9
+ import { parse, serialize } from "../core/frontmatter.js";
10
+ import { atomicWrite } from "../core/fs.js";
11
+ import { subagentOf } from "../variant/materialise.js";
12
+ import { BUILTIN_PROFILES } from "./builtin.js";
13
+ export const PROFILE_NAME = /^[a-z0-9][a-z0-9_-]*$/;
14
+ export const PROFILE_COLORS = ['blue', 'green', 'yellow', 'red', 'magenta', 'cyan', 'orange', 'purple'];
15
+ /** Nearest folder above `cwd` that holds `.git`, or `cwd` itself. */
16
+ export function projectRoot(cwd) {
17
+ let dir = cwd;
18
+ for (;;) {
19
+ if (existsSync(join(dir, '.git')))
20
+ return dir;
21
+ const parent = dirname(dir);
22
+ if (parent === dir)
23
+ return cwd;
24
+ dir = parent;
25
+ }
26
+ }
27
+ export function profileDirs(root, cwd) {
28
+ return { global: join(root, 'agents'), project: cwd ? join(projectRoot(cwd), '.xingular', 'agents') : null };
29
+ }
30
+ function stringList(value, field) {
31
+ if (value === undefined || value === null)
32
+ return null;
33
+ const list = typeof value === 'string' ? value.split(/[\s,]+/u) : value;
34
+ if (!Array.isArray(list) || list.some(item => typeof item !== 'string'))
35
+ throw invalid(`${field} must be a list of names.`);
36
+ return [...new Set(list.map(item => item.trim()).filter(Boolean))];
37
+ }
38
+ function optionalText(value, field) {
39
+ if (value === undefined || value === null || value === '')
40
+ return null;
41
+ if (typeof value !== 'string')
42
+ throw invalid(`${field} must be text.`);
43
+ return value.trim() || null;
44
+ }
45
+ export function parseVariant(value) {
46
+ const text = optionalText(value, 'variant');
47
+ if (!text)
48
+ return null;
49
+ if (text === 'none')
50
+ return 'none';
51
+ const [scope, version, ...rest] = text.split('@');
52
+ if (!scope || rest.length || (version !== undefined && !version))
53
+ throw invalid('variant must be <scope>, <scope>@<version> or none.');
54
+ return { scope, version: version ?? null };
55
+ }
56
+ export function formatVariant(variant) {
57
+ if (!variant)
58
+ return null;
59
+ if (variant === 'none')
60
+ return 'none';
61
+ return variant.version ? `${variant.scope}@${variant.version}` : variant.scope;
62
+ }
63
+ /** Reads one profile file. Throws an `invalid_request` error that names what is wrong. */
64
+ export function parseProfile(text, name, source, path) {
65
+ if (!PROFILE_NAME.test(name))
66
+ throw invalid('A profile name uses lowercase letters, digits, - and _, and starts with a letter or digit.');
67
+ const parsed = parse(text);
68
+ if (!parsed)
69
+ throw invalid('The profile needs a frontmatter block between --- lines.');
70
+ const front = parsed.front;
71
+ if (front.name !== undefined && front.name !== name)
72
+ throw invalid(`The file is named ${name} but its frontmatter says ${String(front.name)}.`);
73
+ const description = optionalText(front.description, 'description');
74
+ if (!description)
75
+ throw invalid('The profile needs a description.');
76
+ const color = optionalText(front.color, 'color') ?? 'blue';
77
+ if (!PROFILE_COLORS.includes(color))
78
+ throw invalid(`color must be one of ${PROFILE_COLORS.join(', ')}.`);
79
+ const effort = optionalText(front.effort, 'effort');
80
+ if (effort && !EFFORTS.includes(effort))
81
+ throw invalid(`effort must be one of ${EFFORTS.join(', ')}.`);
82
+ return {
83
+ name, description, color: color, instructions: parsed.body.trim(),
84
+ tools: stringList(front.tools, 'tools'), plugins: stringList(front.plugins, 'plugins'),
85
+ variant: parseVariant(front.variant), model: optionalText(front.model, 'model'), effort: effort,
86
+ source, path
87
+ };
88
+ }
89
+ /** The file text for a profile, as `createProfile` writes it. */
90
+ export function profileText(profile) {
91
+ const front = { name: profile.name, description: profile.description, color: profile.color };
92
+ if (profile.tools)
93
+ front.tools = profile.tools;
94
+ if (profile.plugins)
95
+ front.plugins = profile.plugins;
96
+ const variant = formatVariant(profile.variant);
97
+ if (variant)
98
+ front.variant = variant;
99
+ if (profile.model)
100
+ front.model = profile.model;
101
+ if (profile.effort)
102
+ front.effort = profile.effort;
103
+ return serialize(front, profile.instructions);
104
+ }
105
+ function readFolder(dir, source, into, problems) {
106
+ if (!dir || !existsSync(dir))
107
+ return;
108
+ for (const file of readdirSync(dir).filter(entry => entry.endsWith('.md')).sort()) {
109
+ const path = join(dir, file);
110
+ const name = file.slice(0, -3);
111
+ try {
112
+ if (!statSync(path).isFile())
113
+ continue;
114
+ into.set(name, parseProfile(readFileSync(path, 'utf8'), name, source, path));
115
+ }
116
+ catch (error) {
117
+ problems.push({ name, source, path, error: error instanceof Error ? error.message : String(error) });
118
+ }
119
+ }
120
+ }
121
+ function variantDefinitions(variantDir) {
122
+ const folder = variantDir ? join(variantDir, 'sub_agents') : null;
123
+ if (!folder || !existsSync(folder))
124
+ return [];
125
+ return readdirSync(folder).sort().flatMap(name => {
126
+ const definition = subagentOf(variantDir, name);
127
+ if (!definition)
128
+ return [];
129
+ return [{
130
+ name, description: definition.description || name, color: 'blue', instructions: definition.instructions || definition.description,
131
+ tools: null, plugins: null, variant: null, model: null, effort: null,
132
+ source: 'variant', path: join(folder, name, 'AGENT.md')
133
+ }];
134
+ });
135
+ }
136
+ /**
137
+ * Every profile a session in `cwd` can use. An unreadable file is reported in `problems`; it hides
138
+ * nothing, so a broken project file leaves the global or built-in profile of that name in force.
139
+ */
140
+ export function listProfiles(root, cwd, variantDir) {
141
+ const profiles = new Map();
142
+ const problems = [];
143
+ for (const definition of BUILTIN_PROFILES)
144
+ profiles.set(definition.name, { ...definition, source: 'builtin', path: null });
145
+ const dirs = profileDirs(root, cwd);
146
+ readFolder(dirs.global, 'global', profiles, problems);
147
+ readFolder(dirs.project, 'project', profiles, problems);
148
+ for (const definition of variantDefinitions(variantDir))
149
+ if (!profiles.has(definition.name))
150
+ profiles.set(definition.name, definition);
151
+ return { profiles: [...profiles.values()].sort((left, right) => left.name.localeCompare(right.name)), problems };
152
+ }
153
+ export function resolveProfile(root, cwd, name, variantDir) {
154
+ if (!PROFILE_NAME.test(name))
155
+ return null;
156
+ return listProfiles(root, cwd, variantDir).profiles.find(profile => profile.name === name) ?? null;
157
+ }
158
+ function scopeDir(root, cwd, scope) {
159
+ const dirs = profileDirs(root, cwd);
160
+ if (scope === 'global')
161
+ return dirs.global;
162
+ if (!dirs.project)
163
+ throw invalid('A project profile needs a working folder.');
164
+ return dirs.project;
165
+ }
166
+ const TEMPLATE = {
167
+ description: 'What this agent is for, in one line.',
168
+ color: 'blue',
169
+ instructions: 'Describe how this agent should work: what to do with a brief, what to avoid, and what to report back.',
170
+ tools: null, plugins: null, variant: null, model: null, effort: null
171
+ };
172
+ /** Writes a new profile file from the template, or a copy of `from`. Refuses to overwrite one. */
173
+ export function createProfile(root, cwd, input) {
174
+ const name = typeof input.name === 'string' ? input.name.trim() : '';
175
+ if (!PROFILE_NAME.test(name))
176
+ throw invalid('A profile name uses lowercase letters, digits, - and _, and starts with a letter or digit.');
177
+ if (input.scope !== 'global' && input.scope !== 'project')
178
+ throw invalid('scope must be global or project.');
179
+ const dir = scopeDir(root, cwd, input.scope);
180
+ const path = join(dir, `${name}.md`);
181
+ if (existsSync(path))
182
+ throw invalid(`There is already a ${input.scope} profile named ${name}.`);
183
+ let source = { ...TEMPLATE, name };
184
+ if (input.from) {
185
+ const original = resolveProfile(root, cwd, input.from);
186
+ if (!original)
187
+ throw notFound(`The profile ${input.from}`);
188
+ source = { ...original, name };
189
+ }
190
+ mkdirSync(dir, { recursive: true });
191
+ const text = profileText(source);
192
+ atomicWrite(path, text);
193
+ return parseProfile(text, name, input.scope, path);
194
+ }
195
+ /** Deletes a person's own profile file. Built-ins and variant subagents cannot be removed here. */
196
+ export function removeProfile(root, cwd, input) {
197
+ if (!PROFILE_NAME.test(input.name))
198
+ throw invalid('That is not a profile name.');
199
+ if (input.scope !== 'global' && input.scope !== 'project')
200
+ throw invalid('scope must be global or project.');
201
+ const path = join(scopeDir(root, cwd, input.scope), `${input.name}.md`);
202
+ if (!existsSync(path))
203
+ throw notFound(`The ${input.scope} profile ${input.name}`);
204
+ rmSync(path);
205
+ }
206
+ /** Where the profile list sits among the prompt sections: the slot a variant's subagents used. */
207
+ export const PROFILE_SECTION_ORDER = 64;
208
+ /** The prompt section that lists the profiles a run can start subagents from. */
209
+ export function profileSection(root, cwd, variantDir) {
210
+ const { profiles } = listProfiles(root, cwd, variantDir);
211
+ return {
212
+ id: 'subagents',
213
+ title: 'Subagents',
214
+ body: [
215
+ 'Start one with spawn_subagent and agent set to a profile name, or omit agent for a copy of yourself. Each works in its own context with a complete brief from you, keeps its own checklist the user can follow, and returns text. A profile can only narrow your tools.',
216
+ ...profiles.map((profile) => `- ${profile.name} — ${profile.description}`)
217
+ ].join('\n'),
218
+ budget: 6_000,
219
+ order: PROFILE_SECTION_ORDER
220
+ };
221
+ }
@@ -0,0 +1,42 @@
1
+ import { type PageOperation } from "./pageScript.js";
2
+ export interface BridgeWindow {
3
+ id: string;
4
+ url: string;
5
+ title: string;
6
+ lastSeen: number;
7
+ }
8
+ export interface BridgeInfo {
9
+ port: number;
10
+ token: string;
11
+ scriptUrl: string;
12
+ /** What to put in the app while developing it. */
13
+ snippet: string;
14
+ }
15
+ /** The script a window loads: the page helper plus a loop that pulls operations from the bridge. */
16
+ export declare function bridgeScript(base: string, token: string): string;
17
+ export declare class NotesBridge {
18
+ private readonly options;
19
+ private server;
20
+ private starting;
21
+ private info;
22
+ private readonly windows;
23
+ private nextWindow;
24
+ private nextCall;
25
+ constructor(options: {
26
+ stateDir: string;
27
+ port?: number;
28
+ });
29
+ private settingsFile;
30
+ /** Starts the bridge (once) on the port and token kept from last time, so the app's snippet stays valid. */
31
+ start(): Promise<BridgeInfo>;
32
+ private listen;
33
+ list(): BridgeWindow[];
34
+ /** Sends one helper operation to a window and waits for its JSON reply. */
35
+ call(windowId: string, operation: PageOperation): Promise<unknown>;
36
+ private flush;
37
+ private sweep;
38
+ private json;
39
+ private body;
40
+ private handle;
41
+ close(): Promise<void>;
42
+ }