ptbk 0.119.0-16 → 0.119.0-21

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 (330) hide show
  1. package/README.md +27 -9
  2. package/dist/book/commitments.d.ts +38 -10
  3. package/dist/book/commitments.js +61 -17
  4. package/dist/book/commitments.js.map +1 -1
  5. package/dist/book/convert.d.ts +36 -0
  6. package/dist/book/convert.js +156 -0
  7. package/dist/book/convert.js.map +1 -0
  8. package/dist/book/parse.d.ts +50 -13
  9. package/dist/book/parse.js +132 -45
  10. package/dist/book/parse.js.map +1 -1
  11. package/dist/book/references.d.ts +12 -4
  12. package/dist/book/references.js +34 -9
  13. package/dist/book/references.js.map +1 -1
  14. package/dist/book/write.d.ts +42 -0
  15. package/dist/book/write.js +74 -0
  16. package/dist/book/write.js.map +1 -0
  17. package/dist/browser/browser.d.ts +122 -12
  18. package/dist/browser/browser.js +372 -21
  19. package/dist/browser/browser.js.map +1 -1
  20. package/dist/browser/recording.d.ts +3 -3
  21. package/dist/browser/recording.js +5 -5
  22. package/dist/browser/recording.js.map +1 -1
  23. package/dist/chats/chat-book.d.ts +105 -0
  24. package/dist/chats/chat-book.js +180 -0
  25. package/dist/chats/chat-book.js.map +1 -0
  26. package/dist/chats/conversation.d.ts +54 -0
  27. package/dist/chats/conversation.js +188 -0
  28. package/dist/chats/conversation.js.map +1 -0
  29. package/dist/chats/local.d.ts +46 -0
  30. package/dist/chats/local.js +184 -0
  31. package/dist/chats/local.js.map +1 -0
  32. package/dist/chats/migration.d.ts +12 -0
  33. package/dist/chats/migration.js +62 -0
  34. package/dist/chats/migration.js.map +1 -0
  35. package/dist/chats/service.d.ts +81 -0
  36. package/dist/chats/service.js +122 -0
  37. package/dist/chats/service.js.map +1 -0
  38. package/dist/chats/store.d.ts +132 -0
  39. package/dist/chats/store.js +181 -0
  40. package/dist/chats/store.js.map +1 -0
  41. package/dist/chats/task-chats.d.ts +52 -0
  42. package/dist/chats/task-chats.js +96 -0
  43. package/dist/chats/task-chats.js.map +1 -0
  44. package/dist/cli/chats.d.ts +45 -0
  45. package/dist/cli/chats.js +108 -0
  46. package/dist/cli/chats.js.map +1 -0
  47. package/dist/cli/commands/add.js +32 -22
  48. package/dist/cli/commands/add.js.map +1 -1
  49. package/dist/cli/commands/answer.d.ts +2 -1
  50. package/dist/cli/commands/answer.js +35 -41
  51. package/dist/cli/commands/answer.js.map +1 -1
  52. package/dist/cli/commands/ask.d.ts +7 -0
  53. package/dist/cli/commands/ask.js +37 -0
  54. package/dist/cli/commands/ask.js.map +1 -0
  55. package/dist/cli/commands/boilerplates.js +14 -3
  56. package/dist/cli/commands/boilerplates.js.map +1 -1
  57. package/dist/cli/commands/chat.d.ts +9 -0
  58. package/dist/cli/commands/chat.js +89 -0
  59. package/dist/cli/commands/chat.js.map +1 -0
  60. package/dist/cli/commands/dashboard.d.ts +3 -1
  61. package/dist/cli/commands/dashboard.js +41 -3
  62. package/dist/cli/commands/dashboard.js.map +1 -1
  63. package/dist/cli/commands/find.js +6 -8
  64. package/dist/cli/commands/find.js.map +1 -1
  65. package/dist/cli/commands/fix.js +4 -6
  66. package/dist/cli/commands/fix.js.map +1 -1
  67. package/dist/cli/commands/init.d.ts +0 -1
  68. package/dist/cli/commands/init.js +38 -38
  69. package/dist/cli/commands/init.js.map +1 -1
  70. package/dist/cli/commands/lint.js +16 -2
  71. package/dist/cli/commands/lint.js.map +1 -1
  72. package/dist/cli/commands/list.js +21 -13
  73. package/dist/cli/commands/list.js.map +1 -1
  74. package/dist/cli/commands/migrate.d.ts +2 -1
  75. package/dist/cli/commands/migrate.js +70 -24
  76. package/dist/cli/commands/migrate.js.map +1 -1
  77. package/dist/cli/commands/pause.d.ts +8 -0
  78. package/dist/cli/commands/pause.js +40 -0
  79. package/dist/cli/commands/pause.js.map +1 -0
  80. package/dist/cli/commands/plan.js +6 -9
  81. package/dist/cli/commands/plan.js.map +1 -1
  82. package/dist/cli/commands/recover.js +7 -3
  83. package/dist/cli/commands/recover.js.map +1 -1
  84. package/dist/cli/commands/restart.d.ts +3 -1
  85. package/dist/cli/commands/restart.js +74 -18
  86. package/dist/cli/commands/restart.js.map +1 -1
  87. package/dist/cli/commands/resume.d.ts +8 -0
  88. package/dist/cli/commands/resume.js +15 -0
  89. package/dist/cli/commands/resume.js.map +1 -0
  90. package/dist/cli/commands/run.js +6 -9
  91. package/dist/cli/commands/run.js.map +1 -1
  92. package/dist/cli/commands/start.js +40 -36
  93. package/dist/cli/commands/start.js.map +1 -1
  94. package/dist/cli/commands/status.d.ts +2 -1
  95. package/dist/cli/commands/status.js +5 -1
  96. package/dist/cli/commands/status.js.map +1 -1
  97. package/dist/cli/commands/stop.d.ts +3 -1
  98. package/dist/cli/commands/stop.js +18 -8
  99. package/dist/cli/commands/stop.js.map +1 -1
  100. package/dist/cli/commands/verify.js +7 -7
  101. package/dist/cli/commands/verify.js.map +1 -1
  102. package/dist/cli/common.d.ts +13 -0
  103. package/dist/cli/common.js +22 -0
  104. package/dist/cli/common.js.map +1 -1
  105. package/dist/cli/engine-delegation.d.ts +14 -0
  106. package/dist/cli/engine-delegation.js +46 -0
  107. package/dist/cli/engine-delegation.js.map +1 -0
  108. package/dist/cli/lifecycle.d.ts +40 -0
  109. package/dist/cli/lifecycle.js +71 -0
  110. package/dist/cli/lifecycle.js.map +1 -0
  111. package/dist/cli/registry.js +113 -31
  112. package/dist/cli/registry.js.map +1 -1
  113. package/dist/cli/service-view.d.ts +14 -2
  114. package/dist/cli/service-view.js +46 -5
  115. package/dist/cli/service-view.js.map +1 -1
  116. package/dist/cli/sources.js +5 -8
  117. package/dist/cli/sources.js.map +1 -1
  118. package/dist/cli/spec.js +1 -1
  119. package/dist/cli/spec.js.map +1 -1
  120. package/dist/common/app-data.d.ts +9 -0
  121. package/dist/common/app-data.js +20 -0
  122. package/dist/common/app-data.js.map +1 -0
  123. package/dist/common/errors.d.ts +2 -0
  124. package/dist/common/errors.js.map +1 -1
  125. package/dist/common/time.d.ts +6 -0
  126. package/dist/common/time.js +9 -0
  127. package/dist/common/time.js.map +1 -1
  128. package/dist/engine/agenda.d.ts +213 -43
  129. package/dist/engine/agenda.js +846 -260
  130. package/dist/engine/agenda.js.map +1 -1
  131. package/dist/engine/executor.d.ts +23 -7
  132. package/dist/engine/executor.js +131 -84
  133. package/dist/engine/executor.js.map +1 -1
  134. package/dist/engine/generated.d.ts +21 -1
  135. package/dist/engine/generated.js +36 -21
  136. package/dist/engine/generated.js.map +1 -1
  137. package/dist/engine/preview.js +10 -10
  138. package/dist/engine/preview.js.map +1 -1
  139. package/dist/engine/prompt.d.ts +16 -4
  140. package/dist/engine/prompt.js +22 -4
  141. package/dist/engine/prompt.js.map +1 -1
  142. package/dist/engine/routing.js +28 -4
  143. package/dist/engine/routing.js.map +1 -1
  144. package/dist/engine/snapshot.d.ts +178 -29
  145. package/dist/engine/state.d.ts +40 -13
  146. package/dist/engine/state.js +20 -5
  147. package/dist/engine/state.js.map +1 -1
  148. package/dist/engine/task-trace.d.ts +35 -7
  149. package/dist/engine/task-trace.js +74 -15
  150. package/dist/engine/task-trace.js.map +1 -1
  151. package/dist/engine/team.d.ts +20 -7
  152. package/dist/engine/team.js +30 -24
  153. package/dist/engine/team.js.map +1 -1
  154. package/dist/engine/traces.d.ts +1 -1
  155. package/dist/engine/traces.js +1 -1
  156. package/dist/expert/_main.md +67 -52
  157. package/dist/expert/agents/_index.md +11 -13
  158. package/dist/expert/agents/chatter.md +31 -0
  159. package/dist/expert/agents/consistency.md +6 -11
  160. package/dist/expert/agents/core-agents.md +9 -7
  161. package/dist/expert/agents/expert.md +2 -2
  162. package/dist/expert/agents/fixer.md +24 -0
  163. package/dist/expert/agents/learning.md +1 -1
  164. package/dist/expert/agents/non-agents.md +6 -4
  165. package/dist/expert/agents/teacher.md +1 -1
  166. package/dist/expert/agents/visual-identity.md +4 -4
  167. package/dist/expert/book-language/_index.md +9 -8
  168. package/dist/expert/book-language/commitments/_index.md +23 -3
  169. package/dist/expert/book-language/commitments/agent.md +11 -0
  170. package/dist/expert/book-language/commitments/chat-member.md +7 -0
  171. package/dist/expert/book-language/commitments/chat-message.md +11 -0
  172. package/dist/expert/book-language/commitments/chat-status.md +7 -0
  173. package/dist/expert/book-language/commitments/chat.md +11 -0
  174. package/dist/expert/book-language/commitments/entity.md +9 -0
  175. package/dist/expert/book-language/commitments/from.md +1 -1
  176. package/dist/expert/book-language/commitments/goal.md +6 -2
  177. package/dist/expert/book-language/commitments/link.md +9 -0
  178. package/dist/expert/book-language/commitments/task-depends-on.md +1 -1
  179. package/dist/expert/book-language/commitments/task-parent.md +1 -1
  180. package/dist/expert/book-language/commitments/task.md +7 -3
  181. package/dist/expert/book-language/commitments/team.md +1 -1
  182. package/dist/expert/book-language/inheritance.md +2 -2
  183. package/dist/expert/book-language/references.md +20 -24
  184. package/dist/expert/book-language/syntax.md +54 -11
  185. package/dist/expert/cli/acceptance.md +59 -24
  186. package/dist/expert/cli/api.md +23 -9
  187. package/dist/expert/cli/authoring.md +6 -4
  188. package/dist/expert/cli/autonomous-operation.md +3 -3
  189. package/dist/expert/cli/browser.md +29 -18
  190. package/dist/expert/cli/channels.md +5 -7
  191. package/dist/expert/cli/chat-ui.md +32 -0
  192. package/dist/expert/cli/chats.md +59 -0
  193. package/dist/expert/cli/cli.md +44 -26
  194. package/dist/expert/cli/configuration.md +3 -3
  195. package/dist/expert/cli/controls.md +13 -7
  196. package/dist/expert/cli/dashboard-details.md +23 -0
  197. package/dist/expert/cli/dashboard-layout.md +19 -12
  198. package/dist/expert/cli/dashboard.md +12 -8
  199. package/dist/expert/cli/execution.md +8 -6
  200. package/dist/expert/cli/first-task.md +4 -4
  201. package/dist/expert/cli/generated-names.md +44 -0
  202. package/dist/expert/cli/git-persistence.md +19 -17
  203. package/dist/expert/cli/global-dashboard.md +33 -0
  204. package/dist/expert/cli/goal-discovery.md +1 -1
  205. package/dist/expert/cli/help.md +1 -1
  206. package/dist/expert/cli/initialization-ui.md +2 -0
  207. package/dist/expert/cli/initialization.md +9 -7
  208. package/dist/expert/cli/interactive-ui.md +9 -5
  209. package/dist/expert/cli/locking.md +21 -0
  210. package/dist/expert/cli/migration.md +22 -6
  211. package/dist/expert/cli/process-modes.md +21 -13
  212. package/dist/expert/cli/recovery.md +8 -6
  213. package/dist/expert/cli/recurrence.md +1 -1
  214. package/dist/expert/cli/rich-input.md +2 -2
  215. package/dist/expert/cli/service-lifecycle.md +57 -0
  216. package/dist/expert/cli/start.md +2 -2
  217. package/dist/expert/cli/status-view.md +16 -12
  218. package/dist/expert/cli/task-books.md +11 -15
  219. package/dist/expert/cli/task-identity.md +8 -10
  220. package/dist/expert/cli/task-markdown.md +6 -10
  221. package/dist/expert/cli/task-origins.md +3 -1
  222. package/dist/expert/cli/task-relationships.md +5 -9
  223. package/dist/expert/cli/team.md +17 -7
  224. package/dist/expert/cli/terminal.md +20 -12
  225. package/dist/expert/cli/trace-configuration.md +1 -1
  226. package/dist/expert/cli/traces.md +19 -23
  227. package/dist/expert/cli/user-interactions.md +14 -26
  228. package/dist/expert/cli/workspace.md +15 -9
  229. package/dist/expert/concepts/engine-and-project.md +11 -0
  230. package/dist/expert/concepts/project-model.md +39 -0
  231. package/dist/expert/dictionary.md +21 -15
  232. package/dist/harness/plugins/claude-code/index.js +1 -0
  233. package/dist/harness/plugins/claude-code/index.js.map +1 -1
  234. package/dist/harness/plugins/cline/index.js +1 -0
  235. package/dist/harness/plugins/cline/index.js.map +1 -1
  236. package/dist/harness/plugins/openai-codex/index.js +1 -0
  237. package/dist/harness/plugins/openai-codex/index.js.map +1 -1
  238. package/dist/harness/plugins/opencode/index.js +1 -0
  239. package/dist/harness/plugins/opencode/index.js.map +1 -1
  240. package/dist/harness/types.d.ts +3 -1
  241. package/dist/project/agents.d.ts +29 -81
  242. package/dist/project/agents.js +48 -267
  243. package/dist/project/agents.js.map +1 -1
  244. package/dist/project/books.d.ts +150 -0
  245. package/dist/project/books.js +253 -0
  246. package/dist/project/books.js.map +1 -0
  247. package/dist/project/config.js +2 -2
  248. package/dist/project/config.js.map +1 -1
  249. package/dist/project/defaults.d.ts +35 -6
  250. package/dist/project/defaults.js +73 -40
  251. package/dist/project/defaults.js.map +1 -1
  252. package/dist/project/expert.js +18 -10
  253. package/dist/project/expert.js.map +1 -1
  254. package/dist/project/git.d.ts +4 -3
  255. package/dist/project/git.js +16 -5
  256. package/dist/project/git.js.map +1 -1
  257. package/dist/project/setup.d.ts +4 -1
  258. package/dist/project/setup.js +26 -6
  259. package/dist/project/setup.js.map +1 -1
  260. package/dist/service/actions.d.ts +34 -10
  261. package/dist/service/actions.js +51 -19
  262. package/dist/service/actions.js.map +1 -1
  263. package/dist/service/control.d.ts +53 -1
  264. package/dist/service/control.js +64 -4
  265. package/dist/service/control.js.map +1 -1
  266. package/dist/service/discovery.d.ts +133 -0
  267. package/dist/service/discovery.js +138 -0
  268. package/dist/service/discovery.js.map +1 -0
  269. package/dist/service/host.d.ts +8 -1
  270. package/dist/service/host.js +65 -18
  271. package/dist/service/host.js.map +1 -1
  272. package/dist/service/http.d.ts +3 -0
  273. package/dist/service/http.js +26 -6
  274. package/dist/service/http.js.map +1 -1
  275. package/dist/service/launcher.d.ts +66 -13
  276. package/dist/service/launcher.js +78 -18
  277. package/dist/service/launcher.js.map +1 -1
  278. package/dist/service/records.d.ts +12 -2
  279. package/dist/service/records.js +19 -5
  280. package/dist/service/records.js.map +1 -1
  281. package/dist/service/spa/app.css +83 -2
  282. package/dist/service/spa/app.js +393 -72
  283. package/dist/service/supervisor.js +7 -1
  284. package/dist/service/supervisor.js.map +1 -1
  285. package/dist/service/worker.js +14 -5
  286. package/dist/service/worker.js.map +1 -1
  287. package/dist/tasks/authoring.d.ts +3 -5
  288. package/dist/tasks/authoring.js +6 -7
  289. package/dist/tasks/authoring.js.map +1 -1
  290. package/dist/tasks/book-task.d.ts +18 -10
  291. package/dist/tasks/book-task.js +27 -23
  292. package/dist/tasks/book-task.js.map +1 -1
  293. package/dist/tasks/eligibility.d.ts +15 -7
  294. package/dist/tasks/eligibility.js +41 -14
  295. package/dist/tasks/eligibility.js.map +1 -1
  296. package/dist/tasks/identity.d.ts +41 -15
  297. package/dist/tasks/identity.js +100 -31
  298. package/dist/tasks/identity.js.map +1 -1
  299. package/dist/tasks/markdown.d.ts +7 -14
  300. package/dist/tasks/markdown.js +15 -29
  301. package/dist/tasks/markdown.js.map +1 -1
  302. package/dist/tasks/model.d.ts +20 -4
  303. package/dist/tasks/model.js +17 -0
  304. package/dist/tasks/model.js.map +1 -1
  305. package/dist/tasks/sources.d.ts +7 -2
  306. package/dist/tasks/sources.js +46 -35
  307. package/dist/tasks/sources.js.map +1 -1
  308. package/dist/ui/chat-client.d.ts +70 -0
  309. package/dist/ui/chat-client.js +209 -0
  310. package/dist/ui/chat-client.js.map +1 -0
  311. package/dist/ui/dashboard.d.ts +8 -5
  312. package/dist/ui/dashboard.js +227 -86
  313. package/dist/ui/dashboard.js.map +1 -1
  314. package/dist/ui/details.d.ts +64 -0
  315. package/dist/ui/details.js +175 -0
  316. package/dist/ui/details.js.map +1 -0
  317. package/dist/ui/global-dashboard.d.ts +59 -0
  318. package/dist/ui/global-dashboard.js +307 -0
  319. package/dist/ui/global-dashboard.js.map +1 -0
  320. package/dist/ui/status-view.d.ts +7 -0
  321. package/dist/ui/status-view.js +94 -36
  322. package/dist/ui/status-view.js.map +1 -1
  323. package/package.json +1 -1
  324. package/dist/engine/requests.d.ts +0 -111
  325. package/dist/engine/requests.js +0 -173
  326. package/dist/engine/requests.js.map +0 -1
  327. package/dist/expert/book-language/commitments/task-id.md +0 -9
  328. package/dist/tasks/prepare.d.ts +0 -33
  329. package/dist/tasks/prepare.js +0 -90
  330. package/dist/tasks/prepare.js.map +0 -1
@@ -0,0 +1,122 @@
1
+ import { PtbkError } from '../common/errors.js';
2
+ import { commitmentsOf } from '../book/parse.js';
3
+ import { formatAgentReference, normalizeAgentName } from '../book/references.js';
4
+ import { CHATTER_NAME } from '../project/defaults.js';
5
+ import { isHumanFacing } from './chat-book.js';
6
+ /**
7
+ * Agents the user may chat with: Chatter first (the default partner of a new chat, when it is a valid agent), then
8
+ * every alternative: visible ordinary agents, hidden/core agents and the engine-provided Expert. User, Void and Null
9
+ * are never offered.
10
+ *
11
+ * @private chats
12
+ */
13
+ export function chatAgentChoices(catalog) {
14
+ const chatterKey = normalizeAgentName(CHATTER_NAME);
15
+ const ordinary = catalog.ordinary().filter((entry) => entry.normalized !== chatterKey);
16
+ const ordinaryKeys = new Set(ordinary.map((agent) => agent.normalized));
17
+ const winners = catalog.index.winners('agent').filter((entry) => entry.kind === 'agent' && entry.tier !== 4);
18
+ const chatter = winners.find((entry) => entry.normalized === chatterKey);
19
+ const hidden = winners.filter((entry) => !ordinaryKeys.has(entry.normalized) && entry.normalized !== chatterKey);
20
+ const choice = (entry, group) => ({
21
+ name: entry.name,
22
+ reference: formatAgentReference(entry.name),
23
+ group,
24
+ description: commitmentsOf(entry.document, 'GOAL')[0]?.value.split('\n')[0]?.slice(0, 100),
25
+ });
26
+ return [
27
+ ...(chatter ? [choice(chatter, 'chatter')] : []),
28
+ ...ordinary.map((entry) => choice(entry, 'ordinary')),
29
+ ...hidden.map((entry) => choice(entry, 'hidden')),
30
+ // The built-in Expert always wins its name, so it is always available.
31
+ { name: 'Expert', reference: '@Expert', group: 'expert', description: 'Engine-provided Promptbook expert (current syntax and capabilities).' },
32
+ ];
33
+ }
34
+ /**
35
+ * The default partner of a new chat (Chatter), or an actionable diagnostic when it is missing or invalid. Another
36
+ * agent is never substituted silently, and nothing is created or initialized here.
37
+ *
38
+ * @private chats
39
+ */
40
+ export function defaultChatAgent(choices) {
41
+ const chatter = choices.find((choice) => choice.group === 'chatter');
42
+ if (chatter)
43
+ return chatter;
44
+ throw new PtbkError(`${CHATTER_NAME}, the default partner of new chats, is missing or invalid in this project.`, {
45
+ kind: 'setup',
46
+ reason: `No valid \`AGENT ${CHATTER_NAME}\` Book was found (normally agents/.core/chatter.book).`,
47
+ effect: 'No chat was started; no other agent was chosen in its place.',
48
+ next: ['Run `ptbk init` (or `ptbk start`) to add the missing core agent, or fix its Book (`ptbk lint`).', 'Or choose an agent explicitly, e.g. `ptbk chat --agent Developer`.'],
49
+ });
50
+ }
51
+ /**
52
+ * Reads one chat with its messages.
53
+ *
54
+ * @private chats
55
+ */
56
+ export async function chatDetail(store, name) {
57
+ const stored = await store.find(name);
58
+ if (!stored)
59
+ throw new PtbkError(`No chat named "${name}".`, { kind: 'not-found', next: ['List chats with `ptbk chat --list`.'] });
60
+ const summary = (await store.summaries()).find((item) => item.path === stored.relativePath);
61
+ return { summary, messages: stored.chat.messages.map((message) => ({ sender: message.sender, at: message.at, text: message.text, choices: message.choices })) };
62
+ }
63
+ /**
64
+ * Starts a new user-led chat with exactly one agent (its first message is committed).
65
+ *
66
+ * @private chats
67
+ */
68
+ export async function createUserChat(store, catalog, agent, text, options = {}) {
69
+ const reference = agent.startsWith('@') || agent.startsWith('{') ? agent : formatAgentReference(agent);
70
+ const resolution = await catalog.resolve(reference);
71
+ if (resolution.kind === 'non-agent' && resolution.name !== 'Expert')
72
+ throw new PtbkError(`You cannot chat with ${resolution.name}; choose a project agent or @Expert.`, { kind: 'usage' });
73
+ if (resolution.kind !== 'agent' && resolution.kind !== 'non-agent') {
74
+ throw new PtbkError(`Cannot chat with ${agent}: ${resolution.kind === 'wrong-kind' ? `${resolution.entry.relativePath} is a ${resolution.entry.kind}, not an agent` : `no such agent (${resolution.kind})`}.`, { kind: 'usage', next: ['Run `ptbk chat` without --agent to choose one.'] });
75
+ }
76
+ const name = resolution.kind === 'agent' ? resolution.definition.name : 'Expert';
77
+ const first = text.split('\n').find((line) => line.trim())?.trim();
78
+ if (!first)
79
+ throw new PtbkError('The first message of a chat cannot be empty.', { kind: 'usage' });
80
+ return await store.create({
81
+ name: options.name?.trim() || `${name}: ${first.slice(0, 60)}`,
82
+ members: ['@User', formatAgentReference(name)],
83
+ status: 'open',
84
+ messages: [{ sender: '@User', text }],
85
+ taken: catalog.index.names(),
86
+ });
87
+ }
88
+ /**
89
+ * Appends the user's message to a chat (one commit).
90
+ *
91
+ * In a chat waiting for the user, a reply matching the offered choices (or any reply when none were offered) answers
92
+ * it, reopens the chat and lets the waiting task resume; any other reply is a clarifying question for the agent.
93
+ * A failure-recovery decision accepts only one of its offered choices.
94
+ *
95
+ * @private chats
96
+ */
97
+ export async function appendUserMessage(store, state, name, text, options = {}) {
98
+ const stored = await store.find(name);
99
+ if (!stored)
100
+ throw new PtbkError(`No chat named "${name}".`, { kind: 'not-found', next: ['List chats with `ptbk chat --list`.'] });
101
+ if (!isHumanFacing(stored.chat))
102
+ throw new PtbkError('Agent↔agent consultation chats are task records; you cannot write into them.', { kind: 'usage' });
103
+ const waiting = stored.chat.status === 'waiting-for-user';
104
+ const awaited = waiting ? [...stored.chat.messages].reverse().find((message) => message.senderKey !== 'user') : undefined;
105
+ const applicable = !awaited?.choices?.length || awaited.choices.includes(text.trim());
106
+ const active = await state.readActive();
107
+ const records = [...(await state.readParked()), ...(active ? [active] : [])];
108
+ const waitingTask = records.find((record) => record.waitingChat === stored.chat.name || record.recoveryChat === stored.chat.name);
109
+ if (waitingTask?.recoveryChat === stored.chat.name && waiting && !applicable) {
110
+ throw new PtbkError(`"${text}" is not one of the offered answers.`, { kind: 'usage', reason: `Choose one of: ${awaited.choices.join(' | ')}`, effect: 'Nothing was saved.' });
111
+ }
112
+ const answered = waiting && applicable;
113
+ const written = await store.append(stored.chat.name, { sender: '@User', text: answered && awaited?.choices?.length ? text.trim() : text }, {
114
+ // Answering reopens the chat; writing into a resolved chat continues the conversation.
115
+ status: answered || stored.chat.status === 'resolved' ? 'open' : undefined,
116
+ expectedMessages: options.expected,
117
+ trailers: waitingTask ? { 'Task-Name': waitingTask.taskTitle } : undefined,
118
+ });
119
+ await store.markRead(written.chat.name);
120
+ return { written, waitingTask, answered, needsReply: !(waitingTask && answered) };
121
+ }
122
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/chats/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,132 @@
1
+ import type { Project } from '../project/workspace.js';
2
+ import { type ChatBook, type ChatMessage, type ChatStatus } from './chat-book.js';
3
+ /**
4
+ * How chat operations are versioned: each human-facing operation is its own commit unless explicitly uncommitted.
5
+ *
6
+ * @private chats
7
+ */
8
+ export type ChatPersistence = {
9
+ /** Commits exactly these paths; returns the commit (undefined when nothing was committed). Absent in no-commit mode. */
10
+ readonly commit?: (paths: readonly string[], subject: string, trailers: Readonly<Record<string, string | readonly string[] | undefined>>) => Promise<string | undefined>;
11
+ /** Records a written path as owned but uncommitted (explicit no-commit mode). */
12
+ readonly uncommitted?: (path: string) => Promise<void>;
13
+ };
14
+ /**
15
+ * A stored chat with its location.
16
+ *
17
+ * @private chats
18
+ */
19
+ export type StoredChat = {
20
+ readonly path: string;
21
+ readonly relativePath: string;
22
+ readonly chat: ChatBook;
23
+ };
24
+ /**
25
+ * Summary of a chat for status views, dashboards and the API.
26
+ *
27
+ * @private chats
28
+ */
29
+ export type ChatSummary = {
30
+ readonly name: string;
31
+ readonly path: string;
32
+ readonly members: readonly string[];
33
+ /** The agent participant (human-facing chats). */
34
+ readonly agent?: string;
35
+ readonly status: ChatStatus;
36
+ readonly humanFacing: boolean;
37
+ readonly links: readonly string[];
38
+ readonly messages: number;
39
+ readonly last?: {
40
+ readonly sender: string;
41
+ readonly at: string;
42
+ readonly preview: string;
43
+ };
44
+ /** The agent's question awaiting the user (when waiting-for-user). */
45
+ readonly awaiting?: {
46
+ readonly sender: string;
47
+ readonly text: string;
48
+ readonly choices?: readonly string[];
49
+ };
50
+ /** New messages not authored by User since this viewer last opened the chat. */
51
+ readonly unread: number;
52
+ };
53
+ /**
54
+ * Result of a chat write.
55
+ *
56
+ * @private chats
57
+ */
58
+ export type ChatWrite = StoredChat & {
59
+ readonly commit?: string;
60
+ };
61
+ /**
62
+ * Durable chat Books in root-level `chats/`, with per-message persistence and local read markers.
63
+ *
64
+ * @private chats
65
+ */
66
+ export declare class ChatStore {
67
+ private readonly project;
68
+ private readonly options;
69
+ private readonly mutex;
70
+ /** Absolute chats directory. */
71
+ readonly directory: string;
72
+ constructor(project: Project, options: {
73
+ readonly timeZone: string;
74
+ readonly persistence?: ChatPersistence;
75
+ /** Fresh normalized names of every project Book, rechecked when a new chat is published. */
76
+ readonly takenNames?: () => Promise<ReadonlySet<string>>;
77
+ });
78
+ /** Current timestamp in the project's time zone. */
79
+ now(): string;
80
+ /** Every chat Book (deterministic order: shallow first, then path). */
81
+ list(): Promise<StoredChat[]>;
82
+ /** Finds a chat by name (`{Name}`, `@Name` or plain text); the first same-name chat wins. */
83
+ find(name: string): Promise<StoredChat | undefined>;
84
+ /**
85
+ * Creates a chat named after its readable title plus creation date/time (distinct under normalization from every
86
+ * Book); a human-facing chat is committed. The final name is allocated once, under the store's lock.
87
+ */
88
+ create(options: {
89
+ readonly name: string;
90
+ readonly members: readonly string[];
91
+ readonly status: ChatStatus;
92
+ readonly links?: readonly string[];
93
+ readonly messages?: ReadonlyArray<Pick<ChatMessage, 'sender' | 'text' | 'choices'> & {
94
+ readonly at?: string;
95
+ }>;
96
+ /** Normalized names already used by other Books. */
97
+ readonly taken?: ReadonlySet<string>;
98
+ /** Extra trailers (e.g. `Task-Name`). */
99
+ readonly trailers?: Readonly<Record<string, string | readonly string[] | undefined>>;
100
+ /**
101
+ * Commit even an agent↔agent chat as its own scoped operation (a consultation serving a conversation without
102
+ * an owning task); task-scoped consultations instead join their task's result commit.
103
+ */
104
+ readonly commit?: boolean;
105
+ }): Promise<ChatWrite>;
106
+ /**
107
+ * Appends one message (optionally changing the status) in a single save and, for human-facing chats, commit.
108
+ *
109
+ * `expectedMessages` rejects a stale client that did not see the latest message (no duplicate answers).
110
+ */
111
+ append(name: string, message: Pick<ChatMessage, 'sender' | 'text' | 'choices'>, options?: {
112
+ readonly status?: ChatStatus;
113
+ readonly expectedMessages?: number;
114
+ readonly trailers?: Readonly<Record<string, string | readonly string[] | undefined>>;
115
+ }): Promise<ChatWrite>;
116
+ /** Changes only the durable status (e.g. resolving a request without a new message). */
117
+ setStatus(name: string, status: ChatStatus): Promise<ChatWrite>;
118
+ /** Commits (or records as uncommitted) one chat operation. */
119
+ private persist;
120
+ /** Local per-viewer read markers (`.ptbk/state/chat-read.json`): chat key → seen message count. */
121
+ readMarkers(): Promise<Record<string, number>>;
122
+ /** Marks a chat as read up to its current messages (runtime-only; never committed, never an answer). */
123
+ markRead(name: string): Promise<void>;
124
+ /** Summaries of every chat, waiting-for-user first, then most recent. */
125
+ summaries(): Promise<ChatSummary[]>;
126
+ }
127
+ /**
128
+ * Builds the summary of one chat.
129
+ *
130
+ * @private chats
131
+ */
132
+ export declare function summarize(chat: ChatBook, relativePath: string, seen: number): ChatSummary;
@@ -0,0 +1,181 @@
1
+ import { readFile, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { PtbkError } from '../common/errors.js';
4
+ import { ensureDirectory, listDirectory, readJsonIfExists, toProjectRelative, writeJsonAtomic } from '../common/fs.js';
5
+ import { Mutex } from '../common/mutex.js';
6
+ import { formatIsoWithOffset } from '../common/time.js';
7
+ import { normalizeAgentName, parseReferenceValue } from '../book/references.js';
8
+ import { CHATS_DIRECTORY } from '../project/books.js';
9
+ import { newTaskPath } from '../tasks/authoring.js';
10
+ import { generatedName } from '../tasks/identity.js';
11
+ import { appendChatMessage, chatAgent, isHumanFacing, parseChatBook, renderChatBook, setChatStatus } from './chat-book.js';
12
+ /**
13
+ * Durable chat Books in root-level `chats/`, with per-message persistence and local read markers.
14
+ *
15
+ * @private chats
16
+ */
17
+ export class ChatStore {
18
+ project;
19
+ options;
20
+ mutex = new Mutex();
21
+ /** Absolute chats directory. */
22
+ directory;
23
+ constructor(project, options) {
24
+ this.project = project;
25
+ this.options = options;
26
+ this.directory = join(project.root, CHATS_DIRECTORY);
27
+ }
28
+ /** Current timestamp in the project's time zone. */
29
+ now() {
30
+ return formatIsoWithOffset(Date.now(), this.options.timeZone);
31
+ }
32
+ /** Every chat Book (deterministic order: shallow first, then path). */
33
+ async list() {
34
+ const found = [];
35
+ const stack = [this.directory];
36
+ while (stack.length) {
37
+ const current = stack.pop();
38
+ for (const entry of await listDirectory(current)) {
39
+ const path = join(current, entry.name);
40
+ if (entry.isDirectory && !entry.name.startsWith('_') && !entry.name.startsWith('.'))
41
+ stack.push(path);
42
+ else if (entry.isFile && entry.name.toLowerCase().endsWith('.book')) {
43
+ const source = await readFile(path, 'utf8').catch(() => undefined);
44
+ if (source !== undefined)
45
+ found.push({ path, relativePath: toProjectRelative(this.project.root, path), chat: parseChatBook(source) });
46
+ }
47
+ }
48
+ }
49
+ return found.sort((a, b) => a.relativePath.split('/').length - b.relativePath.split('/').length || (a.relativePath < b.relativePath ? -1 : 1));
50
+ }
51
+ /** Finds a chat by name (`{Name}`, `@Name` or plain text); the first same-name chat wins. */
52
+ async find(name) {
53
+ const key = parseReferenceValue(name)?.normalized ?? normalizeAgentName(name);
54
+ return (await this.list()).find((stored) => stored.chat.key === key);
55
+ }
56
+ /**
57
+ * Creates a chat named after its readable title plus creation date/time (distinct under normalization from every
58
+ * Book); a human-facing chat is committed. The final name is allocated once, under the store's lock.
59
+ */
60
+ async create(options) {
61
+ return await this.mutex.run(async () => {
62
+ const fresh = (await this.options.takenNames?.().catch(() => undefined)) ?? [];
63
+ const taken = new Set([...(options.taken ?? []), ...fresh, ...(await this.list()).map((stored) => stored.chat.key)]);
64
+ const title = options.name.replace(/[\r\n]+/g, ' ').slice(0, 120);
65
+ const name = generatedName(title, { taken, timeZone: this.options.timeZone });
66
+ await ensureDirectory(this.directory);
67
+ const path = await newTaskPath(this.directory, title, Date.now(), this.options.timeZone);
68
+ const source = renderChatBook({ name, members: options.members, status: options.status, links: options.links, messages: (options.messages ?? []).map((message) => ({ ...message, at: message.at ?? this.now() })) });
69
+ await writeFile(path, source, { flag: 'wx' });
70
+ const chat = parseChatBook(source);
71
+ const commit = isHumanFacing(chat) || options.commit ? await this.persist(path, name, options.trailers) : undefined;
72
+ return { path, relativePath: toProjectRelative(this.project.root, path), chat, commit };
73
+ });
74
+ }
75
+ /**
76
+ * Appends one message (optionally changing the status) in a single save and, for human-facing chats, commit.
77
+ *
78
+ * `expectedMessages` rejects a stale client that did not see the latest message (no duplicate answers).
79
+ */
80
+ async append(name, message, options = {}) {
81
+ return await this.mutex.run(async () => {
82
+ const stored = await this.find(name);
83
+ if (!stored)
84
+ throw new PtbkError(`No chat named "${name}".`, { kind: 'not-found', next: ['List chats with `ptbk chat --list`.'] });
85
+ if (options.expectedMessages !== undefined && options.expectedMessages !== stored.chat.messages.length) {
86
+ throw new PtbkError(`The chat "${stored.chat.name}" changed since it was displayed (${stored.chat.messages.length} messages, expected ${options.expectedMessages}).`, {
87
+ kind: 'conflict',
88
+ effect: 'Your message was not added twice; nothing was saved.',
89
+ next: ['Reload the chat and reply again if still needed.'],
90
+ });
91
+ }
92
+ if (!message.text.trim())
93
+ throw new PtbkError('A chat message cannot be empty.', { kind: 'usage' });
94
+ const source = await readFile(stored.path, 'utf8');
95
+ const updated = appendChatMessage(source, { ...message, at: this.now() }, options.status);
96
+ await writeFile(stored.path, updated);
97
+ const chat = parseChatBook(updated);
98
+ const commit = isHumanFacing(chat) ? await this.persist(stored.path, chat.name, options.trailers) : undefined;
99
+ return { ...stored, chat, commit };
100
+ });
101
+ }
102
+ /** Changes only the durable status (e.g. resolving a request without a new message). */
103
+ async setStatus(name, status) {
104
+ return await this.mutex.run(async () => {
105
+ const stored = await this.find(name);
106
+ if (!stored)
107
+ throw new PtbkError(`No chat named "${name}".`, { kind: 'not-found' });
108
+ if (stored.chat.status === status)
109
+ return stored;
110
+ const updated = setChatStatus(await readFile(stored.path, 'utf8'), status);
111
+ await writeFile(stored.path, updated);
112
+ const chat = parseChatBook(updated);
113
+ const commit = isHumanFacing(chat) ? await this.persist(stored.path, chat.name) : undefined;
114
+ return { ...stored, chat, commit };
115
+ });
116
+ }
117
+ /** Commits (or records as uncommitted) one chat operation. */
118
+ async persist(path, name, trailers = {}) {
119
+ const persistence = this.options.persistence;
120
+ if (!persistence)
121
+ return undefined;
122
+ if (!persistence.commit) {
123
+ await persistence.uncommitted?.(path);
124
+ return undefined;
125
+ }
126
+ try {
127
+ return await persistence.commit([path], `Chat - ${name}`, { 'Chat-Name': name, ...trailers });
128
+ }
129
+ catch (error) {
130
+ throw new PtbkError(`The chat message was written but could not be committed.`, {
131
+ kind: 'persistence',
132
+ reason: error.details?.reason ?? String(error),
133
+ preserved: `${toProjectRelative(this.project.root, path)} keeps the message; it is visibly unsent until committed.`,
134
+ next: ['Fix the Git problem; the pending change is committed with the next chat operation or by `ptbk recover`.'],
135
+ });
136
+ }
137
+ }
138
+ /** Local per-viewer read markers (`.ptbk/state/chat-read.json`): chat key → seen message count. */
139
+ async readMarkers() {
140
+ return (await readJsonIfExists(join(this.project.runtime.state, 'chat-read.json')).catch(() => undefined)) ?? {};
141
+ }
142
+ /** Marks a chat as read up to its current messages (runtime-only; never committed, never an answer). */
143
+ async markRead(name) {
144
+ const stored = await this.find(name);
145
+ if (!stored)
146
+ return;
147
+ const markers = await this.readMarkers();
148
+ markers[stored.chat.key] = stored.chat.messages.length;
149
+ await ensureDirectory(this.project.runtime.state);
150
+ await writeJsonAtomic(join(this.project.runtime.state, 'chat-read.json'), markers);
151
+ }
152
+ /** Summaries of every chat, waiting-for-user first, then most recent. */
153
+ async summaries() {
154
+ const markers = await this.readMarkers();
155
+ const summaries = (await this.list()).map(({ relativePath, chat }) => summarize(chat, relativePath, markers[chat.key] ?? 0));
156
+ return summaries.sort((a, b) => Number(b.status === 'waiting-for-user') - Number(a.status === 'waiting-for-user') || (b.last?.at ?? '').localeCompare(a.last?.at ?? ''));
157
+ }
158
+ }
159
+ /**
160
+ * Builds the summary of one chat.
161
+ *
162
+ * @private chats
163
+ */
164
+ export function summarize(chat, relativePath, seen) {
165
+ const last = chat.messages[chat.messages.length - 1];
166
+ const awaiting = chat.status === 'waiting-for-user' ? [...chat.messages].reverse().find((message) => message.senderKey !== 'user') : undefined;
167
+ return {
168
+ name: chat.name,
169
+ path: relativePath,
170
+ members: chat.members,
171
+ agent: chatAgent(chat),
172
+ status: chat.status,
173
+ humanFacing: isHumanFacing(chat),
174
+ links: chat.links,
175
+ messages: chat.messages.length,
176
+ last: last ? { sender: last.sender, at: last.at, preview: last.text.replace(/\s+/g, ' ').slice(0, 160) } : undefined,
177
+ awaiting: awaiting ? { sender: awaiting.sender, text: awaiting.text, choices: awaiting.choices } : undefined,
178
+ unread: chat.messages.slice(seen).filter((message) => message.senderKey !== 'user').length,
179
+ };
180
+ }
181
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/chats/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import type { ChatStore } from './store.js';
2
+ /**
3
+ * Chat operations available to an executing task (TEAM @User, browser sign-in, recovery) and its consultations.
4
+ *
5
+ * @private chats
6
+ */
7
+ export type TaskChats = {
8
+ /**
9
+ * Starts or continues the agent→User chat for a genuine blocker; repeated reports of the same unresolved question
10
+ * reuse the waiting chat instead of asking again. Returns the chat and the message count to wait beyond.
11
+ */
12
+ readonly askUser: (input: {
13
+ readonly agent: string;
14
+ readonly question: string;
15
+ readonly reason?: string;
16
+ readonly attempted?: string;
17
+ readonly choices?: readonly string[];
18
+ }) => Promise<{
19
+ readonly chat: string;
20
+ readonly awaited: number;
21
+ readonly reused: boolean;
22
+ }>;
23
+ /** Records a task-scoped agent↔agent consultation; it is saved with the task's single result commit. */
24
+ readonly recordConsultation: (input: {
25
+ readonly from: string;
26
+ readonly to: string;
27
+ readonly question: string;
28
+ readonly answer: string;
29
+ }) => Promise<string | undefined>;
30
+ };
31
+ /**
32
+ * Chat operations of a consultation serving a human-facing conversation without an owning task (e.g. Chatter asking
33
+ * a specialist): each bounded two-agent exchange is its own chat Book linked to the conversation and persisted as an
34
+ * ordinary scoped authoring operation — never a fabricated task, a task result or a participant of the human chat.
35
+ *
36
+ * @private chats
37
+ */
38
+ export declare function conversationChats(store: ChatStore, conversation: string, options: {
39
+ readonly takenNames: () => ReadonlySet<string>;
40
+ }): TaskChats;
41
+ /**
42
+ * Chat operations bound to one task.
43
+ *
44
+ * @private chats
45
+ */
46
+ export declare function taskChats(store: ChatStore, task: {
47
+ readonly name: string;
48
+ readonly key: string;
49
+ }, options: {
50
+ readonly takenNames: () => ReadonlySet<string>;
51
+ readonly onCreated?: (path: string) => void;
52
+ }): TaskChats;
@@ -0,0 +1,96 @@
1
+ import { formatAgentReference, parseReferenceValue, referenceName } from '../book/references.js';
2
+ /**
3
+ * Whether a chat links a task (by normalized name).
4
+ */
5
+ function linksTask(chat, taskKey) {
6
+ return chat.links.some((link) => parseReferenceValue(link)?.normalized === taskKey);
7
+ }
8
+ /**
9
+ * Composes the agent's message: what is blocked, what was attempted and the minimal question/action.
10
+ */
11
+ function questionText(input) {
12
+ return [input.reason?.trim(), input.attempted?.trim() ? `Already attempted: ${input.attempted.trim()}` : undefined, input.question.trim()].filter(Boolean).join('\n\n');
13
+ }
14
+ /**
15
+ * Chat operations of a consultation serving a human-facing conversation without an owning task (e.g. Chatter asking
16
+ * a specialist): each bounded two-agent exchange is its own chat Book linked to the conversation and persisted as an
17
+ * ordinary scoped authoring operation — never a fabricated task, a task result or a participant of the human chat.
18
+ *
19
+ * @private chats
20
+ */
21
+ export function conversationChats(store, conversation, options) {
22
+ return {
23
+ askUser: async () => {
24
+ throw new Error('Advisors of a conversation cannot ask the user; the conversation agent replies to the user itself.');
25
+ },
26
+ recordConsultation: async (input) => {
27
+ const from = formatAgentReference(referenceName(input.from));
28
+ const to = formatAgentReference(referenceName(input.to));
29
+ const created = await store.create({
30
+ name: `Consultation: ${referenceName(input.from)} with ${referenceName(input.to)} for ${conversation}`.slice(0, 120),
31
+ members: [from, to],
32
+ status: 'resolved',
33
+ links: [`{{${conversation}}}`],
34
+ messages: [
35
+ { sender: from, text: input.question },
36
+ { sender: to, text: input.answer },
37
+ ],
38
+ taken: options.takenNames(),
39
+ commit: true,
40
+ });
41
+ return created.chat.name;
42
+ },
43
+ };
44
+ }
45
+ /**
46
+ * Chat operations bound to one task.
47
+ *
48
+ * @private chats
49
+ */
50
+ export function taskChats(store, task, options) {
51
+ const taskRef = `{{${task.name}}}`;
52
+ return {
53
+ askUser: async (input) => {
54
+ const agentRef = formatAgentReference(referenceName(input.agent));
55
+ const text = questionText(input);
56
+ const agentKey = parseReferenceValue(agentRef)?.normalized;
57
+ const existing = (await store.list()).find((stored) => linksTask(stored.chat, task.key) && stored.chat.members.some((member) => parseReferenceValue(member)?.normalized === agentKey) && stored.chat.members.some((member) => parseReferenceValue(member)?.normalized === 'user'));
58
+ if (existing) {
59
+ const last = [...existing.chat.messages].reverse().find((message) => message.senderKey === agentKey);
60
+ // The same still-unanswered question is never sent twice.
61
+ if (existing.chat.status === 'waiting-for-user' && last && last.text.trim() === text.trim())
62
+ return { chat: existing.chat.name, awaited: existing.chat.messages.length, reused: true };
63
+ const written = await store.append(existing.chat.name, { sender: agentRef, text, choices: input.choices }, { status: 'waiting-for-user', trailers: { 'Task-Name': task.name } });
64
+ return { chat: written.chat.name, awaited: written.chat.messages.length, reused: false };
65
+ }
66
+ const created = await store.create({
67
+ name: `${referenceName(input.agent)} asks: ${input.question.split('\n').find((line) => line.trim())?.trim().slice(0, 70) ?? task.name}`,
68
+ members: ['@User', agentRef],
69
+ status: 'waiting-for-user',
70
+ links: [taskRef],
71
+ messages: [{ sender: agentRef, text, choices: input.choices }],
72
+ taken: options.takenNames(),
73
+ trailers: { 'Task-Name': task.name },
74
+ });
75
+ options.onCreated?.(created.path);
76
+ return { chat: created.chat.name, awaited: created.chat.messages.length, reused: false };
77
+ },
78
+ recordConsultation: async (input) => {
79
+ const from = formatAgentReference(referenceName(input.from));
80
+ const to = formatAgentReference(referenceName(input.to));
81
+ const created = await store.create({
82
+ name: `Consultation: ${referenceName(input.from)} with ${referenceName(input.to)} on ${task.name}`.slice(0, 120),
83
+ members: [from, to],
84
+ status: 'resolved',
85
+ links: [taskRef],
86
+ messages: [
87
+ { sender: from, text: input.question },
88
+ { sender: to, text: input.answer },
89
+ ],
90
+ taken: options.takenNames(),
91
+ });
92
+ return created.chat.name;
93
+ },
94
+ };
95
+ }
96
+ //# sourceMappingURL=task-chats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-chats.js","sourceRoot":"","sources":["../../src/chats/task-chats.ts"],"names":[],"mappings":"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"}