@skrr-ai/cli 0.1.36 → 0.1.38

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 (266) hide show
  1. package/README.md +17 -4
  2. package/bin/dev-fallback.js +167 -4
  3. package/dist/base-command.js +46 -2
  4. package/dist/commands/agents/actions/create.d.ts +5 -0
  5. package/dist/commands/agents/actions/create.js +42 -5
  6. package/dist/commands/agents/actions/delete.d.ts +1 -0
  7. package/dist/commands/agents/actions/delete.js +7 -0
  8. package/dist/commands/agents/actions/update.d.ts +5 -0
  9. package/dist/commands/agents/actions/update.js +27 -1
  10. package/dist/commands/agents/api-actions/add.d.ts +41 -0
  11. package/dist/commands/agents/api-actions/add.js +205 -0
  12. package/dist/commands/agents/api-actions/index.d.ts +39 -0
  13. package/dist/commands/agents/api-actions/index.js +103 -0
  14. package/dist/commands/agents/api-actions/remove.d.ts +14 -0
  15. package/dist/commands/agents/api-actions/remove.js +36 -0
  16. package/dist/commands/agents/avatar/default/apply.d.ts +25 -0
  17. package/dist/commands/agents/avatar/default/apply.js +56 -0
  18. package/dist/commands/agents/avatar/default/index.d.ts +29 -0
  19. package/dist/commands/agents/avatar/default/index.js +61 -0
  20. package/dist/commands/agents/avatar/default/set.d.ts +24 -0
  21. package/dist/commands/agents/avatar/default/set.js +120 -0
  22. package/dist/commands/agents/avatar/library/delete.d.ts +20 -0
  23. package/dist/commands/agents/avatar/library/delete.js +47 -0
  24. package/dist/commands/agents/avatar/library/index.d.ts +16 -0
  25. package/dist/commands/agents/avatar/library/index.js +56 -0
  26. package/dist/commands/agents/avatar/presets.d.ts +19 -0
  27. package/dist/commands/agents/avatar/presets.js +56 -0
  28. package/dist/commands/agents/avatar/upload.js +7 -1
  29. package/dist/commands/agents/categories/create.d.ts +14 -0
  30. package/dist/commands/agents/categories/create.js +46 -0
  31. package/dist/commands/agents/categories/delete.d.ts +13 -0
  32. package/dist/commands/agents/categories/delete.js +41 -0
  33. package/dist/commands/agents/categories/index.d.ts +36 -0
  34. package/dist/commands/agents/categories/index.js +135 -0
  35. package/dist/commands/agents/categories/update.d.ts +22 -0
  36. package/dist/commands/agents/categories/update.js +59 -0
  37. package/dist/commands/agents/chat.d.ts +21 -0
  38. package/dist/commands/agents/chat.js +94 -7
  39. package/dist/commands/agents/create.d.ts +10 -0
  40. package/dist/commands/agents/create.js +64 -3
  41. package/dist/commands/agents/ensure-default.d.ts +27 -0
  42. package/dist/commands/agents/ensure-default.js +65 -0
  43. package/dist/commands/agents/list.d.ts +15 -0
  44. package/dist/commands/agents/list.js +31 -1
  45. package/dist/commands/agents/mute.d.ts +16 -0
  46. package/dist/commands/agents/mute.js +38 -0
  47. package/dist/commands/agents/pin.d.ts +16 -0
  48. package/dist/commands/agents/pin.js +38 -0
  49. package/dist/commands/agents/scene-background/upload.d.ts +30 -0
  50. package/dist/commands/agents/scene-background/upload.js +97 -0
  51. package/dist/commands/agents/show.js +13 -0
  52. package/dist/commands/agents/tools/auth.d.ts +19 -0
  53. package/dist/commands/agents/tools/auth.js +41 -0
  54. package/dist/commands/agents/tools/calls.d.ts +36 -0
  55. package/dist/commands/agents/tools/calls.js +117 -0
  56. package/dist/commands/agents/tools/index.d.ts +20 -0
  57. package/dist/commands/agents/tools/index.js +77 -0
  58. package/dist/commands/agents/unmute.d.ts +16 -0
  59. package/dist/commands/agents/unmute.js +38 -0
  60. package/dist/commands/agents/unpin.d.ts +16 -0
  61. package/dist/commands/agents/unpin.js +38 -0
  62. package/dist/commands/agents/update.d.ts +12 -0
  63. package/dist/commands/agents/update.js +74 -3
  64. package/dist/commands/code/{oversky-agent.d.ts → skrr-agent.d.ts} +3 -3
  65. package/dist/commands/code/{oversky-agent.js → skrr-agent.js} +11 -8
  66. package/dist/commands/convos/messages.d.ts +13 -0
  67. package/dist/commands/convos/messages.js +40 -16
  68. package/dist/commands/daemon/index.js +8 -1
  69. package/dist/commands/daemon/logs.d.ts +26 -0
  70. package/dist/commands/daemon/logs.js +56 -0
  71. package/dist/commands/daemon/uninstall.js +1 -1
  72. package/dist/commands/followups/act.d.ts +22 -0
  73. package/dist/commands/followups/act.js +103 -0
  74. package/dist/commands/followups/cancel.d.ts +20 -0
  75. package/dist/commands/followups/cancel.js +50 -0
  76. package/dist/commands/followups/checkin.d.ts +27 -0
  77. package/dist/commands/followups/checkin.js +107 -0
  78. package/dist/commands/followups/list.d.ts +24 -0
  79. package/dist/commands/followups/list.js +90 -0
  80. package/dist/commands/followups/observability.d.ts +23 -0
  81. package/dist/commands/followups/observability.js +114 -0
  82. package/dist/commands/followups/remind.d.ts +26 -0
  83. package/dist/commands/followups/remind.js +106 -0
  84. package/dist/commands/followups/reschedule.d.ts +15 -0
  85. package/dist/commands/followups/reschedule.js +48 -0
  86. package/dist/commands/followups/resolve.d.ts +23 -0
  87. package/dist/commands/followups/resolve.js +54 -0
  88. package/dist/commands/followups/show.d.ts +12 -0
  89. package/dist/commands/followups/show.js +46 -0
  90. package/dist/commands/followups/watch.d.ts +51 -0
  91. package/dist/commands/followups/watch.js +211 -0
  92. package/dist/commands/goals/labels/add.js +19 -2
  93. package/dist/commands/goals/labels/remove.js +22 -2
  94. package/dist/commands/harnesses/forget.js +28 -0
  95. package/dist/commands/harnesses/install.d.ts +41 -0
  96. package/dist/commands/harnesses/install.js +165 -0
  97. package/dist/commands/harnesses/installers.d.ts +45 -0
  98. package/dist/commands/harnesses/installers.js +155 -0
  99. package/dist/commands/harnesses/list.d.ts +82 -1
  100. package/dist/commands/harnesses/list.js +104 -12
  101. package/dist/commands/harnesses/models.js +10 -1
  102. package/dist/commands/harnesses/show.d.ts +15 -0
  103. package/dist/commands/harnesses/show.js +44 -3
  104. package/dist/commands/harnesses/update.js +1 -1
  105. package/dist/commands/harnesses/usage.d.ts +25 -0
  106. package/dist/commands/harnesses/usage.js +24 -2
  107. package/dist/commands/instructions/status.js +13 -1
  108. package/dist/commands/labels/list.d.ts +24 -1
  109. package/dist/commands/labels/list.js +62 -19
  110. package/dist/commands/machines/dedicated/catalog.js +4 -15
  111. package/dist/commands/machines/dedicated/create.d.ts +8 -0
  112. package/dist/commands/machines/dedicated/create.js +58 -1
  113. package/dist/commands/machines/dedicated/destroy.js +18 -4
  114. package/dist/commands/machines/dedicated/list.js +9 -6
  115. package/dist/commands/machines/hosted/list.js +12 -0
  116. package/dist/commands/machines/list.js +11 -0
  117. package/dist/commands/machines/show.d.ts +1 -0
  118. package/dist/commands/machines/show.js +40 -11
  119. package/dist/commands/memory/governance/index.d.ts +21 -0
  120. package/dist/commands/memory/governance/index.js +64 -0
  121. package/dist/commands/memory/governance/set.d.ts +24 -0
  122. package/dist/commands/memory/governance/set.js +95 -0
  123. package/dist/commands/memory/provenance.d.ts +18 -0
  124. package/dist/commands/memory/provenance.js +66 -0
  125. package/dist/commands/profiles/list.d.ts +23 -0
  126. package/dist/commands/profiles/list.js +96 -0
  127. package/dist/commands/search/skills.d.ts +11 -1
  128. package/dist/commands/search/skills.js +26 -8
  129. package/dist/commands/skills/details.d.ts +2 -0
  130. package/dist/commands/skills/details.js +24 -1
  131. package/dist/commands/skills/import-as-actions.d.ts +32 -0
  132. package/dist/commands/skills/import-as-actions.js +175 -0
  133. package/dist/commands/skills/install.d.ts +2 -0
  134. package/dist/commands/skills/install.js +24 -1
  135. package/dist/commands/skills/invocable.js +6 -2
  136. package/dist/commands/skills/local.d.ts +40 -0
  137. package/dist/commands/skills/local.js +160 -0
  138. package/dist/commands/skills/search.d.ts +11 -1
  139. package/dist/commands/skills/search.js +29 -8
  140. package/dist/commands/spaces/actions/add.js +23 -5
  141. package/dist/commands/spaces/actions/available.js +3 -0
  142. package/dist/commands/spaces/actions/list.d.ts +5 -0
  143. package/dist/commands/spaces/actions/list.js +49 -3
  144. package/dist/commands/spaces/actions/run.d.ts +28 -0
  145. package/dist/commands/spaces/actions/run.js +146 -0
  146. package/dist/commands/spaces/actions/update.js +27 -5
  147. package/dist/commands/spaces/create.d.ts +3 -0
  148. package/dist/commands/spaces/create.js +32 -2
  149. package/dist/commands/spaces/label-summaries.d.ts +27 -0
  150. package/dist/commands/spaces/label-summaries.js +64 -0
  151. package/dist/commands/spaces/labels/add.js +19 -2
  152. package/dist/commands/spaces/labels/remove.js +22 -2
  153. package/dist/commands/spaces/show.js +2 -0
  154. package/dist/commands/spaces/task-rollups.d.ts +5 -0
  155. package/dist/commands/spaces/task-rollups.js +15 -4
  156. package/dist/commands/spaces/update.d.ts +5 -0
  157. package/dist/commands/spaces/update.js +41 -4
  158. package/dist/commands/spaces/workflows/lifecycle-diagnostics.d.ts +11 -0
  159. package/dist/commands/spaces/workflows/lifecycle-diagnostics.js +105 -0
  160. package/dist/commands/tasks/actions/attach.js +32 -7
  161. package/dist/commands/tasks/complete.d.ts +1 -0
  162. package/dist/commands/tasks/complete.js +21 -1
  163. package/dist/commands/tasks/create.d.ts +3 -0
  164. package/dist/commands/tasks/create.js +34 -2
  165. package/dist/commands/tasks/events/append.js +29 -6
  166. package/dist/commands/tasks/expectations/assess.d.ts +12 -0
  167. package/dist/commands/tasks/expectations/assess.js +123 -14
  168. package/dist/commands/tasks/expectations.js +95 -17
  169. package/dist/commands/tasks/follow-up.js +2 -2
  170. package/dist/commands/tasks/labels/attach.js +20 -3
  171. package/dist/commands/tasks/labels/detach.js +21 -3
  172. package/dist/commands/tasks/lifecycle/attach.d.ts +14 -0
  173. package/dist/commands/tasks/lifecycle/attach.js +47 -0
  174. package/dist/commands/tasks/lifecycle/definitions.d.ts +11 -0
  175. package/dist/commands/tasks/lifecycle/definitions.js +59 -0
  176. package/dist/commands/tasks/lifecycle/detach.d.ts +19 -0
  177. package/dist/commands/tasks/lifecycle/detach.js +72 -0
  178. package/dist/commands/tasks/lifecycle/history.d.ts +13 -0
  179. package/dist/commands/tasks/lifecycle/history.js +38 -0
  180. package/dist/commands/tasks/lifecycle/migrate.d.ts +14 -0
  181. package/dist/commands/tasks/lifecycle/migrate.js +68 -0
  182. package/dist/commands/tasks/lifecycle/reconcile.d.ts +11 -0
  183. package/dist/commands/tasks/lifecycle/reconcile.js +27 -0
  184. package/dist/commands/tasks/lifecycle/reopen.d.ts +15 -0
  185. package/dist/commands/tasks/lifecycle/reopen.js +71 -0
  186. package/dist/commands/tasks/lifecycle/show.d.ts +11 -0
  187. package/dist/commands/tasks/lifecycle/show.js +27 -0
  188. package/dist/commands/tasks/lifecycle/start.d.ts +12 -0
  189. package/dist/commands/tasks/lifecycle/start.js +30 -0
  190. package/dist/commands/tasks/lifecycle/transition.d.ts +15 -0
  191. package/dist/commands/tasks/lifecycle/transition.js +109 -0
  192. package/dist/commands/tasks/list.js +16 -9
  193. package/dist/commands/tasks/move.d.ts +1 -0
  194. package/dist/commands/tasks/move.js +29 -0
  195. package/dist/commands/tasks/result/submit.js +11 -1
  196. package/dist/commands/tasks/review-queue.d.ts +44 -0
  197. package/dist/commands/tasks/review-queue.js +314 -0
  198. package/dist/commands/tasks/show.d.ts +11 -0
  199. package/dist/commands/tasks/show.js +139 -4
  200. package/dist/commands/tasks/trust.js +7 -2
  201. package/dist/commands/tasks/update.d.ts +1 -0
  202. package/dist/commands/tasks/update.js +83 -0
  203. package/dist/commands/tasks/updates/add.js +1 -1
  204. package/dist/commands/token/list.d.ts +22 -0
  205. package/dist/commands/token/list.js +68 -0
  206. package/dist/commands/token/revoke.d.ts +23 -0
  207. package/dist/commands/token/revoke.js +53 -0
  208. package/dist/lib/agent-actions.d.ts +92 -11
  209. package/dist/lib/agent-actions.js +171 -25
  210. package/dist/lib/agent-config.d.ts +11 -0
  211. package/dist/lib/agent-config.js +85 -3
  212. package/dist/lib/agent-pin-mute.d.ts +29 -0
  213. package/dist/lib/agent-pin-mute.js +30 -0
  214. package/dist/lib/agent-visual-refs.d.ts +33 -0
  215. package/dist/lib/agent-visual-refs.js +267 -0
  216. package/dist/lib/agentic-stream.d.ts +6 -0
  217. package/dist/lib/agentic-stream.js +34 -3
  218. package/dist/lib/assignee-resolver.d.ts +1 -1
  219. package/dist/lib/assignee-resolver.js +8 -0
  220. package/dist/lib/bulk-task-targets.d.ts +12 -0
  221. package/dist/lib/bulk-task-targets.js +34 -1
  222. package/dist/lib/cli-installers.d.ts +130 -0
  223. package/dist/lib/cli-installers.js +145 -0
  224. package/dist/lib/command-miss.js +55 -7
  225. package/dist/lib/dedicated-lease-command.d.ts +13 -0
  226. package/dist/lib/dedicated-lease-command.js +22 -1
  227. package/dist/lib/dedicated-machines.d.ts +95 -1
  228. package/dist/lib/dedicated-machines.js +236 -2
  229. package/dist/lib/failed-lookup.d.ts +3 -0
  230. package/dist/lib/failed-lookup.js +40 -0
  231. package/dist/lib/first-party-harness-agent.js +16 -2
  232. package/dist/lib/first-party-harness-doctor.d.ts +15 -0
  233. package/dist/lib/first-party-harness-doctor.js +126 -27
  234. package/dist/lib/followups.d.ts +115 -0
  235. package/dist/lib/followups.js +127 -0
  236. package/dist/lib/harnesses.d.ts +8 -0
  237. package/dist/lib/instruction-input.d.ts +19 -1
  238. package/dist/lib/instruction-input.js +52 -1
  239. package/dist/lib/label-ref.d.ts +95 -0
  240. package/dist/lib/label-ref.js +163 -0
  241. package/dist/lib/label-scope.d.ts +17 -2
  242. package/dist/lib/label-scope.js +27 -7
  243. package/dist/lib/local-skills.d.ts +98 -0
  244. package/dist/lib/local-skills.js +195 -0
  245. package/dist/lib/machines.d.ts +10 -10
  246. package/dist/lib/machines.js +23 -1
  247. package/dist/lib/search-query.d.ts +19 -0
  248. package/dist/lib/search-query.js +35 -0
  249. package/dist/lib/space-custom-actions.d.ts +72 -0
  250. package/dist/lib/space-custom-actions.js +117 -0
  251. package/dist/lib/space-resolver.d.ts +1 -21
  252. package/dist/lib/space-resolver.js +19 -10
  253. package/dist/lib/task-closure.d.ts +33 -0
  254. package/dist/lib/task-closure.js +104 -0
  255. package/dist/lib/task-lifecycle-output.d.ts +14 -0
  256. package/dist/lib/task-lifecycle-output.js +209 -0
  257. package/dist/lib/task-ref-resolver.d.ts +1 -17
  258. package/dist/lib/task-ref-resolver.js +18 -8
  259. package/dist/lib/tasks.d.ts +3 -0
  260. package/dist/lib/tasks.js +40 -3
  261. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessTrust.js +4 -0
  262. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessTrust.js +4 -0
  263. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  264. package/dist/node_modules/@skrr-ai/data-provider/index.js +5559 -4897
  265. package/oclif.manifest.json +33890 -28690
  266. package/package.json +24 -6
@@ -1,2 +1,13 @@
1
+ export declare const COMMITMENT_RESPONSIBILITY_LEVELS: readonly ["reasoning_only", "full_ownership"];
2
+ export declare const REASONING_TOOL_ESCAPES: readonly ["bash", "browser"];
3
+ export declare const RELEASE_INSTALL_CHANNELS: readonly ["latest", "manual"];
4
+ /**
5
+ * `09:00-18:00` or `09:00-18:00@America/Los_Angeles`, or a JSON object.
6
+ *
7
+ * The stored shape is `{start, end, timezone}` with `start`/`end` REQUIRED, so
8
+ * the shorthand always supplies both and never a half-open window the schema
9
+ * would reject at write time with a message about a subdocument.
10
+ */
11
+ export declare function parseWorkingHoursFlag(value: string): Record<string, unknown>;
1
12
  /** Apply the structured agent fields exposed by the web config editor. */
2
13
  export declare function mergeAgentConfigFlags(body: Record<string, unknown>, flags: Record<string, unknown>): Record<string, unknown>;
@@ -1,7 +1,35 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RELEASE_INSTALL_CHANNELS = exports.REASONING_TOOL_ESCAPES = exports.COMMITMENT_RESPONSIBILITY_LEVELS = void 0;
4
+ exports.parseWorkingHoursFlag = parseWorkingHoursFlag;
3
5
  exports.mergeAgentConfigFlags = mergeAgentConfigFlags;
4
6
  const prompt_1 = require("./prompt");
7
+ const agent_visual_refs_1 = require("./agent-visual-refs");
8
+ exports.COMMITMENT_RESPONSIBILITY_LEVELS = ['reasoning_only', 'full_ownership'];
9
+ exports.REASONING_TOOL_ESCAPES = ['bash', 'browser'];
10
+ exports.RELEASE_INSTALL_CHANNELS = ['latest', 'manual'];
11
+ /**
12
+ * `09:00-18:00` or `09:00-18:00@America/Los_Angeles`, or a JSON object.
13
+ *
14
+ * The stored shape is `{start, end, timezone}` with `start`/`end` REQUIRED, so
15
+ * the shorthand always supplies both and never a half-open window the schema
16
+ * would reject at write time with a message about a subdocument.
17
+ */
18
+ function parseWorkingHoursFlag(value) {
19
+ const trimmed = value.trim();
20
+ if (trimmed.startsWith('{'))
21
+ return JSON.parse(trimmed);
22
+ const [window, timezone] = trimmed.split('@');
23
+ const match = /^([0-2]\d:[0-5]\d)-([0-2]\d:[0-5]\d)$/.exec(window ?? '');
24
+ if (!match) {
25
+ throw new Error(`--todo-working-hours: cannot read "${value}". ` +
26
+ 'Write HH:MM-HH:MM, optionally @<IANA timezone> — for example ' +
27
+ '09:00-18:00@America/Los_Angeles. A JSON object is also accepted.');
28
+ }
29
+ return timezone
30
+ ? { start: match[1], end: match[2], timezone }
31
+ : { start: match[1], end: match[2] };
32
+ }
5
33
  /** Apply the structured agent fields exposed by the web config editor. */
6
34
  function mergeAgentConfigFlags(body, flags) {
7
35
  const json = {
@@ -22,10 +50,21 @@ function mergeAgentConfigFlags(body, flags) {
22
50
  if (value !== undefined)
23
51
  parsed[field] = JSON.parse(String(value));
24
52
  }
53
+ /**
54
+ * `avatar` and `sceneBackground` are DISCRIMINATED UNIONS server-side, not
55
+ * strings — see `agent-visual-refs.ts`. `--scene-background` used to sit in
56
+ * the pass-through map below, which made it a flag with no value that could
57
+ * pass validation. Both go through the shared parser.
58
+ */
59
+ if (flags['scene-background'] !== undefined) {
60
+ parsed.sceneBackground = (0, agent_visual_refs_1.parseSceneBackgroundFlag)(String(flags['scene-background']));
61
+ }
62
+ if (flags.avatar !== undefined) {
63
+ parsed.avatar = (0, agent_visual_refs_1.parseAvatarRefFlag)(String(flags.avatar));
64
+ }
25
65
  const direct = {
26
66
  'network-mode': 'network_mode',
27
67
  'character-3d-id': 'character3DId',
28
- 'scene-background': 'sceneBackground',
29
68
  'tone-mode': 'toneMode',
30
69
  'tone-id': 'toneId',
31
70
  'tone-notes': 'toneNotes',
@@ -46,8 +85,51 @@ function mergeAgentConfigFlags(body, flags) {
46
85
  if (flags[flag] !== undefined)
47
86
  parsed[field] = flags[flag];
48
87
  }
49
- if (flags['recursion-limit'] !== undefined)
50
- parsed.recursion_limit = flags['recursion-limit'];
88
+ const integers = {
89
+ 'recursion-limit': 'recursion_limit',
90
+ 'max-turns': 'maxTurns',
91
+ };
92
+ for (const [flag, field] of Object.entries(integers)) {
93
+ if (flags[flag] !== undefined)
94
+ parsed[field] = flags[flag];
95
+ }
96
+ /* Fields the web config editor has always written and no flag reached. */
97
+ if (flags['conversation-starter'] !== undefined) {
98
+ parsed.conversation_starters = flags['conversation-starter'];
99
+ }
100
+ if (flags['todo-working-hours'] !== undefined) {
101
+ parsed.todoWorkingHours = parseWorkingHoursFlag(String(flags['todo-working-hours']));
102
+ }
103
+ if (flags['reasoning-tool-escape'] !== undefined) {
104
+ parsed.reasoningToolEscapes = flags['reasoning-tool-escape'];
105
+ }
106
+ if (flags['commitment-responsibility'] !== undefined) {
107
+ parsed.commitmentResponsibilityDefault = flags['commitment-responsibility'];
108
+ }
109
+ if (flags['commitment-responsibility-ceiling'] !== undefined) {
110
+ parsed.commitmentResponsibilityCeiling = flags['commitment-responsibility-ceiling'];
111
+ }
112
+ if (flags['tool-resources'] !== undefined) {
113
+ parsed.tool_resources = JSON.parse(String(flags['tool-resources']));
114
+ }
115
+ if (flags['project-id'] !== undefined)
116
+ parsed.projectIds = flags['project-id'];
117
+ if (flags['remove-project-id'] !== undefined) {
118
+ parsed.removeProjectIds = flags['remove-project-id'];
119
+ }
120
+ // Two flags, one stored object. Sent whole so a caller who sets only the
121
+ // channel does not silently clear `allowAutoMinorUpdates` — the server
122
+ // replaces the subdocument rather than merging into it.
123
+ if (flags['install-channel'] !== undefined || flags['auto-minor-updates'] !== undefined) {
124
+ const releasePolicy = {};
125
+ if (flags['install-channel'] !== undefined) {
126
+ releasePolicy.defaultInstallChannel = flags['install-channel'];
127
+ }
128
+ if (flags['auto-minor-updates'] !== undefined) {
129
+ releasePolicy.allowAutoMinorUpdates = flags['auto-minor-updates'];
130
+ }
131
+ parsed.releasePolicy = releasePolicy;
132
+ }
51
133
  // Cross-Space Task execution ceiling. `0` means unlimited, matching the
52
134
  // Space limits' convention so a user learns one spelling of "no cap".
53
135
  if (flags['max-concurrent-task-executions'] !== undefined) {
@@ -0,0 +1,29 @@
1
+ /**
2
+ * `pin` / `unpin` / `mute` / `unmute` on top of a TOGGLE endpoint.
3
+ *
4
+ * `PATCH /api/user/agent-preferences/:agentId/pin` flips the bit; it does not
5
+ * take a desired state. A CLI that exposed the flip directly would make
6
+ * `skrr agents pin X` mean "pin it, unless it already was, in which case unpin
7
+ * it" — fine for a button a human clicks while looking at the result, wrong for
8
+ * a command in a script, where the outcome would depend on state the script
9
+ * never read.
10
+ *
11
+ * So the desired state is sent, and the endpoint now takes one. It used to be
12
+ * read-then-flip-if-different, which made each command idempotent when run
13
+ * alone and inverted under concurrency: six `skrr agents pin` calls that each
14
+ * read "unpinned" sent six flips, and six flips from false is false — pinning an
15
+ * agent six times left it unpinned. That was never fixable from out here, as the
16
+ * previous version of this comment said; it needed the endpoint to accept a
17
+ * desired state, which is what `pinned`/`muted` in the body now is.
18
+ *
19
+ * `changed` comes from the server, which is the only place that can compare the
20
+ * old value with the new one without a second race.
21
+ */
22
+ export type AgentFlag = 'pin' | 'mute';
23
+ export interface FlagChange {
24
+ /** The value the server reports AFTER this call. */
25
+ value: boolean;
26
+ /** False when the flag already held the desired value and nothing was sent. */
27
+ changed: boolean;
28
+ }
29
+ export declare function setAgentFlag(agentId: string, flag: AgentFlag, desired: boolean): Promise<FlagChange>;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setAgentFlag = setAgentFlag;
4
+ const data_provider_1 = require("@skrr-ai/data-provider");
5
+ async function setAgentFlag(agentId, flag, desired) {
6
+ const current = await data_provider_1.dataService.getAgentPreferences(agentId);
7
+ const held = flag === 'pin' ? current.preferences?.isPinned : current.preferences?.isMuted;
8
+ // Nothing to send. True on every server version, and the one case where a
9
+ // write would be pure risk.
10
+ if (Boolean(held) === desired) {
11
+ return { value: desired, changed: false };
12
+ }
13
+ // The read stays, and it is NOT what the race was. The race was read-then-
14
+ // FLIP: the write depended on what was read, so two readers of the same value
15
+ // sent two flips. The write now carries the desired state, so any number of
16
+ // them land on it — the read only decides whether to speak at all.
17
+ //
18
+ // It also keeps this honest against a server that has not been deployed yet.
19
+ // A build without the desired-state branch ignores the body and flips; from a
20
+ // state we have just confirmed differs, one flip still reaches `desired`, so
21
+ // the reported outcome is true either way. Reporting the server's `changed`
22
+ // alone was not: an old server omits it, `?? false` read that as "nothing to
23
+ // do", and `skrr agents pin` said so while flipping the bit.
24
+ if (flag === 'pin') {
25
+ const result = await data_provider_1.dataService.toggleAgentPin(agentId, desired);
26
+ return { value: result.isPinned, changed: result.changed ?? true };
27
+ }
28
+ const result = await data_provider_1.dataService.toggleAgentMute(agentId, desired);
29
+ return { value: result.isMuted, changed: result.changed ?? true };
30
+ }
@@ -0,0 +1,33 @@
1
+ import type { AgentAvatarRef, AgentSceneBackground } from '@skrr-ai/data-provider';
2
+ /**
3
+ * The `--scene-background` help, shared by `agents create` and `agents update`.
4
+ *
5
+ * It named three forms and no preset ids, while the refusal it led to named a
6
+ * fourth form (JSON) and listed all the ids — so the only way to learn the valid
7
+ * values was to type a wrong one on purpose. `--avatar`, one flag away, already
8
+ * did this right. Built from the shipped catalog so it cannot fall behind it.
9
+ */
10
+ export declare const SCENE_BACKGROUND_FLAG_HELP: string;
11
+ export declare function imageUploadMimeType(filePath: string): string | undefined;
12
+ /** Preset ids this build ships, for an error message that can be acted on. */
13
+ export declare function avatarPresetIds(): string[];
14
+ export declare function sceneBackgroundPresetIds(): string[];
15
+ export declare function parseAvatarRefFlag(value: string): AgentAvatarRef;
16
+ export declare function parseSceneBackgroundFlag(value: string): AgentSceneBackground;
17
+ /**
18
+ * The avatar line for `agents show` text output.
19
+ *
20
+ * `agents show` printed no avatar at all, so its output was byte-identical before
21
+ * and after `agents update --avatar preset:studio-halo`: "has the Halo preset"
22
+ * and "has no face whatsoever" could not be told apart without `--json` and
23
+ * knowing which raw field to decode. The coercion `--avatar` performs is the one
24
+ * thing a reader would want confirmed, and it was the one thing hidden.
25
+ *
26
+ * Rendered in the same shorthand `--avatar` accepts, so what is printed is what
27
+ * can be typed back. An ABSENT avatar is kept apart from an explicit
28
+ * `{type:'default'}`: both show the platform logo, but only the absent one was
29
+ * never chosen, and that is exactly what `agents avatar default apply` rewrites.
30
+ */
31
+ export declare function agentAvatarLine(raw: unknown): string;
32
+ /** The scene-background line, in the shorthand `--scene-background` accepts. */
33
+ export declare function agentSceneBackgroundLine(raw: unknown): string;
@@ -0,0 +1,267 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SCENE_BACKGROUND_FLAG_HELP = void 0;
4
+ exports.imageUploadMimeType = imageUploadMimeType;
5
+ exports.avatarPresetIds = avatarPresetIds;
6
+ exports.sceneBackgroundPresetIds = sceneBackgroundPresetIds;
7
+ exports.parseAvatarRefFlag = parseAvatarRefFlag;
8
+ exports.parseSceneBackgroundFlag = parseSceneBackgroundFlag;
9
+ exports.agentAvatarLine = agentAvatarLine;
10
+ exports.agentSceneBackgroundLine = agentSceneBackgroundLine;
11
+ /**
12
+ * The two "how does this agent look" fields, parsed from a flag.
13
+ *
14
+ * WHY A PARSER AND NOT A PLAIN STRING
15
+ * ───────────────────────────────────
16
+ * `agent.avatar` and `agent.sceneBackground` are DISCRIMINATED UNIONS on the
17
+ * wire (`packages/api/src/agents/validation.ts`), because a face and a backdrop
18
+ * each have several possible origins — a built-in preset, the caller's own
19
+ * uploaded asset, an explicit "the platform default", a raw storage path — and
20
+ * a bare string cannot say which. `agentUpdateSchema.parse(req.body)` runs with
21
+ * no coercion, so a string reaches the server and is rejected.
22
+ *
23
+ * `--scene-background` shipped as a pass-through string for exactly that
24
+ * reason, which made it a flag with NO value that could ever succeed: every
25
+ * invocation was a 400 naming a zod path rather than the flag. The fix is to
26
+ * own the shorthand here, once, so both fields read the same way and neither
27
+ * can regress into a raw string again.
28
+ *
29
+ * THE SHORTHAND
30
+ * ─────────────
31
+ * default → { type: 'default' }
32
+ * preset:<id> → { type: 'preset', id }
33
+ * asset:<id> → { type: 'asset', id } (avatar only)
34
+ * image:<filepath>@<source> → { type: 'image', filepath, source }
35
+ * {"type":"preset","id":"..."} → parsed verbatim, the escape hatch
36
+ *
37
+ * A bare id is deliberately NOT accepted. `preset:studio` and an asset id are
38
+ * different namespaces, and guessing between them would make the failure mode
39
+ * "your avatar silently became someone else's" rather than a parse error.
40
+ */
41
+ const data_provider_1 = require("@skrr-ai/data-provider");
42
+ /**
43
+ * The `--scene-background` help, shared by `agents create` and `agents update`.
44
+ *
45
+ * It named three forms and no preset ids, while the refusal it led to named a
46
+ * fourth form (JSON) and listed all the ids — so the only way to learn the valid
47
+ * values was to type a wrong one on purpose. `--avatar`, one flag away, already
48
+ * did this right. Built from the shipped catalog so it cannot fall behind it.
49
+ */
50
+ exports.SCENE_BACKGROUND_FLAG_HELP = 'Backdrop for the 3D view: default, preset:<id>, image:<filepath>@<source>, or a JSON ' +
51
+ `object such as {"type":"preset","id":"midnight"}. Presets: ${data_provider_1.AGENT_SCENE_BACKGROUND_PRESETS.map((preset) => preset.id).join(', ')}. For your own photo use \`skrr agents scene-background upload\` — ` +
52
+ '<filepath> is the storage path that command produces, not a file on disk.';
53
+ /**
54
+ * The MIME type to send an image upload as, from its file extension.
55
+ *
56
+ * The upload routes gate on the multipart part's type, and a part built as
57
+ * `new Blob([bytes])` has none, so it arrives as `application/octet-stream` and
58
+ * is refused whatever its bytes are. `agents avatar upload` shipped that way and
59
+ * could not upload any file (OSK-9841). Returns undefined for anything that is
60
+ * not an image type the routes accept, so a caller can refuse locally.
61
+ */
62
+ const UPLOAD_IMAGE_TYPES = {
63
+ '.png': 'image/png',
64
+ '.jpg': 'image/jpeg',
65
+ '.jpeg': 'image/jpeg',
66
+ '.webp': 'image/webp',
67
+ '.gif': 'image/gif',
68
+ };
69
+ function imageUploadMimeType(filePath) {
70
+ const dot = filePath.lastIndexOf('.');
71
+ return dot < 0 ? undefined : UPLOAD_IMAGE_TYPES[filePath.slice(dot).toLowerCase()];
72
+ }
73
+ /** `image:path/to.png@s3` — the `@` is the LAST one, so a path may contain `@`. */
74
+ function parseImage(rest, flag) {
75
+ const at = rest.lastIndexOf('@');
76
+ if (at <= 0 || at === rest.length - 1) {
77
+ throw new Error(`${flag}: "image:" needs a storage source — write image:<filepath>@<source>, ` +
78
+ 'for example image:images/123/face.png@s3.');
79
+ }
80
+ return { filepath: rest.slice(0, at), source: rest.slice(at + 1) };
81
+ }
82
+ /**
83
+ * A JSON object escape hatch, so a shape this grammar has not learned yet is
84
+ * still expressible without falling back to `--from-json` for the whole agent.
85
+ * Returns `undefined` when the value is not JSON at all, so the caller can go on
86
+ * to the shorthand rather than reporting a JSON syntax error for `preset:foo`.
87
+ */
88
+ function parseJsonObject(value) {
89
+ const trimmed = value.trim();
90
+ if (!trimmed.startsWith('{'))
91
+ return undefined;
92
+ try {
93
+ const parsed = JSON.parse(trimmed);
94
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : undefined;
95
+ }
96
+ catch {
97
+ return undefined;
98
+ }
99
+ }
100
+ /**
101
+ * Hold a JSON value to the same rules as the shorthand, for the shapes the
102
+ * grammar knows.
103
+ *
104
+ * The JSON form used to be returned unchecked, so the one form with no worked
105
+ * example was also the one with no local validation: `{"type":"preset"}` went to
106
+ * the server and came back as a raw zod dump
107
+ * (`sceneBackground.id: Invalid input: expected string, received undefined`),
108
+ * while `preset:` with no id got a sentence. A KNOWN `type` is now validated here
109
+ * exactly as its shorthand is. An unknown `type` still passes through untouched,
110
+ * because letting a shape this grammar has not learned reach the server is the
111
+ * reason the escape hatch exists.
112
+ */
113
+ function checkJsonRef(json, flag, shorthand) {
114
+ const type = json.type;
115
+ const str = (value) => (typeof value === 'string' ? value : '');
116
+ if (type === 'default')
117
+ return;
118
+ if (type === 'preset' || type === 'asset') {
119
+ if (!str(json.id)) {
120
+ throw new Error(`${flag}: a JSON value of type "${type}" needs a string "id" — for example ` +
121
+ `{"type":"${type}","id":"<id>"}, or the shorthand ${type}:<id>.`);
122
+ }
123
+ shorthand(`${type}:${str(json.id)}`);
124
+ return;
125
+ }
126
+ if (type === 'image') {
127
+ if (!str(json.filepath) || !str(json.source)) {
128
+ throw new Error(`${flag}: a JSON value of type "image" needs string "filepath" and "source" — ` +
129
+ 'for example {"type":"image","filepath":"<storage path>","source":"s3"}.');
130
+ }
131
+ return;
132
+ }
133
+ if (typeof type !== 'string' || !type) {
134
+ throw new Error(`${flag}: a JSON value needs a string "type" (default, preset, image, …).`);
135
+ }
136
+ }
137
+ /** Preset ids this build ships, for an error message that can be acted on. */
138
+ function avatarPresetIds() {
139
+ return data_provider_1.AGENT_AVATAR_PRESETS.map((preset) => preset.id);
140
+ }
141
+ function sceneBackgroundPresetIds() {
142
+ return data_provider_1.AGENT_SCENE_BACKGROUND_PRESETS.map((preset) => preset.id);
143
+ }
144
+ /**
145
+ * Name a handful of valid ids rather than all of them.
146
+ *
147
+ * There are ~60 avatar presets. An error that prints every one buries the
148
+ * sentence that says what went wrong, so show a few and point at the command
149
+ * that lists the rest.
150
+ */
151
+ function someOf(ids, limit = 6) {
152
+ const head = ids.slice(0, limit).join(', ');
153
+ return ids.length > limit ? `${head}, … (${ids.length} total)` : head;
154
+ }
155
+ function parseAvatarRefFlag(value) {
156
+ const json = parseJsonObject(value);
157
+ if (json) {
158
+ checkJsonRef(json, '--avatar', parseAvatarRefFlag);
159
+ return json;
160
+ }
161
+ const raw = value.trim();
162
+ if (raw === 'default')
163
+ return { type: 'default' };
164
+ const colon = raw.indexOf(':');
165
+ const kind = colon === -1 ? raw : raw.slice(0, colon);
166
+ const rest = colon === -1 ? '' : raw.slice(colon + 1);
167
+ if (kind === 'preset') {
168
+ if (!(0, data_provider_1.isAgentAvatarPresetId)(rest)) {
169
+ throw new Error(`--avatar: "${rest}" is not an avatar preset in this build. ` +
170
+ `Try one of: ${someOf(avatarPresetIds())}. ` +
171
+ 'Run `skrr agents avatar presets` for the full catalog.');
172
+ }
173
+ return { type: 'preset', id: rest };
174
+ }
175
+ if (kind === 'asset') {
176
+ if (!rest) {
177
+ throw new Error('--avatar: "asset:" needs an id from your library — ' +
178
+ 'run `skrr agents avatar library` to list them.');
179
+ }
180
+ return { type: 'asset', id: rest };
181
+ }
182
+ if (kind === 'image')
183
+ return { type: 'image', ...parseImage(rest, '--avatar') };
184
+ throw new Error(`--avatar: cannot read "${value}". Accepted: default, preset:<id>, asset:<id>, ` +
185
+ 'image:<filepath>@<source>, or a JSON object. ' +
186
+ 'To upload a new image file instead, use `skrr agents avatar upload --file <path>`.');
187
+ }
188
+ function parseSceneBackgroundFlag(value) {
189
+ const json = parseJsonObject(value);
190
+ if (json) {
191
+ checkJsonRef(json, '--scene-background', parseSceneBackgroundFlag);
192
+ return json;
193
+ }
194
+ const raw = value.trim();
195
+ if (raw === 'default')
196
+ return { type: 'default' };
197
+ const colon = raw.indexOf(':');
198
+ const kind = colon === -1 ? raw : raw.slice(0, colon);
199
+ const rest = colon === -1 ? '' : raw.slice(colon + 1);
200
+ if (kind === 'preset') {
201
+ if (!(0, data_provider_1.isAgentSceneBackgroundPresetId)(rest)) {
202
+ throw new Error(`--scene-background: "${rest}" is not a backdrop preset in this build. ` +
203
+ `Valid ids: ${sceneBackgroundPresetIds().join(', ')}.`);
204
+ }
205
+ return { type: 'preset', id: rest };
206
+ }
207
+ if (kind === 'image')
208
+ return { type: 'image', ...parseImage(rest, '--scene-background') };
209
+ // A bare preset id is the single most likely mistake here, because the
210
+ // backdrop catalog is short enough to remember. Say so instead of listing the
211
+ // grammar twice.
212
+ if ((0, data_provider_1.isAgentSceneBackgroundPresetId)(raw)) {
213
+ throw new Error(`--scene-background: "${raw}" is a preset id — write preset:${raw}. ` +
214
+ 'A bare id is refused because preset and image live in different namespaces.');
215
+ }
216
+ throw new Error(`--scene-background: cannot read "${value}". Accepted: default, preset:<id>, ` +
217
+ `image:<filepath>@<source>, or a JSON object. Presets: ${sceneBackgroundPresetIds().join(', ')}.`);
218
+ }
219
+ /**
220
+ * The avatar line for `agents show` text output.
221
+ *
222
+ * `agents show` printed no avatar at all, so its output was byte-identical before
223
+ * and after `agents update --avatar preset:studio-halo`: "has the Halo preset"
224
+ * and "has no face whatsoever" could not be told apart without `--json` and
225
+ * knowing which raw field to decode. The coercion `--avatar` performs is the one
226
+ * thing a reader would want confirmed, and it was the one thing hidden.
227
+ *
228
+ * Rendered in the same shorthand `--avatar` accepts, so what is printed is what
229
+ * can be typed back. An ABSENT avatar is kept apart from an explicit
230
+ * `{type:'default'}`: both show the platform logo, but only the absent one was
231
+ * never chosen, and that is exactly what `agents avatar default apply` rewrites.
232
+ */
233
+ function agentAvatarLine(raw) {
234
+ if (raw == null) {
235
+ return 'none chosen — shows the platform logo (`agents avatar default apply` would fill it)';
236
+ }
237
+ const ref = raw;
238
+ switch (ref.type) {
239
+ case 'default':
240
+ return 'default — the platform logo, chosen';
241
+ case 'preset': {
242
+ const id = typeof ref.id === 'string' ? ref.id : '';
243
+ const preset = (0, data_provider_1.getAgentAvatarPreset)(id);
244
+ return preset ? `preset:${id} (${preset.name})` : `preset:${id} (not in this build)`;
245
+ }
246
+ case 'asset':
247
+ return `asset:${typeof ref.id === 'string' ? ref.id : '?'} (from your avatar library)`;
248
+ case 'image':
249
+ return 'an uploaded image';
250
+ default:
251
+ return 'unrecognised value — see `--json`';
252
+ }
253
+ }
254
+ /** The scene-background line, in the shorthand `--scene-background` accepts. */
255
+ function agentSceneBackgroundLine(raw) {
256
+ const ref = (raw ?? { type: 'default' });
257
+ if (ref.type === 'default')
258
+ return 'default';
259
+ if (ref.type === 'preset') {
260
+ const id = typeof ref.id === 'string' ? ref.id : '';
261
+ const preset = data_provider_1.AGENT_SCENE_BACKGROUND_PRESETS.find((candidate) => candidate.id === id);
262
+ return preset ? `preset:${id} (${preset.name})` : `preset:${id} (not in this build)`;
263
+ }
264
+ if (ref.type === 'image')
265
+ return 'an uploaded image';
266
+ return 'unrecognised value — see `--json`';
267
+ }
@@ -1,3 +1,9 @@
1
+ /**
2
+ * The answer a question gets when no terminal exists to ask it on. Written to the
3
+ * agent, so it says what happened and what to do rather than pretending a person
4
+ * replied.
5
+ */
6
+ export declare const NON_INTERACTIVE_QUESTION_ANSWER: string;
1
7
  export type AgenticStreamEvent = {
2
8
  type: string;
3
9
  sessionId?: string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AgenticStreamClient = exports.USER_CONNECTION_CAP_CODE = void 0;
3
+ exports.AgenticStreamClient = exports.USER_CONNECTION_CAP_CODE = exports.NON_INTERACTIVE_QUESTION_ANSWER = void 0;
4
4
  exports.writeJsonLine = writeJsonLine;
5
5
  exports.envelopesText = envelopesText;
6
6
  exports.envelopeKey = envelopeKey;
@@ -19,6 +19,13 @@ const socket_io_client_1 = require("socket.io-client");
19
19
  const data_provider_1 = require("@skrr-ai/data-provider");
20
20
  const config_1 = require("./config");
21
21
  const publicEndpoints_generated_1 = require("./publicEndpoints.generated");
22
+ /**
23
+ * The answer a question gets when no terminal exists to ask it on. Written to the
24
+ * agent, so it says what happened and what to do rather than pretending a person
25
+ * replied.
26
+ */
27
+ exports.NON_INTERACTIVE_QUESTION_ANSWER = 'No answer: this turn was started non-interactively (no terminal), so nobody can reply to ' +
28
+ 'questions. Proceed with your best judgement, or finish by stating exactly what you need.';
22
29
  /**
23
30
  * Write exactly one complete JSON value on one line.
24
31
  *
@@ -667,10 +674,25 @@ class AgenticStreamClient {
667
674
  async handleQuestion(event) {
668
675
  if (this.closed)
669
676
  return;
677
+ // The same question can arrive twice (both delivery paths carry it — a
678
+ // non-interactive run showed two `question.requested` for one questionId,
679
+ // each answered). Answer it once, as tool events are emitted once.
680
+ if (!this.firstTimeSeen('question', event.questionId))
681
+ return;
670
682
  this.emit({ ...event, type: 'question.requested' });
683
+ // Nobody can answer a question in a run with no terminal. This used to
684
+ // prompt on stdin anyway: a scripted `agents chat --jsonl` printed the
685
+ // question and a `>` to stderr, read nothing, and the turn sat for the
686
+ // server's full 300-second question timeout — then ended with the agent told
687
+ // "Not answered … timed out", at the cost of the whole wait. Answer at once
688
+ // instead, saying why, so the agent proceeds or states what it needs. An
689
+ // explicit `input` (tests, an embedding caller) is treated as interactive.
690
+ const interactive = this.options.input !== undefined || Boolean(process.stdin.isTTY);
671
691
  const answers = {};
672
692
  for (const question of event.questions ?? []) {
673
- const answer = await this.ask(`${question.header ?? 'Question'}: ${question.question}\n> `);
693
+ const answer = interactive
694
+ ? await this.ask(`${question.header ?? 'Question'}: ${question.question}\n> `)
695
+ : exports.NON_INTERACTIVE_QUESTION_ANSWER;
674
696
  answers[question.header ?? question.question] = answer;
675
697
  }
676
698
  if (event.questionId && this.socket) {
@@ -680,7 +702,12 @@ class AgenticStreamClient {
680
702
  answers,
681
703
  });
682
704
  }
683
- this.emit({ type: 'question.answered', questionId: event.questionId, answers });
705
+ this.emit({
706
+ type: 'question.answered',
707
+ questionId: event.questionId,
708
+ answers,
709
+ ...(interactive ? {} : { nonInteractive: true }),
710
+ });
684
711
  }
685
712
  ask(prompt) {
686
713
  // A prompt raised after close has nobody to answer it, and re-creating the
@@ -773,6 +800,10 @@ class AgenticStreamClient {
773
800
  output.write(`\n[${event.type}]\n`);
774
801
  this.renderedText = this.accumulated;
775
802
  }
803
+ else if (event.type === 'question.answered' && event.nonInteractive) {
804
+ output.write('\n[question] no terminal to answer on, so the agent was told to proceed without an answer\n');
805
+ this.renderedText = this.accumulated;
806
+ }
776
807
  }
777
808
  finish(status, event) {
778
809
  if (this.settled)
@@ -33,7 +33,7 @@ export type ResolutionFailure = {
33
33
  * NO_SPACE — the reference needs a membership list to match against, but
34
34
  * the task/payload has no space, so only raw ids are possible.
35
35
  */
36
- code: 'NOT_FOUND' | 'AMBIGUOUS' | 'NO_SPACE';
36
+ code: 'NOT_FOUND' | 'AMBIGUOUS' | 'NO_SPACE' | 'NOT_A_REFERENCE';
37
37
  query: string;
38
38
  kind: MemberKind;
39
39
  /** For AMBIGUOUS: the tied matches. For NOT_FOUND: everything available. */
@@ -7,6 +7,7 @@ exports.describeMember = describeMember;
7
7
  exports.describeCandidates = describeCandidates;
8
8
  exports.resolveAssigneeReferences = resolveAssigneeReferences;
9
9
  exports.formatResolutionFailure = formatResolutionFailure;
10
+ const failed_lookup_1 = require("./failed-lookup");
10
11
  const data_provider_1 = require("@skrr-ai/data-provider");
11
12
  const OBJECT_ID_RE = /^[0-9a-fA-F]{24}$/;
12
13
  const UUID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
@@ -84,6 +85,11 @@ function resolveMember(query, members, kind) {
84
85
  if (!q) {
85
86
  return { ok: false, code: 'NOT_FOUND', query: raw, kind, candidates: pool };
86
87
  }
88
+ // A failed lookup's output is refused before the name tiers can match it.
89
+ // See `failed-lookup.ts`.
90
+ if ((0, failed_lookup_1.isFailedLookupValue)(raw)) {
91
+ return { ok: false, code: 'NOT_A_REFERENCE', query: raw, kind, candidates: [] };
92
+ }
87
93
  const tiers = [
88
94
  // 1. Exact id. Case-sensitive on purpose — ids are opaque.
89
95
  (m) => m.id === raw,
@@ -186,6 +192,8 @@ function formatResolutionFailure(failure, bin = 'skrr') {
186
192
  const noun = failure.kind === 'user' ? 'person' : 'agent';
187
193
  const plural = failure.kind === 'user' ? 'people' : 'agents';
188
194
  switch (failure.code) {
195
+ case 'NOT_A_REFERENCE':
196
+ return (0, failed_lookup_1.describeFailedLookupValue)(failure.query, noun);
189
197
  case 'AMBIGUOUS':
190
198
  return (`"${failure.query}" matches ${failure.candidates.length} ${plural} in this space:\n` +
191
199
  `${describeCandidates(failure.candidates)}\n` +
@@ -29,6 +29,18 @@ export type BulkApplyResult = {
29
29
  * semantics match `skrr tasks bulk-update`). `apply` returns whether the task
30
30
  * actually changed (already-satisfied tasks are reported as no-ops).
31
31
  */
32
+ /**
33
+ * The server's reason for a failed per-task write, not the transport's.
34
+ *
35
+ * A bulk run used to record `err.message`, which for a dataService call is the
36
+ * transport line — `HTTP 400 Bad Request — PATCH https://…/api/tasks/<id>` — so
37
+ * `tasks actions attach --all-in-space` printed that once per task and named
38
+ * neither the ref nor the rule that refused it (OSK-9652). The same call made
39
+ * for a single task goes through `handleApiError`, which reads the body. This
40
+ * reads it the same way: `message`, then `error`, plus the first validation
41
+ * detail when the server sent one.
42
+ */
43
+ export declare function bulkFailureMessage(err: unknown): string;
32
44
  export declare function applyToTaskTargets(targets: Array<{
33
45
  id: string;
34
46
  identifier?: string;