@robota-sdk/agent-command 3.0.0-beta.79 → 3.0.0-beta.81

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 (200) hide show
  1. package/README.md +11 -2
  2. package/dist/node/index.cjs +76 -38
  3. package/dist/node/index.d.cts +946 -0
  4. package/dist/node/index.d.cts.map +1 -0
  5. package/dist/node/index.d.ts +564 -51
  6. package/dist/node/index.d.ts.map +1 -1
  7. package/dist/node/index.js +76 -38
  8. package/dist/node/index.js.map +1 -1
  9. package/package.json +34 -16
  10. package/src/advisor/advisor-command-module.ts +47 -0
  11. package/src/advisor/advisor-command.test.ts +102 -0
  12. package/src/advisor/advisor-command.ts +55 -0
  13. package/src/advisor/advisor-cost-functional.test.ts +92 -0
  14. package/src/advisor/index.ts +6 -0
  15. package/src/agent/__tests__/agent-command.test.ts +39 -8
  16. package/src/agent/agent-command-module.ts +14 -7
  17. package/src/agent/agent-command-parser.ts +1 -1
  18. package/src/agent/agent-command.ts +3 -3
  19. package/src/background/__tests__/background-command-module.test.ts +59 -52
  20. package/src/background/background-command-module.ts +2 -1
  21. package/src/background/background-command.ts +3 -3
  22. package/src/command-module-utils.ts +33 -0
  23. package/src/compact/__tests__/compact-command-module.test.ts +31 -23
  24. package/src/compact/compact-command-module.ts +10 -1
  25. package/src/compact/compact-command.ts +6 -3
  26. package/src/context/__tests__/context-command-module.test.ts +214 -63
  27. package/src/context/auto-compact-format.ts +25 -0
  28. package/src/context/context-breakdown.ts +295 -0
  29. package/src/context/context-command-module.ts +45 -14
  30. package/src/context/context-command.ts +34 -257
  31. package/src/default/__tests__/__snapshots__/model-exposure.test.ts.snap +110 -0
  32. package/src/default/__tests__/default-command-modules.test.ts +59 -7
  33. package/src/default/__tests__/model-exposure.test.ts +241 -0
  34. package/src/default/__tests__/org-policy-forwarding.test.ts +98 -0
  35. package/src/default/default-command-modules.ts +130 -18
  36. package/src/default/index.ts +4 -1
  37. package/src/devices/__tests__/devices-command-module.test.ts +212 -0
  38. package/src/devices/devices-command-module.ts +224 -0
  39. package/src/devices/devices-command-port.ts +82 -0
  40. package/src/devices/index.ts +11 -0
  41. package/src/doctor/__tests__/doctor-command-module.test.ts +145 -0
  42. package/src/doctor/__tests__/doctor-display-vocabulary.test.ts +38 -0
  43. package/src/doctor/__tests__/doctor-fixture.ts +145 -0
  44. package/src/doctor/__tests__/doctor-redaction.test.ts +29 -0
  45. package/src/doctor/__tests__/doctor-runner.test.ts +464 -0
  46. package/src/doctor/doctor-command-module.ts +132 -0
  47. package/src/doctor/doctor-extensions-probe.ts +299 -0
  48. package/src/doctor/doctor-node-deps.ts +89 -0
  49. package/src/doctor/doctor-redaction.ts +99 -0
  50. package/src/doctor/doctor-render.ts +70 -0
  51. package/src/doctor/doctor-repair.ts +136 -0
  52. package/src/doctor/doctor-runner.ts +122 -0
  53. package/src/doctor/doctor-settings-probe.ts +239 -0
  54. package/src/doctor/doctor-storage-probe.ts +187 -0
  55. package/src/doctor/doctor-types.ts +109 -0
  56. package/src/doctor/index.ts +32 -0
  57. package/src/editor/__tests__/editor-command-functional.test.ts +49 -16
  58. package/src/editor/editor-command-module.ts +6 -5
  59. package/src/editor/editor-command.ts +10 -5
  60. package/src/effort/effort-command-module.ts +51 -0
  61. package/src/effort/effort-command.test.ts +140 -0
  62. package/src/effort/effort-command.ts +122 -0
  63. package/src/effort/index.ts +6 -0
  64. package/src/exit/__tests__/exit-command-module.test.ts +16 -9
  65. package/src/exit/exit-command-module.ts +2 -1
  66. package/src/exit/exit-command.ts +6 -6
  67. package/src/fork/__tests__/fork-command.test.ts +184 -0
  68. package/src/fork/fork-command-module.ts +61 -0
  69. package/src/fork/fork-command.ts +162 -0
  70. package/src/fork/index.ts +8 -0
  71. package/src/git/__tests__/fake-git-port.ts +28 -0
  72. package/src/git/__tests__/git-command-module.test.ts +103 -0
  73. package/src/git/__tests__/git-commit.test.ts +185 -0
  74. package/src/git/__tests__/git-diff.test.ts +125 -0
  75. package/src/git/__tests__/git-process.test.ts +221 -0
  76. package/src/git/__tests__/git-status.test.ts +117 -0
  77. package/src/git/git-command-module.ts +139 -0
  78. package/src/git/git-commit.ts +251 -0
  79. package/src/git/git-diff.ts +129 -0
  80. package/src/git/git-process.ts +169 -0
  81. package/src/git/git-status.ts +134 -0
  82. package/src/git/index.ts +17 -0
  83. package/src/goal/__tests__/goal-command.test.ts +9 -4
  84. package/src/goal/goal-command-module.ts +2 -1
  85. package/src/goal/goal-command.ts +6 -9
  86. package/src/handoff/__tests__/handoff-command.test.ts +218 -0
  87. package/src/handoff/handoff-command-module.ts +48 -0
  88. package/src/handoff/handoff-command.ts +187 -0
  89. package/src/handoff/index.ts +6 -0
  90. package/src/help/__tests__/help-command-module.test.ts +63 -40
  91. package/src/help/__tests__/help-command.test.ts +46 -37
  92. package/src/help/help-command-module.ts +2 -1
  93. package/src/help/help-command.ts +3 -3
  94. package/src/index.ts +124 -2
  95. package/src/keybindings/__tests__/keybindings-command-module.test.ts +57 -0
  96. package/src/keybindings/index.ts +6 -0
  97. package/src/keybindings/keybindings-command-module.ts +74 -0
  98. package/src/language/__tests__/language-command-module.test.ts +33 -28
  99. package/src/language/language-command-module.ts +2 -1
  100. package/src/language/language-command.ts +8 -6
  101. package/src/mcp-activation/__tests__/mcp-activation-command.test.ts +630 -0
  102. package/src/mcp-activation/__tests__/mcp-model-view.test.ts +211 -0
  103. package/src/mcp-activation/index.ts +12 -0
  104. package/src/mcp-activation/mcp-activation-command-module.ts +92 -0
  105. package/src/mcp-activation/mcp-activation-command.ts +448 -0
  106. package/src/mcp-activation/mcp-model-notice.ts +85 -0
  107. package/src/mcp-activation/mcp-model-status.ts +99 -0
  108. package/src/memory/__tests__/memory-command-module.test.ts +167 -74
  109. package/src/memory/memory-command-module.ts +18 -1
  110. package/src/memory/memory-command.ts +38 -38
  111. package/src/mode/__tests__/mode-command-module.test.ts +51 -38
  112. package/src/mode/mode-command-module.ts +2 -1
  113. package/src/mode/mode-command.ts +15 -7
  114. package/src/output-style/__tests__/output-style-command.test.ts +96 -0
  115. package/src/output-style/index.ts +6 -0
  116. package/src/output-style/output-style-command-module.ts +143 -0
  117. package/src/peers/__tests__/peers-command.test.ts +241 -0
  118. package/src/peers/index.ts +9 -0
  119. package/src/peers/peers-command-module.ts +47 -0
  120. package/src/peers/peers-command.ts +142 -0
  121. package/src/permissions/__tests__/permissions-command-module.test.ts +149 -37
  122. package/src/permissions/permissions-command-module.ts +2 -1
  123. package/src/permissions/permissions-command.ts +46 -4
  124. package/src/plan/__tests__/plan-command.test.ts +129 -0
  125. package/src/plan/index.ts +9 -0
  126. package/src/plan/plan-command-module.ts +49 -0
  127. package/src/plan/plan-command.ts +92 -0
  128. package/src/plugin/__tests__/plugin-command-module.test.ts +46 -40
  129. package/src/plugin/__tests__/plugin-host-boundary.test.ts +11 -0
  130. package/src/plugin/plugin-command-module.ts +3 -1
  131. package/src/plugin/plugin-command.ts +17 -16
  132. package/src/preset/__tests__/host-supplied-preset-registry.test.ts +98 -0
  133. package/src/preset/__tests__/preset-command-module.test.ts +102 -56
  134. package/src/preset/preset-command-module.ts +26 -8
  135. package/src/preset/preset-command.ts +85 -27
  136. package/src/provider/__tests__/org-policy.test.ts +4 -5
  137. package/src/provider/__tests__/provider-command-module.test.ts +11 -14
  138. package/src/provider/__tests__/provider-startup-host-settings.test.ts +16 -0
  139. package/src/provider/__tests__/scripted-interaction.ts +7 -5
  140. package/src/provider/provider-command-execution.ts +4 -4
  141. package/src/provider/provider-command-module.ts +2 -1
  142. package/src/provider/provider-command-profile-lifecycle.ts +4 -4
  143. package/src/provider/provider-command-profile-operations.ts +3 -3
  144. package/src/provider/provider-command-profile.ts +1 -1
  145. package/src/provider/provider-command-setup.ts +4 -4
  146. package/src/provider/provider-startup.ts +62 -35
  147. package/src/remote-control/__tests__/remote-control-command-module.test.ts +36 -0
  148. package/src/remote-control/__tests__/remote-control-command.test.ts +216 -0
  149. package/src/remote-control/index.ts +8 -0
  150. package/src/remote-control/remote-control-command-module.ts +61 -0
  151. package/src/remote-control/remote-control-command.ts +138 -0
  152. package/src/reset/__tests__/reset-command-module.test.ts +5 -5
  153. package/src/reset/reset-command-module.ts +2 -1
  154. package/src/reset/reset-command.ts +3 -4
  155. package/src/rewind/__tests__/rewind-command-module.test.ts +40 -0
  156. package/src/rewind/rewind-command-module.ts +2 -1
  157. package/src/rewind/rewind-command.ts +73 -6
  158. package/src/sandbox/__tests__/sandbox-command.test.ts +80 -0
  159. package/src/sandbox/index.ts +2 -0
  160. package/src/sandbox/sandbox-command-module.ts +56 -0
  161. package/src/sandbox/sandbox-command.ts +94 -0
  162. package/src/schedule/__tests__/loop-command-functional.test.ts +386 -0
  163. package/src/schedule/__tests__/loop-command.test.ts +360 -0
  164. package/src/schedule/__tests__/loop-jitter.test.ts +25 -0
  165. package/src/schedule/__tests__/monitor-model-permission-functional.test.ts +126 -0
  166. package/src/schedule/__tests__/schedule-command.test.ts +143 -3
  167. package/src/schedule/__tests__/schedule-redos.test.ts +81 -0
  168. package/src/schedule/index.ts +3 -0
  169. package/src/schedule/loop-command.ts +333 -0
  170. package/src/schedule/loop-jitter.ts +41 -0
  171. package/src/schedule/schedule-command-module.ts +92 -14
  172. package/src/schedule/schedule-command.ts +98 -9
  173. package/src/schedule/schedule-spec-parser.ts +5 -1
  174. package/src/session/__tests__/rename-host-persistence.test.ts +72 -0
  175. package/src/session/__tests__/session-command-module.test.ts +210 -45
  176. package/src/session/session-command-module.ts +68 -37
  177. package/src/session/session-command.ts +147 -80
  178. package/src/settings/settings-command-module.ts +3 -2
  179. package/src/shell/__tests__/resolve-shell-host.test.ts +11 -0
  180. package/src/shell/__tests__/shell-command-functional.test.ts +23 -1
  181. package/src/shell/resolve-shell.ts +2 -2
  182. package/src/shell/shell-command-module.ts +6 -5
  183. package/src/shell/shell-command.ts +9 -5
  184. package/src/skills/__tests__/skills-command-module.test.ts +69 -28
  185. package/src/skills/index.ts +1 -0
  186. package/src/skills/skills-command-module.ts +22 -5
  187. package/src/skills/skills-command.ts +14 -13
  188. package/src/statusline/__tests__/statusline-command-module.test.ts +5 -4
  189. package/src/statusline/statusline-command-module.ts +2 -1
  190. package/src/statusline/statusline-command.ts +4 -4
  191. package/src/theme/__tests__/theme-command.test.ts +204 -0
  192. package/src/theme/index.ts +12 -0
  193. package/src/theme/theme-command-module.ts +211 -0
  194. package/src/user-local/__tests__/user-local-command.test.ts +145 -110
  195. package/src/user-local/user-local-command-constants.ts +1 -1
  196. package/src/user-local/user-local-command-module.ts +6 -5
  197. package/src/user-local/user-local-command.ts +12 -6
  198. package/src/user-local/user-local-memory-command.ts +17 -7
  199. package/src/plugins/default-plugin-command-adapter.ts +0 -164
  200. package/src/plugins/default-plugin-command-source-loader.ts +0 -31
@@ -0,0 +1,187 @@
1
+ import type {
2
+ ICommandHostAdapterAccess,
3
+ ICommandHostAdapters,
4
+ ICommandHostUserInteraction,
5
+ IHandoffProgress,
6
+ } from '@robota-sdk/agent-framework';
7
+ import type { ICommandResult } from '@robota-sdk/agent-interface-command';
8
+
9
+ /**
10
+ * `/handoff` (HANDOFF-001, issue #1864) — move this session to another machine.
11
+ *
12
+ * The command holds no protocol. It reads an injected adapter and touches no transport, for the same
13
+ * reason `/peers` does not: the carrier, the wire composition and the device identity are
14
+ * composition-root concerns and must have one owner.
15
+ *
16
+ * ## What it is FOR, which is the consent
17
+ *
18
+ * A hand-off loses things on purpose — uncommitted working-tree changes and running subprocesses
19
+ * stay on this machine, because moving them would make this a file-sync product and a process cannot
20
+ * migrate at all. Those are the operator's to lose, so they are named in the prompt BEFORE the
21
+ * question, not reported afterwards. A confirmation that did not mention them is not consent.
22
+ *
23
+ * ## The sentence it always ends on
24
+ *
25
+ * Every outcome that is not `done` says the session is still on this machine. That is not
26
+ * reassurance: it is the one invariant the whole design exists to preserve, and the operator is the
27
+ * person who has to know whether to keep typing here or walk to the other computer.
28
+ */
29
+
30
+ type THandoffHost = ICommandHostAdapterAccess & Partial<ICommandHostUserInteraction>;
31
+
32
+ /** The line that answers "where is my session?" — printed on every path, success included. */
33
+ function whereIsIt(progress: IHandoffProgress): string {
34
+ return progress.stillMine
35
+ ? 'This session is still on this machine, and still yours to use.'
36
+ : 'This session now belongs to the destination. This copy is read-only.';
37
+ }
38
+
39
+ function describeDestinations(
40
+ destinations: readonly { readonly deviceId: string; readonly name?: string }[],
41
+ ): string {
42
+ return destinations.map((d) => ` ${d.deviceId}${d.name ? ` ${d.name}` : ''}`).join('\n');
43
+ }
44
+
45
+ function usage(): ICommandResult {
46
+ return {
47
+ success: true,
48
+ message: [
49
+ 'Usage:',
50
+ ' /handoff list the machines this session could move to',
51
+ ' /handoff <device-id> move it there, after confirming what stays behind',
52
+ ].join('\n'),
53
+ };
54
+ }
55
+
56
+ export async function executeHandoffCommand(
57
+ context: THandoffHost,
58
+ args: string,
59
+ ): Promise<ICommandResult> {
60
+ const adapter: ICommandHostAdapters['handoff'] = context.getCommandHostAdapters?.().handoff;
61
+ if (adapter === undefined) {
62
+ // Said plainly rather than offered and then failed. A host with no carrier cannot move a
63
+ // session, and a command that took the request anyway would spend the operator's attention
64
+ // before telling them.
65
+ return {
66
+ success: false,
67
+ message:
68
+ 'This session has no hand-off carrier, so it cannot be moved to another machine. ' +
69
+ 'Nothing changed — the session is still here.',
70
+ };
71
+ }
72
+
73
+ const target = args.trim();
74
+ if (target === '' || target === 'help') {
75
+ const destinations = await adapter.destinations();
76
+ if (destinations.length === 0) {
77
+ return {
78
+ success: true,
79
+ message:
80
+ 'No other machine is reachable for a hand-off right now.\n' + whereIsIt(adapter.status()),
81
+ };
82
+ }
83
+ return {
84
+ success: true,
85
+ message: [
86
+ 'This session could move to:',
87
+ describeDestinations(destinations),
88
+ '',
89
+ usage().message ?? '',
90
+ ].join('\n'),
91
+ };
92
+ }
93
+
94
+ const destinations = await adapter.destinations();
95
+ if (!destinations.some((d) => d.deviceId === target)) {
96
+ return {
97
+ success: false,
98
+ message:
99
+ `No reachable machine is called '${target}'.\n` +
100
+ (destinations.length > 0
101
+ ? `Reachable now:\n${describeDestinations(destinations)}`
102
+ : 'None are reachable right now.') +
103
+ `\n${whereIsIt(adapter.status())}`,
104
+ };
105
+ }
106
+
107
+ const staying = await adapter.staysBehind();
108
+ const consequences = [
109
+ staying.uncommittedChanges
110
+ ? ' - uncommitted changes in the working tree stay on THIS machine'
111
+ : null,
112
+ staying.subprocesses > 0
113
+ ? ` - ${staying.subprocesses} running process(es) stay on THIS machine and will not be reachable there`
114
+ : null,
115
+ ' - the destination uses ITS OWN provider credential; credentials are never transferred',
116
+ ].filter((line): line is string => line !== null);
117
+
118
+ const ask = context.getUserInteraction?.();
119
+ if (ask === undefined) {
120
+ // No human is attached. Refused, never guessed: a hand-off is irreversible from the source's
121
+ // side, and "nobody was there to say no" is not a yes.
122
+ return {
123
+ success: false,
124
+ message:
125
+ 'A hand-off needs a person to confirm what stays behind, and no interactive session is ' +
126
+ `attached.\n${whereIsIt(adapter.status())}`,
127
+ };
128
+ }
129
+
130
+ const answer = await ask.ask({
131
+ id: `handoff:${target}`,
132
+ title: `Move this session to ${target}?`,
133
+ description: ['What will NOT travel:', ...consequences].join('\n'),
134
+ options: [
135
+ { value: 'move', label: `Move it to ${target}` },
136
+ { value: 'stay', label: 'Keep it here' },
137
+ ],
138
+ });
139
+ if (answer.type !== 'answer' || !answer.values.includes('move')) {
140
+ return {
141
+ success: true,
142
+ message: `Hand-off cancelled.\n${whereIsIt(adapter.status())}`,
143
+ };
144
+ }
145
+
146
+ const progressLines: string[] = [];
147
+ const final = await adapter.transfer(target, (progress) => {
148
+ progressLines.push(renderProgress(progress, target));
149
+ });
150
+
151
+ return {
152
+ success: final.state === 'done',
153
+ message: [
154
+ ...progressLines,
155
+ final.state === 'done'
156
+ ? `Hand-off complete. ${target} is running this session now.`
157
+ : `Hand-off stopped: ${final.reason ?? 'no reason was reported'}.`,
158
+ whereIsIt(final),
159
+ ].join('\n'),
160
+ };
161
+ }
162
+
163
+ /**
164
+ * One progress line, in the operator's vocabulary.
165
+ *
166
+ * `awaiting-confirmation` is spelled out rather than shortened because it is the phase the operator
167
+ * is most likely to interrupt: the payload is on the other machine and this one has not yet heard
168
+ * that it is on disk there. Quitting here is safe, and the wording is what says so.
169
+ */
170
+ function renderProgress(progress: IHandoffProgress, target: string): string {
171
+ // Exhaustive by construction: the default returns nothing rather than a plausible-looking line, so
172
+ // a new state added to the contract shows up as a blank line in a test instead of as a wrong one.
173
+ switch (progress.state) {
174
+ case 'offered':
175
+ return `Offering the session to ${target}...`;
176
+ case 'sending':
177
+ return 'Sending the session...';
178
+ case 'awaiting-confirmation':
179
+ return `Sent. Waiting for ${target} to confirm it has the session on disk — until it does, this machine keeps it.`;
180
+ case 'done':
181
+ return `${target} confirmed it has the session.`;
182
+ case 'stopped':
183
+ return `Stopped: ${progress.reason ?? 'no reason was reported'}.`;
184
+ default:
185
+ return '';
186
+ }
187
+ }
@@ -0,0 +1,6 @@
1
+ export { executeHandoffCommand } from './handoff-command.js';
2
+ export {
3
+ HandoffCommandSource,
4
+ createHandoffCommandEntry,
5
+ createHandoffCommandModule,
6
+ } from './handoff-command-module.js';
@@ -2,9 +2,13 @@ import type { ICommandHostContext, ICommandSessionRuntime } from '@robota-sdk/ag
2
2
  import { describe, expect, it } from 'vitest';
3
3
  import { createHelpCommandModule } from '../help-command-module.js';
4
4
  import { executeHelpCommand } from '../help-command.js';
5
+ import {
6
+ createTestCommandHost,
7
+ createTestSessionRuntime,
8
+ } from '@robota-sdk/agent-framework/testing';
5
9
 
6
- function createCommandSessionRuntime(): ICommandSessionRuntime {
7
- return {
10
+ function createCommandSessionRuntime() {
11
+ return createTestSessionRuntime({
8
12
  clearHistory: () => undefined,
9
13
  compact: async () => undefined,
10
14
  getContextState: () => ({
@@ -21,10 +25,10 @@ function createCommandSessionRuntime(): ICommandSessionRuntime {
21
25
  getAutoCompactThreshold: () => false,
22
26
  getFullHistory: () => [],
23
27
  getHistory: () => [],
24
- };
28
+ });
25
29
  }
26
30
 
27
- function createCommandHostContext(): ICommandHostContext {
31
+ function createCommandHostContext() {
28
32
  const checkpoint = {
29
33
  id: 'checkpoint_1',
30
34
  sessionId: 'session_1',
@@ -33,42 +37,61 @@ function createCommandHostContext(): ICommandHostContext {
33
37
  createdAt: '2026-05-03T00:00:00.000Z',
34
38
  fileCount: 0,
35
39
  };
36
- return {
37
- getSession: () => createCommandSessionRuntime(),
38
- getContextState: () => ({
39
- maxTokens: 100,
40
- usedTokens: 10,
41
- usedPercentage: 10,
42
- remainingPercentage: 90,
43
- }),
44
- getAutoCompactThreshold: () => 0.8,
45
- compactContext: async () => undefined,
46
- getCwd: () => '/workspace',
47
- listCommands: () => [
48
- { name: 'help', displayName: 'Help', description: 'Show available commands' },
49
- { name: 'provider', displayName: 'Provider Setup', description: 'Manage provider profiles' },
50
- { name: 'plugin', displayName: 'Plugins', description: 'Manage plugins' },
51
- ],
52
- listEditCheckpoints: () => [],
53
- restoreEditCheckpoint: async () => ({
54
- target: checkpoint,
55
- restoredCheckpointCount: 0,
56
- restoredFileCount: 0,
57
- removedCheckpointCount: 0,
58
- }),
59
- rollbackEditCheckpoint: async () => ({
60
- target: checkpoint,
61
- restoredCheckpointCount: 0,
62
- restoredFileCount: 0,
63
- removedCheckpointCount: 0,
64
- }),
65
- getUsedMemoryReferences: () => [],
66
- recordMemoryEvent: () => undefined,
67
- listBackgroundTasks: () => [],
68
- readBackgroundTaskLog: async (taskId) => ({ taskId, lines: [] }),
69
- cancelBackgroundTask: async () => undefined,
70
- closeBackgroundTask: async () => undefined,
71
- };
40
+ return createTestCommandHost({
41
+ overrides: {
42
+ getSession: () => createCommandSessionRuntime(),
43
+ getContextState: () => ({
44
+ maxTokens: 100,
45
+ usedTokens: 10,
46
+ usedPercentage: 10,
47
+ remainingPercentage: 90,
48
+ }),
49
+ getAutoCompactThreshold: () => 0.8,
50
+ compactContext: async () => undefined,
51
+ getCwd: () => '/workspace',
52
+ listCommands: () => [
53
+ {
54
+ name: 'help',
55
+ displayName: 'Help',
56
+ description: 'Show available commands',
57
+ modelInvocable: true,
58
+ },
59
+ {
60
+ name: 'provider',
61
+ displayName: 'Provider Setup',
62
+ description: 'Manage provider profiles',
63
+ modelInvocable: true,
64
+ },
65
+ // SEC-008: `plugin` installs and enables code, so it is NOT model-invocable — the fixture says
66
+ // what the real command says rather than the value that happened to compile.
67
+ {
68
+ name: 'plugin',
69
+ displayName: 'Plugins',
70
+ description: 'Manage plugins',
71
+ modelInvocable: false,
72
+ },
73
+ ],
74
+ listEditCheckpoints: () => [],
75
+ restoreEditCheckpoint: async () => ({
76
+ target: checkpoint,
77
+ restoredCheckpointCount: 0,
78
+ restoredFileCount: 0,
79
+ removedCheckpointCount: 0,
80
+ }),
81
+ rollbackEditCheckpoint: async () => ({
82
+ target: checkpoint,
83
+ restoredCheckpointCount: 0,
84
+ restoredFileCount: 0,
85
+ removedCheckpointCount: 0,
86
+ }),
87
+ getUsedMemoryReferences: () => [],
88
+ recordMemoryEvent: () => undefined,
89
+ listBackgroundTasks: () => [],
90
+ readBackgroundTaskLog: async (taskId) => ({ taskId, lines: [] }),
91
+ cancelBackgroundTask: async () => undefined,
92
+ closeBackgroundTask: async () => undefined,
93
+ },
94
+ });
72
95
  }
73
96
 
74
97
  describe('createHelpCommandModule', () => {
@@ -1,10 +1,14 @@
1
1
  import type { ICommandHostContext, ICommandSessionRuntime } from '@robota-sdk/agent-framework';
2
- import type { ICommandListEntry } from '@robota-sdk/agent-interface-transport';
2
+ import type { ICommandListEntry } from '@robota-sdk/agent-interface-command';
3
3
  import { formatCommandHelpMessage } from '@robota-sdk/agent-framework';
4
4
  import { describe, expect, it } from 'vitest';
5
+ import {
6
+ createTestCommandHost,
7
+ createTestSessionRuntime,
8
+ } from '@robota-sdk/agent-framework/testing';
5
9
 
6
- function createCommandSessionRuntime(): ICommandSessionRuntime {
7
- return {
10
+ function createCommandSessionRuntime() {
11
+ return createTestSessionRuntime({
8
12
  clearHistory: () => undefined,
9
13
  compact: async () => undefined,
10
14
  getContextState: () => ({
@@ -21,7 +25,7 @@ function createCommandSessionRuntime(): ICommandSessionRuntime {
21
25
  getAutoCompactThreshold: () => false,
22
26
  getFullHistory: () => [],
23
27
  getHistory: () => [],
24
- };
28
+ });
25
29
  }
26
30
 
27
31
  function createCheckpoint() {
@@ -35,40 +39,42 @@ function createCheckpoint() {
35
39
  };
36
40
  }
37
41
 
38
- function buildContext(commands: ICommandListEntry[]): ICommandHostContext {
42
+ function buildContext(commands: ICommandListEntry[]) {
39
43
  const checkpoint = createCheckpoint();
40
- return {
41
- getSession: () => createCommandSessionRuntime(),
42
- getContextState: () => ({
43
- maxTokens: 100,
44
- usedTokens: 10,
45
- usedPercentage: 10,
46
- remainingPercentage: 90,
47
- }),
48
- getAutoCompactThreshold: () => 0.8,
49
- compactContext: async () => undefined,
50
- getCwd: () => '/workspace',
51
- listCommands: () => commands,
52
- listEditCheckpoints: () => [],
53
- restoreEditCheckpoint: async () => ({
54
- target: checkpoint,
55
- restoredCheckpointCount: 0,
56
- restoredFileCount: 0,
57
- removedCheckpointCount: 0,
58
- }),
59
- rollbackEditCheckpoint: async () => ({
60
- target: checkpoint,
61
- restoredCheckpointCount: 0,
62
- restoredFileCount: 0,
63
- removedCheckpointCount: 0,
64
- }),
65
- getUsedMemoryReferences: () => [],
66
- recordMemoryEvent: () => undefined,
67
- listBackgroundTasks: () => [],
68
- readBackgroundTaskLog: async (taskId) => ({ taskId, lines: [] }),
69
- cancelBackgroundTask: async () => undefined,
70
- closeBackgroundTask: async () => undefined,
71
- };
44
+ return createTestCommandHost({
45
+ overrides: {
46
+ getSession: () => createCommandSessionRuntime(),
47
+ getContextState: () => ({
48
+ maxTokens: 100,
49
+ usedTokens: 10,
50
+ usedPercentage: 10,
51
+ remainingPercentage: 90,
52
+ }),
53
+ getAutoCompactThreshold: () => 0.8,
54
+ compactContext: async () => undefined,
55
+ getCwd: () => '/workspace',
56
+ listCommands: () => commands,
57
+ listEditCheckpoints: () => [],
58
+ restoreEditCheckpoint: async () => ({
59
+ target: checkpoint,
60
+ restoredCheckpointCount: 0,
61
+ restoredFileCount: 0,
62
+ removedCheckpointCount: 0,
63
+ }),
64
+ rollbackEditCheckpoint: async () => ({
65
+ target: checkpoint,
66
+ restoredCheckpointCount: 0,
67
+ restoredFileCount: 0,
68
+ removedCheckpointCount: 0,
69
+ }),
70
+ getUsedMemoryReferences: () => [],
71
+ recordMemoryEvent: () => undefined,
72
+ listBackgroundTasks: () => [],
73
+ readBackgroundTaskLog: async (taskId) => ({ taskId, lines: [] }),
74
+ cancelBackgroundTask: async () => undefined,
75
+ closeBackgroundTask: async () => undefined,
76
+ },
77
+ });
72
78
  }
73
79
 
74
80
  describe('formatCommandHelpMessage — example field', () => {
@@ -79,6 +85,7 @@ describe('formatCommandHelpMessage — example field', () => {
79
85
  displayName: 'Compact Context',
80
86
  description: 'Compress context window',
81
87
  example: '/compact Summarize the current context',
88
+ modelInvocable: true,
82
89
  },
83
90
  ]);
84
91
 
@@ -94,6 +101,7 @@ describe('formatCommandHelpMessage — example field', () => {
94
101
  displayName: 'Provider Setup',
95
102
  description: 'Manage provider profiles',
96
103
  example: '/provider switch production',
104
+ modelInvocable: true,
97
105
  },
98
106
  ]);
99
107
 
@@ -108,6 +116,7 @@ describe('formatCommandHelpMessage — example field', () => {
108
116
  name: 'help',
109
117
  displayName: 'Help',
110
118
  description: 'Show available commands',
119
+ modelInvocable: true,
111
120
  },
112
121
  ]);
113
122
 
@@ -3,7 +3,7 @@ import { HELP_COMMAND_DESCRIPTION } from '@robota-sdk/agent-framework';
3
3
  import { executeHelpCommand } from './help-command.js';
4
4
 
5
5
  import type { ICommandModule, ISystemCommand } from '@robota-sdk/agent-framework';
6
- import type { ICommand, ICommandSource } from '@robota-sdk/agent-interface-transport';
6
+ import type { ICommand, ICommandSource } from '@robota-sdk/agent-interface-command';
7
7
 
8
8
  export function createHelpCommandEntry(): ICommand {
9
9
  return {
@@ -11,6 +11,7 @@ export function createHelpCommandEntry(): ICommand {
11
11
  displayName: 'Help',
12
12
  description: HELP_COMMAND_DESCRIPTION,
13
13
  source: 'help',
14
+ // User-only: a listing for the user; the model already sees the commands it may run.
14
15
  modelInvocable: false,
15
16
  };
16
17
  }
@@ -1,9 +1,9 @@
1
1
  import { formatCommandHelpMessage } from '@robota-sdk/agent-framework';
2
2
 
3
- import type { ICommandHostContext } from '@robota-sdk/agent-framework';
4
- import type { ICommandResult } from '@robota-sdk/agent-interface-transport';
3
+ import type { ICommandHostCatalog } from '@robota-sdk/agent-framework';
4
+ import type { ICommandResult } from '@robota-sdk/agent-interface-command';
5
5
 
6
- export function executeHelpCommand(context: ICommandHostContext, _args: string): ICommandResult {
6
+ export function executeHelpCommand(context: ICommandHostCatalog, _args: string): ICommandResult {
7
7
  return {
8
8
  success: true,
9
9
  message: formatCommandHelpMessage(context),
package/src/index.ts CHANGED
@@ -2,19 +2,143 @@ export * from './agent/index.js';
2
2
  export * from './default/index.js';
3
3
  export * from './background/index.js';
4
4
  export * from './goal/index.js';
5
+ // HANDOFF-001 (issue #1864). Named rather than `export *`: the star form is frozen pre-existing
6
+ // debt in this barrel, and `sdk-public-surface` lets it shrink but never grow.
7
+ export {
8
+ HandoffCommandSource,
9
+ createHandoffCommandEntry,
10
+ createHandoffCommandModule,
11
+ executeHandoffCommand,
12
+ } from './handoff/index.js';
13
+ export * from './plan/index.js';
5
14
  export * from './compact/index.js';
6
15
  export * from './context/index.js';
7
16
  export * from './editor/index.js';
17
+ export {
18
+ createGitCommandEntry,
19
+ createGitCommandModule,
20
+ createGitProcess,
21
+ executeGitCommand,
22
+ gitEnvironment,
23
+ GitCommandSource,
24
+ type ICreateGitProcessOptions,
25
+ type IGitCommandModuleOptions,
26
+ type IGitProcessPort,
27
+ type IGitProcessRunOptions,
28
+ type TGitCommandContext,
29
+ type TGitProcessFailureReason,
30
+ type TGitProcessOutcome,
31
+ } from './git/index.js';
32
+ // OBSERVABILITY-1991: named rather than `export *` — the star form is frozen debt in this barrel.
33
+ export {
34
+ STORAGE_REPAIR_ID,
35
+ doctorRepairAllowlist,
36
+ DoctorCommandSource,
37
+ applyDoctorRepair,
38
+ buildDoctorReport,
39
+ collectSettingsSecrets,
40
+ createDoctorCommandEntry,
41
+ createDoctorCommandModule,
42
+ createNodeDoctorDeps,
43
+ isDoctorRepairId,
44
+ planDoctorRepair,
45
+ redactDiagnosticText,
46
+ renderDoctorReport,
47
+ type IDoctorDisplayVocabulary,
48
+ resolveCommandOnPath,
49
+ runDoctor,
50
+ type IDoctorCheck,
51
+ type IDoctorDeps,
52
+ type IDoctorEndpointProbeResult,
53
+ type IDoctorInputs,
54
+ type IDoctorPathFacts,
55
+ type IDoctorRepairPlan,
56
+ type IDoctorReport,
57
+ type TDoctorCheckStatus,
58
+ type TDoctorNotProbed,
59
+ type TDoctorRepairOutcome,
60
+ type TDoctorRepairPlanResult,
61
+ } from './doctor/index.js';
62
+ export {
63
+ EffortCommandSource,
64
+ createEffortCommandEntry,
65
+ createEffortCommandModule,
66
+ executeEffortCommand,
67
+ } from './effort/index.js';
68
+ export {
69
+ ADVISOR_SETTING_KEY,
70
+ AdvisorCommandSource,
71
+ createAdvisorCommandEntry,
72
+ createAdvisorCommandModule,
73
+ executeAdvisorCommand,
74
+ } from './advisor/index.js';
8
75
  export * from './exit/index.js';
9
76
  export * from './help/index.js';
77
+ export {
78
+ createDevicesCommandModule,
79
+ type IDeviceListEntry,
80
+ type IDevicesCommandPort,
81
+ type IDevicesInitResult,
82
+ type IDevicesRecoverResult,
83
+ type IDevicesRevokeResult,
84
+ type IDevicesView,
85
+ type TDevicesOutcome,
86
+ type TDevicesRefusal,
87
+ } from './devices/index.js';
88
+ export {
89
+ createKeybindingsCommandEntry,
90
+ createKeybindingsCommandModule,
91
+ KeybindingsCommandSource,
92
+ type IKeybindingsFilePort,
93
+ } from './keybindings/index.js';
94
+ export {
95
+ createThemeCommandEntry,
96
+ createThemeCommandModule,
97
+ executeThemeCommand,
98
+ ThemeCommandSource,
99
+ } from './theme/index.js';
100
+ // Re-exported from the contract package so a composition root that depends on this package — the
101
+ // CLI — can name the port it injects without also depending on the interface package directly.
102
+ export type {
103
+ IThemeAppearanceState,
104
+ IThemeCatalogueEntry,
105
+ IThemeCataloguePort,
106
+ TReducedMotionOverride,
107
+ } from './theme/index.js';
10
108
  export * from './language/index.js';
11
109
  export * from './memory/index.js';
110
+ export {
111
+ createMCPActivationCommandEntry,
112
+ createMCPActivationCommandModule,
113
+ MCPActivationCommandSource,
114
+ } from './mcp-activation/mcp-activation-command-module.js';
115
+ export { executeMCPActivationCommand } from './mcp-activation/mcp-activation-command.js';
116
+ export {
117
+ mcpUnavailableServersNotice,
118
+ mcpUserActionCommand,
119
+ mcpUserActionNotice,
120
+ } from './mcp-activation/mcp-model-notice.js';
121
+ export type { TMCPUserAction, TMCPUserActionSurface } from './mcp-activation/mcp-model-notice.js';
12
122
  export * from './mode/index.js';
123
+ export {
124
+ createSandboxCommandModule,
125
+ executeSandboxCommand,
126
+ formatSandboxStatus,
127
+ SANDBOX_MODES,
128
+ SandboxCommandSource,
129
+ } from './sandbox/index.js';
130
+ export {
131
+ OutputStyleCommandSource,
132
+ createOutputStyleCommandEntry,
133
+ createOutputStyleCommandModule,
134
+ executeOutputStyleCommand,
135
+ } from './output-style/index.js';
13
136
  export * from './permissions/index.js';
14
137
  export * from './plugin/index.js';
15
138
  export * from './preset/index.js';
16
139
  export * from './shell/index.js';
17
140
  export * from './provider/index.js';
141
+ export * from './remote-control/index.js';
18
142
  export * from './reset/index.js';
19
143
  export * from './rewind/index.js';
20
144
  export * from './schedule/index.js';
@@ -23,5 +147,3 @@ export * from './settings/index.js';
23
147
  export * from './skills/index.js';
24
148
  export * from './statusline/index.js';
25
149
  export * from './user-local/index.js';
26
- export { createDefaultPluginCommandAdapter } from './plugins/default-plugin-command-adapter.js';
27
- export { reloadPluginCommandSource } from './plugins/default-plugin-command-source-loader.js';