@agimon-ai/doompi-user-feedback 0.0.1-alpha.6 → 0.0.1-alpha.60

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 (183) hide show
  1. package/README.md +96 -35
  2. package/dist/api-contracts.cjs +1 -0
  3. package/dist/api-contracts.d.cts +2 -0
  4. package/dist/api-contracts.d.mts +2 -0
  5. package/dist/api-contracts.mjs +1 -0
  6. package/dist/constants/package.cjs +2 -0
  7. package/dist/constants/package.cjs.map +1 -0
  8. package/dist/constants/package.mjs +2 -0
  9. package/dist/constants/package.mjs.map +1 -0
  10. package/dist/constants/questionnaire.cjs +2 -0
  11. package/dist/constants/questionnaire.cjs.map +1 -0
  12. package/dist/constants/questionnaire.mjs +2 -0
  13. package/dist/constants/questionnaire.mjs.map +1 -0
  14. package/dist/constants/tool.cjs +2 -0
  15. package/dist/constants/tool.cjs.map +1 -0
  16. package/dist/constants/tool.mjs +2 -0
  17. package/dist/constants/tool.mjs.map +1 -0
  18. package/dist/extensions/pi.cjs +1 -1
  19. package/dist/extensions/pi.cjs.map +1 -1
  20. package/dist/extensions/pi.d.cts +2 -3
  21. package/dist/extensions/pi.d.cts.map +1 -1
  22. package/dist/extensions/pi.d.mts +2 -3
  23. package/dist/extensions/pi.d.mts.map +1 -1
  24. package/dist/extensions/pi.mjs +1 -1
  25. package/dist/extensions/pi.mjs.map +1 -1
  26. package/dist/extensions/server.cjs +2 -0
  27. package/dist/extensions/server.cjs.map +1 -0
  28. package/dist/extensions/server.d.cts +5 -0
  29. package/dist/extensions/server.d.cts.map +1 -0
  30. package/dist/extensions/server.d.mts +5 -0
  31. package/dist/extensions/server.d.mts.map +1 -0
  32. package/dist/extensions/server.mjs +2 -0
  33. package/dist/extensions/server.mjs.map +1 -0
  34. package/dist/index.cjs +0 -1
  35. package/dist/index.d.cts +3 -4
  36. package/dist/index.d.mts +3 -4
  37. package/dist/index.mjs +1 -1
  38. package/dist/schemas/apiContracts.cjs +2 -0
  39. package/dist/schemas/apiContracts.cjs.map +1 -0
  40. package/dist/schemas/apiContracts.d.cts +53 -0
  41. package/dist/schemas/apiContracts.d.cts.map +1 -0
  42. package/dist/schemas/apiContracts.d.mts +53 -0
  43. package/dist/schemas/apiContracts.d.mts.map +1 -0
  44. package/dist/schemas/apiContracts.mjs +2 -0
  45. package/dist/schemas/apiContracts.mjs.map +1 -0
  46. package/dist/schemas/questionnaire.cjs +1 -1
  47. package/dist/schemas/questionnaire.cjs.map +1 -1
  48. package/dist/schemas/questionnaire.d.cts +2 -3
  49. package/dist/schemas/questionnaire.d.cts.map +1 -1
  50. package/dist/schemas/questionnaire.d.mts +2 -3
  51. package/dist/schemas/questionnaire.d.mts.map +1 -1
  52. package/dist/schemas/questionnaire.mjs +1 -1
  53. package/dist/schemas/questionnaire.mjs.map +1 -1
  54. package/dist/services/answerEnvelope/index.cjs +2 -0
  55. package/dist/services/answerEnvelope/index.cjs.map +1 -0
  56. package/dist/services/answerEnvelope/index.mjs +2 -0
  57. package/dist/services/answerEnvelope/index.mjs.map +1 -0
  58. package/dist/services/askUserToolGate/index.cjs +2 -0
  59. package/dist/services/askUserToolGate/index.cjs.map +1 -0
  60. package/dist/services/askUserToolGate/index.mjs +2 -0
  61. package/dist/services/askUserToolGate/index.mjs.map +1 -0
  62. package/dist/services/autonomousVoiceMode/index.cjs +2 -0
  63. package/dist/services/autonomousVoiceMode/index.cjs.map +1 -0
  64. package/dist/services/autonomousVoiceMode/index.mjs +2 -0
  65. package/dist/services/autonomousVoiceMode/index.mjs.map +1 -0
  66. package/dist/{adapters/config/configAdapter.cjs → services/config/index.cjs} +1 -1
  67. package/dist/{adapters/config/configAdapter.mjs.map → services/config/index.cjs.map} +1 -1
  68. package/dist/{adapters/config/configAdapter.mjs → services/config/index.mjs} +1 -1
  69. package/dist/services/config/index.mjs.map +1 -0
  70. package/dist/services/questionnaireCoordinator/index.cjs +2 -0
  71. package/dist/services/questionnaireCoordinator/index.cjs.map +1 -0
  72. package/dist/services/questionnaireCoordinator/index.mjs +2 -0
  73. package/dist/services/questionnaireCoordinator/index.mjs.map +1 -0
  74. package/dist/services/questionnaireService/index.cjs +2 -0
  75. package/dist/services/questionnaireService/index.cjs.map +1 -0
  76. package/dist/services/questionnaireService/index.mjs +2 -0
  77. package/dist/services/questionnaireService/index.mjs.map +1 -0
  78. package/dist/services/{responseService.cjs → responseService/index.cjs} +1 -1
  79. package/dist/services/responseService/index.cjs.map +1 -0
  80. package/dist/services/{responseService.mjs → responseService/index.mjs} +1 -1
  81. package/dist/services/responseService/index.mjs.map +1 -0
  82. package/dist/services/rpcQuestionnaire/index.cjs +2 -0
  83. package/dist/services/rpcQuestionnaire/index.cjs.map +1 -0
  84. package/dist/services/rpcQuestionnaire/index.mjs +2 -0
  85. package/dist/services/rpcQuestionnaire/index.mjs.map +1 -0
  86. package/dist/services/validationService/index.cjs +2 -0
  87. package/dist/services/validationService/index.cjs.map +1 -0
  88. package/dist/services/validationService/index.mjs +2 -0
  89. package/dist/services/validationService/index.mjs.map +1 -0
  90. package/dist/services/voiceQuestionHandoff/index.cjs +5 -0
  91. package/dist/services/voiceQuestionHandoff/index.cjs.map +1 -0
  92. package/dist/services/voiceQuestionHandoff/index.mjs +5 -0
  93. package/dist/services/voiceQuestionHandoff/index.mjs.map +1 -0
  94. package/dist/services/voiceQuestionNarration/index.cjs +2 -0
  95. package/dist/services/voiceQuestionNarration/index.cjs.map +1 -0
  96. package/dist/services/voiceQuestionNarration/index.mjs +2 -0
  97. package/dist/services/voiceQuestionNarration/index.mjs.map +1 -0
  98. package/dist/tools/askUserHeadless.cjs +2 -0
  99. package/dist/tools/askUserHeadless.cjs.map +1 -0
  100. package/dist/tools/askUserHeadless.mjs +2 -0
  101. package/dist/tools/askUserHeadless.mjs.map +1 -0
  102. package/dist/tools/askUserQuestion.cjs +14 -0
  103. package/dist/tools/askUserQuestion.cjs.map +1 -0
  104. package/dist/tools/askUserQuestion.mjs +14 -0
  105. package/dist/tools/askUserQuestion.mjs.map +1 -0
  106. package/dist/tui/askUserToolRender.cjs +3 -0
  107. package/dist/tui/askUserToolRender.cjs.map +1 -0
  108. package/dist/tui/askUserToolRender.mjs +3 -0
  109. package/dist/tui/askUserToolRender.mjs.map +1 -0
  110. package/dist/tui/externalEditor.cjs.map +1 -1
  111. package/dist/tui/externalEditor.mjs.map +1 -1
  112. package/dist/tui/questionnaireComponent.cjs +1 -1
  113. package/dist/tui/questionnaireComponent.cjs.map +1 -1
  114. package/dist/tui/questionnaireComponent.mjs +1 -1
  115. package/dist/tui/questionnaireComponent.mjs.map +1 -1
  116. package/dist/tui/runQuestionnaire.cjs.map +1 -1
  117. package/dist/tui/runQuestionnaire.mjs.map +1 -1
  118. package/dist/types/askUserWire.cjs +2 -0
  119. package/dist/types/askUserWire.cjs.map +1 -0
  120. package/dist/types/askUserWire.mjs +2 -0
  121. package/dist/types/askUserWire.mjs.map +1 -0
  122. package/dist/types/questionnaire.d.cts +7 -6
  123. package/dist/types/questionnaire.d.cts.map +1 -1
  124. package/dist/types/questionnaire.d.mts +7 -6
  125. package/dist/types/questionnaire.d.mts.map +1 -1
  126. package/package.json +68 -27
  127. package/src/extensions/web.ts +33 -0
  128. package/src/types/askUserWire.ts +45 -0
  129. package/src/types/questionnaire.ts +44 -0
  130. package/src/web/components/AskUserQuestionToolMessage.tsx +88 -0
  131. package/src/web/components/QuestionnairePrompt.tsx +371 -0
  132. package/src/web/lib/askUserText.ts +65 -0
  133. package/src/web/lib/questionnaireDraft.ts +149 -0
  134. package/src/web/tsconfig.json +23 -0
  135. package/dist/adapters/config/configAdapter.cjs.map +0 -1
  136. package/dist/adapters/doom/voiceQuestionHandoff.cjs +0 -5
  137. package/dist/adapters/doom/voiceQuestionHandoff.cjs.map +0 -1
  138. package/dist/adapters/doom/voiceQuestionHandoff.d.cts +0 -32
  139. package/dist/adapters/doom/voiceQuestionHandoff.d.cts.map +0 -1
  140. package/dist/adapters/doom/voiceQuestionHandoff.d.mts +0 -32
  141. package/dist/adapters/doom/voiceQuestionHandoff.d.mts.map +0 -1
  142. package/dist/adapters/doom/voiceQuestionHandoff.mjs +0 -5
  143. package/dist/adapters/doom/voiceQuestionHandoff.mjs.map +0 -1
  144. package/dist/adapters/pi/askUserQuestionAdapter.cjs +0 -15
  145. package/dist/adapters/pi/askUserQuestionAdapter.cjs.map +0 -1
  146. package/dist/adapters/pi/askUserQuestionAdapter.mjs +0 -15
  147. package/dist/adapters/pi/askUserQuestionAdapter.mjs.map +0 -1
  148. package/dist/adapters/pi/reconcileAdapter.cjs +0 -2
  149. package/dist/adapters/pi/reconcileAdapter.cjs.map +0 -1
  150. package/dist/adapters/pi/reconcileAdapter.mjs +0 -2
  151. package/dist/adapters/pi/reconcileAdapter.mjs.map +0 -1
  152. package/dist/adapters/pi/rpcQuestionnaireAdapter.cjs +0 -2
  153. package/dist/adapters/pi/rpcQuestionnaireAdapter.cjs.map +0 -1
  154. package/dist/adapters/pi/rpcQuestionnaireAdapter.mjs +0 -2
  155. package/dist/adapters/pi/rpcQuestionnaireAdapter.mjs.map +0 -1
  156. package/dist/events.cjs +0 -1
  157. package/dist/events.d.cts +0 -2
  158. package/dist/events.d.mts +0 -2
  159. package/dist/events.mjs +0 -1
  160. package/dist/schemas/events.cjs +0 -1
  161. package/dist/schemas/events.d.cts +0 -22
  162. package/dist/schemas/events.d.cts.map +0 -1
  163. package/dist/schemas/events.d.mts +0 -22
  164. package/dist/schemas/events.d.mts.map +0 -1
  165. package/dist/schemas/events.mjs +0 -1
  166. package/dist/services/questionnaireCoordinator.cjs +0 -2
  167. package/dist/services/questionnaireCoordinator.cjs.map +0 -1
  168. package/dist/services/questionnaireCoordinator.mjs +0 -2
  169. package/dist/services/questionnaireCoordinator.mjs.map +0 -1
  170. package/dist/services/responseService.cjs.map +0 -1
  171. package/dist/services/responseService.mjs.map +0 -1
  172. package/dist/services/validationService.cjs +0 -2
  173. package/dist/services/validationService.cjs.map +0 -1
  174. package/dist/services/validationService.mjs +0 -2
  175. package/dist/services/validationService.mjs.map +0 -1
  176. package/dist/services/voiceQuestionNarration.cjs +0 -2
  177. package/dist/services/voiceQuestionNarration.cjs.map +0 -1
  178. package/dist/services/voiceQuestionNarration.mjs +0 -2
  179. package/dist/services/voiceQuestionNarration.mjs.map +0 -1
  180. package/dist/voiceQuestionHandoff.cjs +0 -1
  181. package/dist/voiceQuestionHandoff.d.cts +0 -4
  182. package/dist/voiceQuestionHandoff.d.mts +0 -4
  183. package/dist/voiceQuestionHandoff.mjs +0 -1
package/README.md CHANGED
@@ -1,62 +1,123 @@
1
1
  # @agimon-ai/doompi-user-feedback
2
2
 
3
- **Sometimes the agent needs an answer. Make it ask.**
3
+ Ask structured questions in interactive Pi sessions, including sessions using autonomous Voice.
4
4
 
5
- This package provides the structured `ask_user_question` interaction for Pi. In a normal
6
- terminal session it opens a blocking questionnaire and returns the selected answer to the
7
- agent. The question does not get buried in prose and the turn does not continue on a
8
- guess.
5
+ Part of the [DoomPi distribution](https://www.npmjs.com/package/@agimon-ai/doompi).
9
6
 
10
- In autonomous Voice mode there is no modal to click. The extension formats the choices as
11
- plain text, hands them to narration, terminates the turn, and accepts the next spoken reply
12
- as an ordinary user message.
7
+ The `ask_user_question` tool presents concrete options and waits for a choice or custom-text answer.
13
8
 
14
- This implementation is written for Pi and has no dependency on the old Juicesharp
15
- packages. It keeps the established `rpiv:ask-user:prompt` and `rpiv:ask-user:blocked`
16
- channel names so existing Doompi listeners can migrate without being rewritten.
9
+ > **Alpha:** tool and Voice-handoff contracts may change between releases.
17
10
 
18
- This is part of [Doompi](https://www.npmjs.com/package/@agimon-ai/doompi). The bundled
19
- `ask-user` layer loads it where structured feedback is wanted.
11
+ ## Requirements
20
12
 
21
- ## Install
22
-
23
- ```bash
24
- npm install @agimon-ai/doompi-user-feedback
25
- ```
13
+ - Node.js 22.19.0 or newer
14
+ - Pi 0.85.0 and Pi TUI 0.85.0
26
15
 
27
- ## How it loads
16
+ ## Install
28
17
 
29
- Put the bare package name in a layer, then add that layer to a major mode:
18
+ Define a layer in `.doom/modes.yaml`, then include it in a major mode:
30
19
 
31
20
  ```yaml
32
21
  layers:
33
- ask-user:
22
+ feedback:
34
23
  packages: ['@agimon-ai/doompi-user-feedback']
35
24
 
36
25
  majorMode:
37
- dev:
38
- description: Development mode with structured user clarification.
39
- layers: [ask-user]
26
+ minimal:
27
+ description: Interactive questions without the full default composition.
28
+ layers: [feedback]
29
+ ```
30
+
31
+ For standalone Pi:
32
+
33
+ ```bash
34
+ pi install npm:@agimon-ai/doompi-user-feedback
40
35
  ```
41
36
 
42
- The package's Pi manifest selects the Pi adapter. The explicit `/extensions/pi` subpath
43
- remains available when an adapter path is required.
37
+ Pi loads the package through `package.json.pi.extensions`. Each factory invocation owns one package
38
+ instance. Session replacement resets its state, and shutdown removes its registrations.
39
+
40
+ ## Ask a question
41
+
42
+ ```json
43
+ {
44
+ "questions": [
45
+ {
46
+ "header": "Storage",
47
+ "question": "Where should session results be stored?",
48
+ "options": [
49
+ {
50
+ "label": "Project files",
51
+ "description": "Easy to inspect and commit, but visible in the repository."
52
+ },
53
+ {
54
+ "label": "User state",
55
+ "description": "Keeps generated state outside the repository."
56
+ }
57
+ ],
58
+ "multiSelect": false
59
+ }
60
+ ]
61
+ }
62
+ ```
63
+
64
+ A call accepts 1 to 4 questions. Each question requires 2 to 4 options. Headers are at most 16
65
+ characters; option labels are at most 60 characters. Use `multiSelect: true` when several choices
66
+ are valid. An option can include a Markdown `preview` for meaningful visual comparison.
67
+
68
+ The tool result contains readable answer text plus structured `details.answers`; cancellation sets
69
+ `cancelled: true`. Pressing Escape declines the questionnaire. Do not add an `Other` option because
70
+ the UI supplies a custom-text row.
44
71
 
45
- ## Reusable voice handoff
72
+ ## UI, RPC, and headless behavior
46
73
 
47
- `@agimon-ai/doompi-user-feedback/voice-question-handoff` exposes the same minor-mode
48
- detection, plain-text formatting, and narration handoff to other tools.
49
- `createVoiceQuestionHandoff()` returns a session-scoped handle; call `dispose()` when its
50
- session ends so subscriptions do not outlive their owner.
74
+ The tool requires an interactive UI. It uses the TUI questionnaire when available and can use Pi's
75
+ dialog or RPC UI bridge. A headless context without either UI returns a structured `no_ui`
76
+ cancellation rather than waiting forever.
51
77
 
52
- Voice results include the readable question, set `terminate: true`, and mark the turn as
53
- waiting for a response. The answer then arrives through the normal user-message path.
78
+ Questionnaires are coordinated one at a time. Cancellation, session replacement, or shutdown
79
+ aborts the active prompt and settles queued work.
80
+
81
+ ## Autonomous Voice
82
+
83
+ While autonomous Voice mode is active, `ask_user_question` leaves Pi's active tool set. Voice already
84
+ speaks its own question through `narrate` and then waits for the spoken reply, so keeping a
85
+ questionnaire tool alongside it asks the same thing twice. The agent asks by narrating, and the
86
+ answer arrives as an ordinary user message.
87
+
88
+ The gate tracks its own removal. A tool switched off by the user stays off, no other package's tools
89
+ are touched, and the removal is restored when Voice deactivates, when the Voice services unload, or
90
+ when this runtime shuts down.
91
+
92
+ A call already in flight when Voice activates still hands off: the extension formats the questions as
93
+ plain text, requests narration, and returns `terminate: true`, ending the turn while it waits.
94
+
95
+ Both paths use the shared `doom/minor-mode-catalog` and `doom/narration` Cordis services. If a
96
+ provider unloads, User Feedback restores the tool and falls back to its interactive questionnaire
97
+ without retaining the old provider.
54
98
 
55
99
  ## Public API
56
100
 
57
- The package exports the extension, event names, reusable voice handoff, and Pi adapter
58
- through supported subpaths.
101
+ Question and result types are available from the package root. Cross-extension ask-user events and
102
+ Voice services come from `@agimon-ai/doompi-core`; consumers should use those Cordis
103
+ contracts. Pi loads the default export of `/extensions/pi` through package metadata, so installation
104
+ does not require a manual registration call.
105
+
106
+ ## Development
107
+
108
+ Run from this package directory in the workspace:
109
+
110
+ ```bash
111
+ pnpm build
112
+ pnpm typecheck
113
+ pnpm test
114
+ pnpm lint
115
+ ```
116
+
117
+ Maintained by [Agimon](https://agimon.ai/about).
59
118
 
60
119
  ## License
61
120
 
62
121
  MIT
122
+
123
+ The named Pi and server factories live in `src/extensions`. Tools return typed declarations using questionnaire services. TUI renderers are supplied by the Pi entry. Each Pi mount owns optional Voice service subscriptions, a live tool restriction, and awaited questionnaire shutdown through `onDispose`. Public types are exposed through flat `src/exports`.
@@ -0,0 +1 @@
1
+ Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("./schemas/apiContracts.cjs");exports.apiContracts=e.apiContracts,exports.default=e.apiContracts;
@@ -0,0 +1,2 @@
1
+ import { apiContracts } from "./schemas/apiContracts.cjs";
2
+ export { apiContracts, apiContracts as default };
@@ -0,0 +1,2 @@
1
+ import { apiContracts } from "./schemas/apiContracts.mjs";
2
+ export { apiContracts, apiContracts as default };
@@ -0,0 +1 @@
1
+ import{apiContracts as e}from"./schemas/apiContracts.mjs";export{e as apiContracts,e as default};
@@ -0,0 +1,2 @@
1
+ exports.PACKAGE_SOURCE=`@agimon-ai/doompi-user-feedback`;
2
+ //# sourceMappingURL=package.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package.cjs","names":[],"sources":["../../src/constants/package.ts"],"sourcesContent":["export const PACKAGE_SOURCE = '@agimon-ai/doompi-user-feedback';\n"],"mappings":""}
@@ -0,0 +1,2 @@
1
+ const e=`@agimon-ai/doompi-user-feedback`;export{e as PACKAGE_SOURCE};
2
+ //# sourceMappingURL=package.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package.mjs","names":[],"sources":["../../src/constants/package.ts"],"sourcesContent":["export const PACKAGE_SOURCE = '@agimon-ai/doompi-user-feedback';\n"],"mappings":"AAAA,MAAa,EAAiB"}
@@ -0,0 +1,2 @@
1
+ const e=[`Other`,`Type something.`,`Next`];exports.RESERVED_LABELS=e;
2
+ //# sourceMappingURL=questionnaire.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"questionnaire.cjs","names":[],"sources":["../../src/constants/questionnaire.ts"],"sourcesContent":["export const MAX_QUESTIONS = 4;\nexport const MIN_OPTIONS = 2;\nexport const MAX_OPTIONS = 4;\nexport const MAX_HEADER_LENGTH = 16;\nexport const MAX_LABEL_LENGTH = 60;\n\nexport const RESERVED_LABELS = ['Other', 'Type something.', 'Next'] as const;\n"],"mappings":"AAMA,MAAa,EAAkB,CAAC,QAAS,kBAAmB,MAAM"}
@@ -0,0 +1,2 @@
1
+ const e=[`Other`,`Type something.`,`Next`];export{e as RESERVED_LABELS};
2
+ //# sourceMappingURL=questionnaire.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"questionnaire.mjs","names":[],"sources":["../../src/constants/questionnaire.ts"],"sourcesContent":["export const MAX_QUESTIONS = 4;\nexport const MIN_OPTIONS = 2;\nexport const MAX_OPTIONS = 4;\nexport const MAX_HEADER_LENGTH = 16;\nexport const MAX_LABEL_LENGTH = 60;\n\nexport const RESERVED_LABELS = ['Other', 'Type something.', 'Next'] as const;\n"],"mappings":"AAMA,MAAa,EAAkB,CAAC,QAAS,kBAAmB,MAAM"}
@@ -0,0 +1,2 @@
1
+ const e=[`Use ask_user_question whenever the user's request is underspecified and you cannot proceed without concrete decisions, you can ask up to 4 questions per invocation.`,`Each question MUST have 2-4 options. Every option requires a concise label (1-5 words) and a description explaining what the choice means or its trade-offs. The user can additionally type a custom answer via the automatically appended "Type something." row on every question, or press Esc to abandon the questionnaire. Do NOT author "Other" or "Type something." labels yourself, reserved labels are rejected at runtime.`,`Set multiSelect: true when multiple answers are valid. Provide an options[].preview markdown string when an option benefits from richer side-by-side context (mockups, code snippets, diagrams, configs), single-select only. If you recommend a specific option, make that the first option and append "(Recommended)" to its label.`,`Do not stack multiple ask_user_question calls back-to-back, group all clarifying questions into one invocation.`];exports.ASK_USER_QUESTION_TOOL_NAME=`ask_user_question`,exports.DEFAULT_PROMPT_GUIDELINES=e,exports.DEFAULT_PROMPT_SNIPPET=`Ask the user up to 4 structured questions (2-4 options each) when requirements are ambiguous`,exports.ERROR_NO_CUSTOM_UI=`Error: this client cannot render the questionnaire (custom UI is unavailable). The user never saw the questions, do NOT treat this as a decline. Ask the questions as plain chat text instead, without using this tool.`,exports.ERROR_NO_UI=`Error: UI not available (running in non-interactive mode)`,exports.ERROR_SESSION_INACTIVE=`Error: the questionnaire session is no longer active.`,exports.ERROR_SESSION_LOAD_FAILED=`Error: the questionnaire UI failed to load. The user never saw the questions, do NOT treat this as a decline. Ask the questions as plain chat text instead.`;
2
+ //# sourceMappingURL=tool.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.cjs","names":[],"sources":["../../src/constants/tool.ts"],"sourcesContent":["import { MAX_OPTIONS, MAX_QUESTIONS, MIN_OPTIONS } from './questionnaire';\nexport const ASK_USER_QUESTION_TOOL_NAME = 'ask_user_question';\n\nexport const DEFAULT_PROMPT_SNIPPET = `Ask the user up to ${MAX_QUESTIONS} structured questions (${MIN_OPTIONS}-${MAX_OPTIONS} options each) when requirements are ambiguous`;\nexport const DEFAULT_PROMPT_GUIDELINES = [\n `Use ask_user_question whenever the user's request is underspecified and you cannot proceed without concrete decisions, you can ask up to ${MAX_QUESTIONS} questions per invocation.`,\n `Each question MUST have ${MIN_OPTIONS}-${MAX_OPTIONS} options. Every option requires a concise label (1-5 words) and a description explaining what the choice means or its trade-offs. The user can additionally type a custom answer via the automatically appended \"Type something.\" row on every question, or press Esc to abandon the questionnaire. Do NOT author \"Other\" or \"Type something.\" labels yourself, reserved labels are rejected at runtime.`,\n 'Set multiSelect: true when multiple answers are valid. Provide an options[].preview markdown string when an option benefits from richer side-by-side context (mockups, code snippets, diagrams, configs), single-select only. If you recommend a specific option, make that the first option and append \"(Recommended)\" to its label.',\n 'Do not stack multiple ask_user_question calls back-to-back, group all clarifying questions into one invocation.',\n];\n\nexport const ERROR_NO_UI = 'Error: UI not available (running in non-interactive mode)';\nexport const ERROR_NO_CUSTOM_UI =\n 'Error: this client cannot render the questionnaire (custom UI is unavailable). The user never saw the questions, do NOT treat this as a decline. Ask the questions as plain chat text instead, without using this tool.';\nexport const ERROR_SESSION_LOAD_FAILED =\n 'Error: the questionnaire UI failed to load. The user never saw the questions, do NOT treat this as a decline. Ask the questions as plain chat text instead.';\nexport const ERROR_SESSION_INACTIVE = 'Error: the questionnaire session is no longer active.';\n"],"mappings":"AACA,MAGa,EAA4B,CACvC,uKACA,saACA,wUACA,iHACF"}
@@ -0,0 +1,2 @@
1
+ const e=`ask_user_question`,t=`Ask the user up to 4 structured questions (2-4 options each) when requirements are ambiguous`,n=[`Use ask_user_question whenever the user's request is underspecified and you cannot proceed without concrete decisions, you can ask up to 4 questions per invocation.`,`Each question MUST have 2-4 options. Every option requires a concise label (1-5 words) and a description explaining what the choice means or its trade-offs. The user can additionally type a custom answer via the automatically appended "Type something." row on every question, or press Esc to abandon the questionnaire. Do NOT author "Other" or "Type something." labels yourself, reserved labels are rejected at runtime.`,`Set multiSelect: true when multiple answers are valid. Provide an options[].preview markdown string when an option benefits from richer side-by-side context (mockups, code snippets, diagrams, configs), single-select only. If you recommend a specific option, make that the first option and append "(Recommended)" to its label.`,`Do not stack multiple ask_user_question calls back-to-back, group all clarifying questions into one invocation.`],r=`Error: UI not available (running in non-interactive mode)`,i=`Error: this client cannot render the questionnaire (custom UI is unavailable). The user never saw the questions, do NOT treat this as a decline. Ask the questions as plain chat text instead, without using this tool.`,a=`Error: the questionnaire UI failed to load. The user never saw the questions, do NOT treat this as a decline. Ask the questions as plain chat text instead.`,o=`Error: the questionnaire session is no longer active.`;export{e as ASK_USER_QUESTION_TOOL_NAME,n as DEFAULT_PROMPT_GUIDELINES,t as DEFAULT_PROMPT_SNIPPET,i as ERROR_NO_CUSTOM_UI,r as ERROR_NO_UI,o as ERROR_SESSION_INACTIVE,a as ERROR_SESSION_LOAD_FAILED};
2
+ //# sourceMappingURL=tool.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.mjs","names":[],"sources":["../../src/constants/tool.ts"],"sourcesContent":["import { MAX_OPTIONS, MAX_QUESTIONS, MIN_OPTIONS } from './questionnaire';\nexport const ASK_USER_QUESTION_TOOL_NAME = 'ask_user_question';\n\nexport const DEFAULT_PROMPT_SNIPPET = `Ask the user up to ${MAX_QUESTIONS} structured questions (${MIN_OPTIONS}-${MAX_OPTIONS} options each) when requirements are ambiguous`;\nexport const DEFAULT_PROMPT_GUIDELINES = [\n `Use ask_user_question whenever the user's request is underspecified and you cannot proceed without concrete decisions, you can ask up to ${MAX_QUESTIONS} questions per invocation.`,\n `Each question MUST have ${MIN_OPTIONS}-${MAX_OPTIONS} options. Every option requires a concise label (1-5 words) and a description explaining what the choice means or its trade-offs. The user can additionally type a custom answer via the automatically appended \"Type something.\" row on every question, or press Esc to abandon the questionnaire. Do NOT author \"Other\" or \"Type something.\" labels yourself, reserved labels are rejected at runtime.`,\n 'Set multiSelect: true when multiple answers are valid. Provide an options[].preview markdown string when an option benefits from richer side-by-side context (mockups, code snippets, diagrams, configs), single-select only. If you recommend a specific option, make that the first option and append \"(Recommended)\" to its label.',\n 'Do not stack multiple ask_user_question calls back-to-back, group all clarifying questions into one invocation.',\n];\n\nexport const ERROR_NO_UI = 'Error: UI not available (running in non-interactive mode)';\nexport const ERROR_NO_CUSTOM_UI =\n 'Error: this client cannot render the questionnaire (custom UI is unavailable). The user never saw the questions, do NOT treat this as a decline. Ask the questions as plain chat text instead, without using this tool.';\nexport const ERROR_SESSION_LOAD_FAILED =\n 'Error: the questionnaire UI failed to load. The user never saw the questions, do NOT treat this as a decline. Ask the questions as plain chat text instead.';\nexport const ERROR_SESSION_INACTIVE = 'Error: the questionnaire session is no longer active.';\n"],"mappings":"AACA,MAAa,EAA8B,oBAE9B,EAAyB,+FACzB,EAA4B,CACvC,uKACA,saACA,wUACA,iHACF,EAEa,EAAc,4DACd,EACX,0NACW,EACX,8JACW,EAAyB"}
@@ -1,2 +1,2 @@
1
- Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../adapters/doom/voiceQuestionHandoff.cjs"),t=require("../adapters/pi/askUserQuestionAdapter.cjs"),n=require("../adapters/pi/reconcileAdapter.cjs"),r=require("../services/questionnaireCoordinator.cjs"),i=`@agimon-ai/doompi-user-feedback`,a=Symbol.for(`${i}/installed-hosts`);function o(e){let t=globalThis,n=t[a]??new WeakSet;return t[a]=n,!n.has(e)&&(n.add(e),!0)}function s(a){if(!o(a))return;let s=new r.QuestionnaireCoordinator,c,l=()=>{c?.dispose(),c=void 0};t.registerAskUserQuestionTool(a,{enqueue:(e,t)=>s.enqueue(e,t),runTui:async(...e)=>{let{runTuiQuestionnaire:t}=await Promise.resolve().then(()=>require("../tui/runQuestionnaire.cjs"));return t(...e)},tryVoice:e=>c?.handoff(e)}),n.registerAskUserQuestionReconciler(a),a.on(`session_start`,(t,n)=>{s.shutdown(),s=new r.QuestionnaireCoordinator,l(),c=e.createVoiceQuestionHandoff({bus:a.events,source:i,sessionId:n.sessionManager.getSessionId()})}),a.on(`session_shutdown`,()=>{s.shutdown(),l()})}exports.default=s,exports.registerUserFeedbackExtension=s;
1
+ Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../constants/package.cjs"),t=require("../constants/tool.cjs"),n=require("../services/askUserToolGate/index.cjs"),r=require("../services/autonomousVoiceMode/index.cjs"),i=require("../services/questionnaireCoordinator/index.cjs"),a=require("../services/voiceQuestionHandoff/index.cjs"),o=require("../tools/askUserQuestion.cjs"),s=require("../tui/askUserToolRender.cjs");let c=require("@agimon-ai/doompi-core/narration"),l=require("@agimon-ai/doompi-core/pi-extension"),u=require("@agimon-ai/doompi-minor-mode");function d(){return{answers:[],cancelled:!0}}const f=(0,l.definePiExtension)(e.PACKAGE_SOURCE,({context:f})=>{let p=!0,m=0,h,g=!1,_=new i.QuestionnaireCoordinator,v,y=!1,b=!0,x=new Set,S=()=>n.askUserToolRestriction(t.ASK_USER_QUESTION_TOOL_NAME,b&&!y),C=()=>x.forEach(e=>e()),w=Promise.resolve(),T=new Set,E=e=>(T.add(e),e.then(()=>T.delete(e),()=>T.delete(e)),e),D=(e,t)=>p&&e===m&&t===h,O=(e,t)=>p&&g&&t?.aborted!==!0&&e.sessionManager.getSessionId()===h,k=async()=>{if(!p)return;let e=_;e.shutdown(),p=!1,m+=1,g=!1,h=void 0,await Promise.allSettled(T),await e.waitForIdle()},A={enqueue:(e,t)=>{let n=_,r=m,i=h;return!g||!D(r,i)||t?.aborted?Promise.resolve(d()):n.enqueue(async t=>{if(!g||!D(r,i)||t.signal.aborted)return d();let n=await e({...t,reportProgress:e=>{D(r,i)&&!t.signal.aborted&&t.reportProgress(e)}});return g&&D(r,i)&&!t.signal.aborted?n:d()},t)},isActive:O,runTui:async(e,t,n,r,i)=>{if(!O(e,r))return;let{runTuiQuestionnaire:a}=await Promise.resolve().then(()=>require("../tui/runQuestionnaire.cjs"));if(!O(e,r))return;let o=await a(e,t,n,r,i);return O(e,r)?o:void 0},tryVoice:e=>p&&g?v?.handoff(e):void 0};return{services:[e=>{e.inject([u.DOOM_MINOR_MODE_CATALOG_SERVICE,c.DOOM_NARRATION_SERVICE],e=>{let t=a.createVoiceQuestionHandoff((0,u.requireMinorModeCatalog)(e),(0,c.requireDoomNarrationService)(e));return v=t,()=>{v===t&&(v=void 0)}}),e.inject([u.DOOM_MINOR_MODE_CATALOG_SERVICE],e=>{let t=(0,u.requireMinorModeCatalog)(e),n=()=>{y=r.isAutonomousVoiceActive(t.list()),C()},i=t.subscribe(n);return n(),()=>{i(),y=!1,C()}})}],tools:[(0,l.definePiTool)(o.createAskUserQuestionTool(f,A,s.askUserToolRender))],events:{before_agent_start:(e,t)=>{O(t)&&(b=t.hasUI,C())},session_start:(e,t)=>{if(!p)return;let n=_;n.shutdown();let r=t.sessionManager.getSessionId(),a=++m;_=new i.QuestionnaireCoordinator,h=r,g=!1;let o=async()=>{await n.waitForIdle(),D(a,r)&&(g=!0)},s=w.then(o,o);return w=s.catch(()=>void 0),E(s)}},onDispose:k,toolRestrictions:[{source:e.PACKAGE_SOURCE,get restrict(){return S()},subscribe(e){return x.add(e),()=>{x.delete(e)}}}]}});exports.default=f,exports.userFeedbackExtension=f;
2
2
  //# sourceMappingURL=pi.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"pi.cjs","names":["QuestionnaireCoordinator","createVoiceQuestionHandoff"],"sources":["../../src/extensions/pi.ts"],"sourcesContent":["import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { createVoiceQuestionHandoff, type VoiceQuestionHandoffHandle } from '../adapters/doom/voiceQuestionHandoff.js';\nimport { registerAskUserQuestionTool } from '../adapters/pi/askUserQuestionAdapter.js';\nimport { registerAskUserQuestionReconciler } from '../adapters/pi/reconcileAdapter.js';\nimport { QuestionnaireCoordinator } from '../services/questionnaireCoordinator.js';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi-user-feedback';\nconst INSTALLATIONS_KEY = Symbol.for(`${PACKAGE_SOURCE}/installed-hosts`);\n\ntype InstallationRegistry = typeof globalThis & {\n [INSTALLATIONS_KEY]?: WeakSet<object>;\n};\n\nfunction claimHostInstallation(pi: ExtensionAPI): boolean {\n const registry = globalThis as InstallationRegistry;\n const installedHosts = registry[INSTALLATIONS_KEY] ?? new WeakSet<object>();\n registry[INSTALLATIONS_KEY] = installedHosts;\n if (installedHosts.has(pi)) return false;\n installedHosts.add(pi);\n return true;\n}\n\nexport function registerUserFeedbackExtension(pi: ExtensionAPI): void {\n if (!claimHostInstallation(pi)) return;\n\n let coordinator = new QuestionnaireCoordinator();\n let voiceHandoff: VoiceQuestionHandoffHandle | undefined;\n const resetVoiceHandoff = (): void => {\n voiceHandoff?.dispose();\n voiceHandoff = undefined;\n };\n\n registerAskUserQuestionTool(pi, {\n enqueue: (runner, signal) => coordinator.enqueue(runner, signal),\n runTui: async (...args) => {\n const { runTuiQuestionnaire } = await import('../tui/runQuestionnaire.js');\n return runTuiQuestionnaire(...args);\n },\n tryVoice: (params) => voiceHandoff?.handoff(params),\n });\n registerAskUserQuestionReconciler(pi);\n\n pi.on('session_start', (_event, context) => {\n coordinator.shutdown();\n coordinator = new QuestionnaireCoordinator();\n resetVoiceHandoff();\n\n voiceHandoff = createVoiceQuestionHandoff({\n bus: pi.events,\n source: PACKAGE_SOURCE,\n sessionId: context.sessionManager.getSessionId(),\n });\n });\n pi.on('session_shutdown', () => {\n coordinator.shutdown();\n resetVoiceHandoff();\n });\n}\n\nexport { registerUserFeedbackExtension as default };\n"],"mappings":"0TAMM,EAAiB,kCACjB,EAAoB,OAAO,IAAI,GAAG,EAAe,iBAAiB,EAMxE,SAAS,EAAsB,EAA2B,CACxD,IAAM,EAAW,WACX,EAAiB,EAAS,IAAsB,IAAI,QAI1D,MAHA,GAAS,GAAqB,EAC9B,CAAI,EAAe,IAAI,CAAE,IACzB,EAAe,IAAI,CAAE,EACd,GACT,CAEA,SAAgB,EAA8B,EAAwB,CACpE,GAAI,CAAC,EAAsB,CAAE,EAAG,OAEhC,IAAI,EAAc,IAAIA,EAAAA,yBAClB,EACE,MAAgC,CACpC,GAAc,QAAQ,EACtB,EAAe,IAAA,EACjB,EAEA,EAAA,4BAA4B,EAAI,CAC9B,SAAU,EAAQ,IAAW,EAAY,QAAQ,EAAQ,CAAM,EAC/D,OAAQ,MAAO,GAAG,IAAS,CACzB,GAAM,CAAE,uBAAwB,MAAA,QAAA,QAAA,CAAA,CAAA,SAAA,QAAM,6BAAA,CAAA,EACtC,OAAO,EAAoB,GAAG,CAAI,CACpC,EACA,SAAW,GAAW,GAAc,QAAQ,CAAM,CACpD,CAAC,EACD,EAAA,kCAAkC,CAAE,EAEpC,EAAG,GAAG,iBAAkB,EAAQ,IAAY,CAC1C,EAAY,SAAS,EACrB,EAAc,IAAIA,EAAAA,yBAClB,EAAkB,EAElB,EAAeC,EAAAA,2BAA2B,CACxC,IAAK,EAAG,OACR,OAAQ,EACR,UAAW,EAAQ,eAAe,aAAa,CACjD,CAAC,CACH,CAAC,EACD,EAAG,GAAG,uBAA0B,CAC9B,EAAY,SAAS,EACrB,EAAkB,CACpB,CAAC,CACH"}
1
+ {"version":3,"file":"pi.cjs","names":["definePiExtension","PACKAGE_SOURCE","QuestionnaireCoordinator","askUserToolRestriction","ASK_USER_QUESTION_TOOL_NAME","DOOM_MINOR_MODE_CATALOG_SERVICE","DOOM_NARRATION_SERVICE","createVoiceQuestionHandoff","requireMinorModeCatalog","requireDoomNarrationService","isAutonomousVoiceActive","definePiTool","createAskUserQuestionTool","askUserToolRender"],"sources":["../../src/extensions/pi.ts"],"sourcesContent":["import { DOOM_NARRATION_SERVICE, requireDoomNarrationService } from '@agimon-ai/doompi-core/narration';\nimport { definePiExtension, definePiTool } from '@agimon-ai/doompi-core/pi-extension';\nimport { type DoomToolRestriction } from '@agimon-ai/doompi-core/tool-surface';\nimport { DOOM_MINOR_MODE_CATALOG_SERVICE, requireMinorModeCatalog } from '@agimon-ai/doompi-minor-mode';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionContext } from '@earendil-works/pi-coding-agent';\n\nimport { PACKAGE_SOURCE } from '../constants/package';\nimport { ASK_USER_QUESTION_TOOL_NAME } from '../constants/tool';\nimport { askUserToolRestriction } from '../services/askUserToolGate';\nimport { isAutonomousVoiceActive } from '../services/autonomousVoiceMode';\nimport { QuestionnaireCoordinator, type QuestionnaireRunner } from '../services/questionnaireCoordinator';\nimport { createVoiceQuestionHandoff, type VoiceQuestionHandoff } from '../services/voiceQuestionHandoff';\nimport { createAskUserQuestionTool } from '../tools/askUserQuestion';\nimport { askUserToolRender } from '../tui/askUserToolRender';\nimport type { QuestionnaireResult } from '../types/questionnaire';\n\nfunction cancelledResult(): QuestionnaireResult {\n return { answers: [], cancelled: true };\n}\n\nexport const userFeedbackExtension = definePiExtension(PACKAGE_SOURCE, ({ context: cordis }) => {\n let active = true;\n let sessionGeneration = 0;\n let sessionId: string | undefined;\n let sessionReady = false;\n let coordinator = new QuestionnaireCoordinator();\n let voiceHandoff: VoiceQuestionHandoff | undefined;\n let voiceActive = false;\n let hasUI = true;\n const restrictionListeners = new Set<() => void>();\n const currentRestriction = (): DoomToolRestriction =>\n askUserToolRestriction(ASK_USER_QUESTION_TOOL_NAME, hasUI && !voiceActive);\n const syncToolRestriction = (): void => restrictionListeners.forEach((listener) => listener());\n let lifecycleQueue: Promise<void> = Promise.resolve();\n const pendingOperations = new Set<Promise<unknown>>();\n\n const trackOperation = <T>(operation: Promise<T>): Promise<T> => {\n pendingOperations.add(operation);\n void operation.then(\n () => pendingOperations.delete(operation),\n () => pendingOperations.delete(operation),\n );\n return operation;\n };\n const ownsGeneration = (generation: number, expectedSessionId: string | undefined): boolean =>\n active && generation === sessionGeneration && expectedSessionId === sessionId;\n const isContextActive = (context: ExtensionContext, signal?: AbortSignal): boolean =>\n active && sessionReady && signal?.aborted !== true && context.sessionManager.getSessionId() === sessionId;\n\n const shutdownRuntime = async (): Promise<void> => {\n if (!active) return;\n const ownedCoordinator = coordinator;\n ownedCoordinator.shutdown();\n active = false;\n sessionGeneration += 1;\n sessionReady = false;\n sessionId = undefined;\n await Promise.allSettled(pendingOperations);\n await ownedCoordinator.waitForIdle();\n };\n\n const toolDependencies: Parameters<typeof createAskUserQuestionTool>[1] = {\n enqueue: (runner: QuestionnaireRunner, signal?: AbortSignal) => {\n const ownedCoordinator = coordinator;\n const generation = sessionGeneration;\n const expectedSessionId = sessionId;\n if (!sessionReady || !ownsGeneration(generation, expectedSessionId) || signal?.aborted) {\n return Promise.resolve(cancelledResult());\n }\n return ownedCoordinator.enqueue(async (context) => {\n if (!sessionReady || !ownsGeneration(generation, expectedSessionId) || context.signal.aborted) {\n return cancelledResult();\n }\n const result = await runner({\n ...context,\n reportProgress: (progress) => {\n if (ownsGeneration(generation, expectedSessionId) && !context.signal.aborted) {\n context.reportProgress(progress);\n }\n },\n });\n return sessionReady && ownsGeneration(generation, expectedSessionId) && !context.signal.aborted\n ? result\n : cancelledResult();\n }, signal);\n },\n isActive: isContextActive,\n runTui: async (context, params, collapseKey, signal, reportProgress) => {\n if (!isContextActive(context, signal)) return undefined;\n const { runTuiQuestionnaire } = await import('../tui/runQuestionnaire');\n if (!isContextActive(context, signal)) return undefined;\n const result = await runTuiQuestionnaire(context, params, collapseKey, signal, reportProgress);\n return isContextActive(context, signal) ? result : undefined;\n },\n tryVoice: (params) => (active && sessionReady ? voiceHandoff?.handoff(params) : undefined),\n };\n\n const beforeAgentStart = (_event: unknown, context: ExtensionContext) => {\n if (!isContextActive(context)) return;\n hasUI = context.hasUI;\n syncToolRestriction();\n };\n\n const sessionStart = (_event: unknown, context: ExtensionContext) => {\n if (!active) return;\n const previousCoordinator = coordinator;\n previousCoordinator.shutdown();\n const activeSessionId = context.sessionManager.getSessionId();\n const generation = ++sessionGeneration;\n const nextCoordinator = new QuestionnaireCoordinator();\n coordinator = nextCoordinator;\n sessionId = activeSessionId;\n sessionReady = false;\n\n const initializeSession = async (): Promise<void> => {\n await previousCoordinator.waitForIdle();\n if (!ownsGeneration(generation, activeSessionId)) return;\n sessionReady = true;\n // The surface is re-registered per session from the gate state in force, so a session\n // that starts with Voice already active needs nothing extra here.\n };\n\n const operation = lifecycleQueue.then(initializeSession, initializeSession);\n lifecycleQueue = operation.catch(() => undefined);\n return trackOperation(operation);\n };\n return {\n services: [\n (serviceContextOwner: Context) => {\n serviceContextOwner.inject([DOOM_MINOR_MODE_CATALOG_SERVICE, DOOM_NARRATION_SERVICE], (serviceContext) => {\n const handoff = createVoiceQuestionHandoff(\n requireMinorModeCatalog(serviceContext),\n requireDoomNarrationService(serviceContext),\n );\n voiceHandoff = handoff;\n return () => {\n if (voiceHandoff === handoff) voiceHandoff = undefined;\n };\n });\n\n // Separate from the handoff binding above: the gate needs only the mode catalog, so it\n // still applies to a Voice build that publishes modes without providing narration.\n serviceContextOwner.inject([DOOM_MINOR_MODE_CATALOG_SERVICE], (modeContext) => {\n const modes = requireMinorModeCatalog(modeContext);\n const apply = (): void => {\n voiceActive = isAutonomousVoiceActive(modes.list());\n syncToolRestriction();\n };\n const unsubscribe = modes.subscribe(apply);\n apply();\n return () => {\n unsubscribe();\n voiceActive = false;\n syncToolRestriction();\n };\n });\n },\n ],\n tools: [definePiTool(createAskUserQuestionTool(cordis, toolDependencies, askUserToolRender))],\n events: { before_agent_start: beforeAgentStart, session_start: sessionStart },\n onDispose: shutdownRuntime,\n toolRestrictions: [\n {\n source: PACKAGE_SOURCE,\n get restrict() {\n return currentRestriction();\n },\n subscribe(listener) {\n restrictionListeners.add(listener);\n return () => {\n restrictionListeners.delete(listener);\n };\n },\n },\n ],\n };\n});\nexport default userFeedbackExtension;\n"],"mappings":"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"}
@@ -1,6 +1,5 @@
1
- import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
1
  //#region src/extensions/pi.d.ts
3
- declare function registerUserFeedbackExtension(pi: ExtensionAPI): void;
2
+ export declare const userFeedbackExtension: import("@agimon-ai/doompi-core/runtime-pi-extension").DefinedPiExtension<undefined>;
4
3
  //#endregion
5
- export { registerUserFeedbackExtension as default, registerUserFeedbackExtension };
4
+ export { userFeedbackExtension as default };
6
5
  //# sourceMappingURL=pi.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pi.d.cts","names":[],"sources":["../../src/extensions/pi.ts"],"mappings":";;iBAsBgB,8BAA8B,IAAI"}
1
+ {"version":3,"file":"pi.d.cts","names":[],"sources":["../../src/extensions/pi.ts"],"mappings":";qBAqBa,6EAAqB"}
@@ -1,6 +1,5 @@
1
- import { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
1
  //#region src/extensions/pi.d.ts
3
- declare function registerUserFeedbackExtension(pi: ExtensionAPI): void;
2
+ export declare const userFeedbackExtension: import("@agimon-ai/doompi-core/runtime-pi-extension").DefinedPiExtension<undefined>;
4
3
  //#endregion
5
- export { registerUserFeedbackExtension as default, registerUserFeedbackExtension };
4
+ export { userFeedbackExtension as default };
6
5
  //# sourceMappingURL=pi.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pi.d.mts","names":[],"sources":["../../src/extensions/pi.ts"],"mappings":";;iBAsBgB,8BAA8B,IAAI"}
1
+ {"version":3,"file":"pi.d.mts","names":[],"sources":["../../src/extensions/pi.ts"],"mappings":";qBAqBa,6EAAqB"}
@@ -1,2 +1,2 @@
1
- import{createVoiceQuestionHandoff as e}from"../adapters/doom/voiceQuestionHandoff.mjs";import{registerAskUserQuestionTool as t}from"../adapters/pi/askUserQuestionAdapter.mjs";import{registerAskUserQuestionReconciler as n}from"../adapters/pi/reconcileAdapter.mjs";import{QuestionnaireCoordinator as r}from"../services/questionnaireCoordinator.mjs";const i=`@agimon-ai/doompi-user-feedback`,a=Symbol.for(`${i}/installed-hosts`);function o(e){let t=globalThis,n=t[a]??new WeakSet;return t[a]=n,!n.has(e)&&(n.add(e),!0)}function s(a){if(!o(a))return;let s=new r,c,l=()=>{c?.dispose(),c=void 0};t(a,{enqueue:(e,t)=>s.enqueue(e,t),runTui:async(...e)=>{let{runTuiQuestionnaire:t}=await import(`../tui/runQuestionnaire.mjs`);return t(...e)},tryVoice:e=>c?.handoff(e)}),n(a),a.on(`session_start`,(t,n)=>{s.shutdown(),s=new r,l(),c=e({bus:a.events,source:i,sessionId:n.sessionManager.getSessionId()})}),a.on(`session_shutdown`,()=>{s.shutdown(),l()})}export{s as default,s as registerUserFeedbackExtension};
1
+ import{PACKAGE_SOURCE as e}from"../constants/package.mjs";import{ASK_USER_QUESTION_TOOL_NAME as t}from"../constants/tool.mjs";import{askUserToolRestriction as n}from"../services/askUserToolGate/index.mjs";import{isAutonomousVoiceActive as r}from"../services/autonomousVoiceMode/index.mjs";import{QuestionnaireCoordinator as i}from"../services/questionnaireCoordinator/index.mjs";import{createVoiceQuestionHandoff as a}from"../services/voiceQuestionHandoff/index.mjs";import{createAskUserQuestionTool as o}from"../tools/askUserQuestion.mjs";import{askUserToolRender as s}from"../tui/askUserToolRender.mjs";import{DOOM_NARRATION_SERVICE as c,requireDoomNarrationService as l}from"@agimon-ai/doompi-core/narration";import{definePiExtension as u,definePiTool as d}from"@agimon-ai/doompi-core/pi-extension";import{DOOM_MINOR_MODE_CATALOG_SERVICE as f,requireMinorModeCatalog as p}from"@agimon-ai/doompi-minor-mode";function m(){return{answers:[],cancelled:!0}}const h=u(e,({context:u})=>{let h=!0,g=0,_,v=!1,y=new i,b,x=!1,S=!0,C=new Set,w=()=>n(t,S&&!x),T=()=>C.forEach(e=>e()),E=Promise.resolve(),D=new Set,O=e=>(D.add(e),e.then(()=>D.delete(e),()=>D.delete(e)),e),k=(e,t)=>h&&e===g&&t===_,A=(e,t)=>h&&v&&t?.aborted!==!0&&e.sessionManager.getSessionId()===_;return{services:[e=>{e.inject([f,c],e=>{let t=a(p(e),l(e));return b=t,()=>{b===t&&(b=void 0)}}),e.inject([f],e=>{let t=p(e),n=()=>{x=r(t.list()),T()},i=t.subscribe(n);return n(),()=>{i(),x=!1,T()}})}],tools:[d(o(u,{enqueue:(e,t)=>{let n=y,r=g,i=_;return!v||!k(r,i)||t?.aborted?Promise.resolve(m()):n.enqueue(async t=>{if(!v||!k(r,i)||t.signal.aborted)return m();let n=await e({...t,reportProgress:e=>{k(r,i)&&!t.signal.aborted&&t.reportProgress(e)}});return v&&k(r,i)&&!t.signal.aborted?n:m()},t)},isActive:A,runTui:async(e,t,n,r,i)=>{if(!A(e,r))return;let{runTuiQuestionnaire:a}=await import(`../tui/runQuestionnaire.mjs`);if(!A(e,r))return;let o=await a(e,t,n,r,i);return A(e,r)?o:void 0},tryVoice:e=>h&&v?b?.handoff(e):void 0},s))],events:{before_agent_start:(e,t)=>{A(t)&&(S=t.hasUI,T())},session_start:(e,t)=>{if(!h)return;let n=y;n.shutdown();let r=t.sessionManager.getSessionId(),a=++g;y=new i,_=r,v=!1;let o=async()=>{await n.waitForIdle(),k(a,r)&&(v=!0)},s=E.then(o,o);return E=s.catch(()=>void 0),O(s)}},onDispose:async()=>{if(!h)return;let e=y;e.shutdown(),h=!1,g+=1,v=!1,_=void 0,await Promise.allSettled(D),await e.waitForIdle()},toolRestrictions:[{source:e,get restrict(){return w()},subscribe(e){return C.add(e),()=>{C.delete(e)}}}]}});export{h as default,h as userFeedbackExtension};
2
2
  //# sourceMappingURL=pi.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"pi.mjs","names":[],"sources":["../../src/extensions/pi.ts"],"sourcesContent":["import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport { createVoiceQuestionHandoff, type VoiceQuestionHandoffHandle } from '../adapters/doom/voiceQuestionHandoff.js';\nimport { registerAskUserQuestionTool } from '../adapters/pi/askUserQuestionAdapter.js';\nimport { registerAskUserQuestionReconciler } from '../adapters/pi/reconcileAdapter.js';\nimport { QuestionnaireCoordinator } from '../services/questionnaireCoordinator.js';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi-user-feedback';\nconst INSTALLATIONS_KEY = Symbol.for(`${PACKAGE_SOURCE}/installed-hosts`);\n\ntype InstallationRegistry = typeof globalThis & {\n [INSTALLATIONS_KEY]?: WeakSet<object>;\n};\n\nfunction claimHostInstallation(pi: ExtensionAPI): boolean {\n const registry = globalThis as InstallationRegistry;\n const installedHosts = registry[INSTALLATIONS_KEY] ?? new WeakSet<object>();\n registry[INSTALLATIONS_KEY] = installedHosts;\n if (installedHosts.has(pi)) return false;\n installedHosts.add(pi);\n return true;\n}\n\nexport function registerUserFeedbackExtension(pi: ExtensionAPI): void {\n if (!claimHostInstallation(pi)) return;\n\n let coordinator = new QuestionnaireCoordinator();\n let voiceHandoff: VoiceQuestionHandoffHandle | undefined;\n const resetVoiceHandoff = (): void => {\n voiceHandoff?.dispose();\n voiceHandoff = undefined;\n };\n\n registerAskUserQuestionTool(pi, {\n enqueue: (runner, signal) => coordinator.enqueue(runner, signal),\n runTui: async (...args) => {\n const { runTuiQuestionnaire } = await import('../tui/runQuestionnaire.js');\n return runTuiQuestionnaire(...args);\n },\n tryVoice: (params) => voiceHandoff?.handoff(params),\n });\n registerAskUserQuestionReconciler(pi);\n\n pi.on('session_start', (_event, context) => {\n coordinator.shutdown();\n coordinator = new QuestionnaireCoordinator();\n resetVoiceHandoff();\n\n voiceHandoff = createVoiceQuestionHandoff({\n bus: pi.events,\n source: PACKAGE_SOURCE,\n sessionId: context.sessionManager.getSessionId(),\n });\n });\n pi.on('session_shutdown', () => {\n coordinator.shutdown();\n resetVoiceHandoff();\n });\n}\n\nexport { registerUserFeedbackExtension as default };\n"],"mappings":"2VAMA,MAAM,EAAiB,kCACjB,EAAoB,OAAO,IAAI,GAAG,EAAe,iBAAiB,EAMxE,SAAS,EAAsB,EAA2B,CACxD,IAAM,EAAW,WACX,EAAiB,EAAS,IAAsB,IAAI,QAI1D,MAHA,GAAS,GAAqB,EAC9B,CAAI,EAAe,IAAI,CAAE,IACzB,EAAe,IAAI,CAAE,EACd,GACT,CAEA,SAAgB,EAA8B,EAAwB,CACpE,GAAI,CAAC,EAAsB,CAAE,EAAG,OAEhC,IAAI,EAAc,IAAI,EAClB,EACE,MAAgC,CACpC,GAAc,QAAQ,EACtB,EAAe,IAAA,EACjB,EAEA,EAA4B,EAAI,CAC9B,SAAU,EAAQ,IAAW,EAAY,QAAQ,EAAQ,CAAM,EAC/D,OAAQ,MAAO,GAAG,IAAS,CACzB,GAAM,CAAE,uBAAwB,MAAM,OAAO,+BAC7C,OAAO,EAAoB,GAAG,CAAI,CACpC,EACA,SAAW,GAAW,GAAc,QAAQ,CAAM,CACpD,CAAC,EACD,EAAkC,CAAE,EAEpC,EAAG,GAAG,iBAAkB,EAAQ,IAAY,CAC1C,EAAY,SAAS,EACrB,EAAc,IAAI,EAClB,EAAkB,EAElB,EAAe,EAA2B,CACxC,IAAK,EAAG,OACR,OAAQ,EACR,UAAW,EAAQ,eAAe,aAAa,CACjD,CAAC,CACH,CAAC,EACD,EAAG,GAAG,uBAA0B,CAC9B,EAAY,SAAS,EACrB,EAAkB,CACpB,CAAC,CACH"}
1
+ {"version":3,"file":"pi.mjs","names":[],"sources":["../../src/extensions/pi.ts"],"sourcesContent":["import { DOOM_NARRATION_SERVICE, requireDoomNarrationService } from '@agimon-ai/doompi-core/narration';\nimport { definePiExtension, definePiTool } from '@agimon-ai/doompi-core/pi-extension';\nimport { type DoomToolRestriction } from '@agimon-ai/doompi-core/tool-surface';\nimport { DOOM_MINOR_MODE_CATALOG_SERVICE, requireMinorModeCatalog } from '@agimon-ai/doompi-minor-mode';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionContext } from '@earendil-works/pi-coding-agent';\n\nimport { PACKAGE_SOURCE } from '../constants/package';\nimport { ASK_USER_QUESTION_TOOL_NAME } from '../constants/tool';\nimport { askUserToolRestriction } from '../services/askUserToolGate';\nimport { isAutonomousVoiceActive } from '../services/autonomousVoiceMode';\nimport { QuestionnaireCoordinator, type QuestionnaireRunner } from '../services/questionnaireCoordinator';\nimport { createVoiceQuestionHandoff, type VoiceQuestionHandoff } from '../services/voiceQuestionHandoff';\nimport { createAskUserQuestionTool } from '../tools/askUserQuestion';\nimport { askUserToolRender } from '../tui/askUserToolRender';\nimport type { QuestionnaireResult } from '../types/questionnaire';\n\nfunction cancelledResult(): QuestionnaireResult {\n return { answers: [], cancelled: true };\n}\n\nexport const userFeedbackExtension = definePiExtension(PACKAGE_SOURCE, ({ context: cordis }) => {\n let active = true;\n let sessionGeneration = 0;\n let sessionId: string | undefined;\n let sessionReady = false;\n let coordinator = new QuestionnaireCoordinator();\n let voiceHandoff: VoiceQuestionHandoff | undefined;\n let voiceActive = false;\n let hasUI = true;\n const restrictionListeners = new Set<() => void>();\n const currentRestriction = (): DoomToolRestriction =>\n askUserToolRestriction(ASK_USER_QUESTION_TOOL_NAME, hasUI && !voiceActive);\n const syncToolRestriction = (): void => restrictionListeners.forEach((listener) => listener());\n let lifecycleQueue: Promise<void> = Promise.resolve();\n const pendingOperations = new Set<Promise<unknown>>();\n\n const trackOperation = <T>(operation: Promise<T>): Promise<T> => {\n pendingOperations.add(operation);\n void operation.then(\n () => pendingOperations.delete(operation),\n () => pendingOperations.delete(operation),\n );\n return operation;\n };\n const ownsGeneration = (generation: number, expectedSessionId: string | undefined): boolean =>\n active && generation === sessionGeneration && expectedSessionId === sessionId;\n const isContextActive = (context: ExtensionContext, signal?: AbortSignal): boolean =>\n active && sessionReady && signal?.aborted !== true && context.sessionManager.getSessionId() === sessionId;\n\n const shutdownRuntime = async (): Promise<void> => {\n if (!active) return;\n const ownedCoordinator = coordinator;\n ownedCoordinator.shutdown();\n active = false;\n sessionGeneration += 1;\n sessionReady = false;\n sessionId = undefined;\n await Promise.allSettled(pendingOperations);\n await ownedCoordinator.waitForIdle();\n };\n\n const toolDependencies: Parameters<typeof createAskUserQuestionTool>[1] = {\n enqueue: (runner: QuestionnaireRunner, signal?: AbortSignal) => {\n const ownedCoordinator = coordinator;\n const generation = sessionGeneration;\n const expectedSessionId = sessionId;\n if (!sessionReady || !ownsGeneration(generation, expectedSessionId) || signal?.aborted) {\n return Promise.resolve(cancelledResult());\n }\n return ownedCoordinator.enqueue(async (context) => {\n if (!sessionReady || !ownsGeneration(generation, expectedSessionId) || context.signal.aborted) {\n return cancelledResult();\n }\n const result = await runner({\n ...context,\n reportProgress: (progress) => {\n if (ownsGeneration(generation, expectedSessionId) && !context.signal.aborted) {\n context.reportProgress(progress);\n }\n },\n });\n return sessionReady && ownsGeneration(generation, expectedSessionId) && !context.signal.aborted\n ? result\n : cancelledResult();\n }, signal);\n },\n isActive: isContextActive,\n runTui: async (context, params, collapseKey, signal, reportProgress) => {\n if (!isContextActive(context, signal)) return undefined;\n const { runTuiQuestionnaire } = await import('../tui/runQuestionnaire');\n if (!isContextActive(context, signal)) return undefined;\n const result = await runTuiQuestionnaire(context, params, collapseKey, signal, reportProgress);\n return isContextActive(context, signal) ? result : undefined;\n },\n tryVoice: (params) => (active && sessionReady ? voiceHandoff?.handoff(params) : undefined),\n };\n\n const beforeAgentStart = (_event: unknown, context: ExtensionContext) => {\n if (!isContextActive(context)) return;\n hasUI = context.hasUI;\n syncToolRestriction();\n };\n\n const sessionStart = (_event: unknown, context: ExtensionContext) => {\n if (!active) return;\n const previousCoordinator = coordinator;\n previousCoordinator.shutdown();\n const activeSessionId = context.sessionManager.getSessionId();\n const generation = ++sessionGeneration;\n const nextCoordinator = new QuestionnaireCoordinator();\n coordinator = nextCoordinator;\n sessionId = activeSessionId;\n sessionReady = false;\n\n const initializeSession = async (): Promise<void> => {\n await previousCoordinator.waitForIdle();\n if (!ownsGeneration(generation, activeSessionId)) return;\n sessionReady = true;\n // The surface is re-registered per session from the gate state in force, so a session\n // that starts with Voice already active needs nothing extra here.\n };\n\n const operation = lifecycleQueue.then(initializeSession, initializeSession);\n lifecycleQueue = operation.catch(() => undefined);\n return trackOperation(operation);\n };\n return {\n services: [\n (serviceContextOwner: Context) => {\n serviceContextOwner.inject([DOOM_MINOR_MODE_CATALOG_SERVICE, DOOM_NARRATION_SERVICE], (serviceContext) => {\n const handoff = createVoiceQuestionHandoff(\n requireMinorModeCatalog(serviceContext),\n requireDoomNarrationService(serviceContext),\n );\n voiceHandoff = handoff;\n return () => {\n if (voiceHandoff === handoff) voiceHandoff = undefined;\n };\n });\n\n // Separate from the handoff binding above: the gate needs only the mode catalog, so it\n // still applies to a Voice build that publishes modes without providing narration.\n serviceContextOwner.inject([DOOM_MINOR_MODE_CATALOG_SERVICE], (modeContext) => {\n const modes = requireMinorModeCatalog(modeContext);\n const apply = (): void => {\n voiceActive = isAutonomousVoiceActive(modes.list());\n syncToolRestriction();\n };\n const unsubscribe = modes.subscribe(apply);\n apply();\n return () => {\n unsubscribe();\n voiceActive = false;\n syncToolRestriction();\n };\n });\n },\n ],\n tools: [definePiTool(createAskUserQuestionTool(cordis, toolDependencies, askUserToolRender))],\n events: { before_agent_start: beforeAgentStart, session_start: sessionStart },\n onDispose: shutdownRuntime,\n toolRestrictions: [\n {\n source: PACKAGE_SOURCE,\n get restrict() {\n return currentRestriction();\n },\n subscribe(listener) {\n restrictionListeners.add(listener);\n return () => {\n restrictionListeners.delete(listener);\n };\n },\n },\n ],\n };\n});\nexport default userFeedbackExtension;\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../constants/package.cjs"),t=require("../services/questionnaireCoordinator/index.cjs"),n=require("../tools/askUserHeadless.cjs"),r=(0,require("@agimon-ai/doompi-core/server-facet").defineServerPlugin)({name:e.PACKAGE_SOURCE,session:()=>{let e=new t.QuestionnaireCoordinator;return{tools:[n.createAskUserHeadlessTool(e)],hooks:[{event:`session_shutdown`,handle:()=>e.shutdown()}],onDispose:()=>e.shutdown()}}});exports.default=r,exports.userFeedbackServerFacet=r;
2
+ //# sourceMappingURL=server.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.cjs","names":["defineServerPlugin","PACKAGE_SOURCE","QuestionnaireCoordinator","createAskUserHeadlessTool"],"sources":["../../src/extensions/server.ts"],"sourcesContent":["import { defineServerPlugin } from '@agimon-ai/doompi-core/server-facet';\n\nimport { PACKAGE_SOURCE } from '../constants/package';\nimport { QuestionnaireCoordinator } from '../services/questionnaireCoordinator';\nimport { createAskUserHeadlessTool } from '../tools/askUserHeadless';\nexport const userFeedbackServerFacet = defineServerPlugin({\n name: PACKAGE_SOURCE,\n session: () => {\n const coordinator = new QuestionnaireCoordinator();\n return {\n tools: [createAskUserHeadlessTool(coordinator)],\n hooks: [{ event: 'session_shutdown', handle: () => coordinator.shutdown() }],\n onDispose: () => coordinator.shutdown(),\n };\n },\n});\nexport default userFeedbackServerFacet;\n"],"mappings":"iPAKa,GAAA,+CAA0BA,CAAAA,CAAAA,mBAAAA,CAAmB,CACxD,KAAMC,EAAAA,eACN,YAAe,CACb,IAAM,EAAc,IAAIC,EAAAA,yBACxB,MAAO,CACL,MAAO,CAACC,EAAAA,0BAA0B,CAAW,CAAC,EAC9C,MAAO,CAAC,CAAE,MAAO,mBAAoB,WAAc,EAAY,SAAS,CAAE,CAAC,EAC3E,cAAiB,EAAY,SAAS,CACxC,CACF,CACF,CAAC"}
@@ -0,0 +1,5 @@
1
+ //#region src/extensions/server.d.ts
2
+ export declare const userFeedbackServerFacet: import("@agimon-ai/doompi-core/server-facet").DoomServerFacet;
3
+ //#endregion
4
+ export { userFeedbackServerFacet as default };
5
+ //# sourceMappingURL=server.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.cts","names":[],"sources":["../../src/extensions/server.ts"],"mappings":";qBAKa,uEAAuB"}
@@ -0,0 +1,5 @@
1
+ //#region src/extensions/server.d.ts
2
+ export declare const userFeedbackServerFacet: import("@agimon-ai/doompi-core/server-facet").DoomServerFacet;
3
+ //#endregion
4
+ export { userFeedbackServerFacet as default };
5
+ //# sourceMappingURL=server.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.mts","names":[],"sources":["../../src/extensions/server.ts"],"mappings":";qBAKa,uEAAuB"}
@@ -0,0 +1,2 @@
1
+ import{PACKAGE_SOURCE as e}from"../constants/package.mjs";import{QuestionnaireCoordinator as t}from"../services/questionnaireCoordinator/index.mjs";import{createAskUserHeadlessTool as n}from"../tools/askUserHeadless.mjs";import{defineServerPlugin as r}from"@agimon-ai/doompi-core/server-facet";const i=r({name:e,session:()=>{let e=new t;return{tools:[n(e)],hooks:[{event:`session_shutdown`,handle:()=>e.shutdown()}],onDispose:()=>e.shutdown()}}});export{i as default,i as userFeedbackServerFacet};
2
+ //# sourceMappingURL=server.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.mjs","names":[],"sources":["../../src/extensions/server.ts"],"sourcesContent":["import { defineServerPlugin } from '@agimon-ai/doompi-core/server-facet';\n\nimport { PACKAGE_SOURCE } from '../constants/package';\nimport { QuestionnaireCoordinator } from '../services/questionnaireCoordinator';\nimport { createAskUserHeadlessTool } from '../tools/askUserHeadless';\nexport const userFeedbackServerFacet = defineServerPlugin({\n name: PACKAGE_SOURCE,\n session: () => {\n const coordinator = new QuestionnaireCoordinator();\n return {\n tools: [createAskUserHeadlessTool(coordinator)],\n hooks: [{ event: 'session_shutdown', handle: () => coordinator.shutdown() }],\n onDispose: () => coordinator.shutdown(),\n };\n },\n});\nexport default userFeedbackServerFacet;\n"],"mappings":"sSAKA,MAAa,EAA0B,EAAmB,CACxD,KAAM,EACN,YAAe,CACb,IAAM,EAAc,IAAI,EACxB,MAAO,CACL,MAAO,CAAC,EAA0B,CAAW,CAAC,EAC9C,MAAO,CAAC,CAAE,MAAO,mBAAoB,WAAc,EAAY,SAAS,CAAE,CAAC,EAC3E,cAAiB,EAAY,SAAS,CACxC,CACF,CACF,CAAC"}
package/dist/index.cjs CHANGED
@@ -1 +0,0 @@
1
- Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("./extensions/pi.cjs");require("./events.cjs");let t=require("@agimon-ai/doompi-extension-contracts");Object.defineProperty(exports,"ASK_USER_BLOCKED_EVENT",{enumerable:!0,get:function(){return t.ASK_USER_BLOCKED_EVENT}}),Object.defineProperty(exports,"ASK_USER_PROMPT_EVENT",{enumerable:!0,get:function(){return t.ASK_USER_PROMPT_EVENT}}),exports.default=e.default,exports.registerUserFeedbackExtension=e.default;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,3 @@
1
- import { ASK_USER_BLOCKED_EVENT, ASK_USER_PROMPT_EVENT, AskUserBlockedEventPayload, AskUserPromptEventPayload, AskUserPromptOption, AskUserPromptQuestion } from "./schemas/events.cjs";
2
- import "./events.cjs";
3
- import registerUserFeedbackExtension from "./extensions/pi.cjs";
4
- export { ASK_USER_BLOCKED_EVENT, ASK_USER_PROMPT_EVENT, type AskUserBlockedEventPayload, type AskUserPromptEventPayload, type AskUserPromptOption, type AskUserPromptQuestion, registerUserFeedbackExtension as default, registerUserFeedbackExtension };
1
+ import { QuestionParams } from "./schemas/questionnaire.cjs";
2
+ import { QuestionnaireResult, ToolTextResult } from "./types/questionnaire.cjs";
3
+ export type { QuestionParams, QuestionnaireResult, ToolTextResult };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,3 @@
1
- import { ASK_USER_BLOCKED_EVENT, ASK_USER_PROMPT_EVENT, AskUserBlockedEventPayload, AskUserPromptEventPayload, AskUserPromptOption, AskUserPromptQuestion } from "./schemas/events.mjs";
2
- import "./events.mjs";
3
- import registerUserFeedbackExtension from "./extensions/pi.mjs";
4
- export { ASK_USER_BLOCKED_EVENT, ASK_USER_PROMPT_EVENT, type AskUserBlockedEventPayload, type AskUserPromptEventPayload, type AskUserPromptOption, type AskUserPromptQuestion, registerUserFeedbackExtension as default, registerUserFeedbackExtension };
1
+ import { QuestionParams } from "./schemas/questionnaire.mjs";
2
+ import { QuestionnaireResult, ToolTextResult } from "./types/questionnaire.mjs";
3
+ export type { QuestionParams, QuestionnaireResult, ToolTextResult };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import e from"./extensions/pi.mjs";import{ASK_USER_BLOCKED_EVENT as t,ASK_USER_PROMPT_EVENT as n}from"./schemas/events.mjs";import"./events.mjs";export{t as ASK_USER_BLOCKED_EVENT,n as ASK_USER_PROMPT_EVENT,e as default,e as registerUserFeedbackExtension};
1
+ export{};
@@ -0,0 +1,2 @@
1
+ const e=(0,require("@agimon-ai/doompi-core/api-contracts").defineApiContract)({version:1,http:[],sockets:[],dynamic:[]});exports.apiContracts=e,exports.default=e;
2
+ //# sourceMappingURL=apiContracts.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiContracts.cjs","names":["defineApiContract"],"sources":["../../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract } from '@agimon-ai/doompi-core/api-contracts';\n\n/** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */\nexport const apiContracts = defineApiContract({ version: 1, http: [], sockets: [], dynamic: [] });\nexport default apiContracts;\n"],"mappings":"AAGA,MAAa,GAAA,gDAAeA,CAAAA,CAAAA,kBAAAA,CAAkB,CAAE,QAAS,EAAG,KAAM,CAAC,EAAG,QAAS,CAAC,EAAG,QAAS,CAAC,CAAE,CAAC"}
@@ -0,0 +1,53 @@
1
+ //#region src/schemas/apiContracts.d.ts
2
+ /** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */
3
+ export declare const apiContracts: {
4
+ version: 1;
5
+ protocols?: Record<string, string> | undefined;
6
+ schemas?: Record<string, boolean | object> | undefined;
7
+ http: {
8
+ id: string;
9
+ scope: "global" | "session" | "workspace";
10
+ basePath?: string | undefined;
11
+ path: string;
12
+ method: "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT";
13
+ description: string;
14
+ availability?: string | undefined;
15
+ authentication: "device" | "none" | "owner";
16
+ parameters?: {
17
+ name: string;
18
+ in: "header" | "path" | "query";
19
+ required: boolean;
20
+ schema: boolean | object;
21
+ }[] | undefined;
22
+ body?: {
23
+ contentType: string;
24
+ contentTypes?: string[] | undefined;
25
+ required: boolean;
26
+ schema: boolean | object;
27
+ } | undefined;
28
+ responses: Record<string, {
29
+ description: string;
30
+ contentType?: string | undefined;
31
+ schema?: boolean | object | undefined;
32
+ events?: Record<string, boolean | object> | undefined;
33
+ }>;
34
+ }[];
35
+ sockets: {
36
+ id: string;
37
+ scope: "global" | "session" | "workspace";
38
+ description: string;
39
+ availability?: string | undefined;
40
+ service: string;
41
+ member: string;
42
+ direction: "client-to-server" | "server-to-client";
43
+ kind: "channel" | "method" | "state";
44
+ input: boolean | object;
45
+ output?: boolean | object | undefined;
46
+ errors?: boolean | object | undefined;
47
+ }[];
48
+ dynamic: string[];
49
+ gaps?: string[] | undefined;
50
+ };
51
+ //#endregion
52
+ export { apiContracts as default };
53
+ //# sourceMappingURL=apiContracts.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiContracts.d.cts","names":[],"sources":["../../src/schemas/apiContracts.ts"],"mappings":";;qBAGa"}
@@ -0,0 +1,53 @@
1
+ //#region src/schemas/apiContracts.d.ts
2
+ /** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */
3
+ export declare const apiContracts: {
4
+ version: 1;
5
+ protocols?: Record<string, string> | undefined;
6
+ schemas?: Record<string, boolean | object> | undefined;
7
+ http: {
8
+ id: string;
9
+ scope: "global" | "session" | "workspace";
10
+ basePath?: string | undefined;
11
+ path: string;
12
+ method: "DELETE" | "GET" | "HEAD" | "OPTIONS" | "PATCH" | "POST" | "PUT";
13
+ description: string;
14
+ availability?: string | undefined;
15
+ authentication: "device" | "none" | "owner";
16
+ parameters?: {
17
+ name: string;
18
+ in: "header" | "path" | "query";
19
+ required: boolean;
20
+ schema: boolean | object;
21
+ }[] | undefined;
22
+ body?: {
23
+ contentType: string;
24
+ contentTypes?: string[] | undefined;
25
+ required: boolean;
26
+ schema: boolean | object;
27
+ } | undefined;
28
+ responses: Record<string, {
29
+ description: string;
30
+ contentType?: string | undefined;
31
+ schema?: boolean | object | undefined;
32
+ events?: Record<string, boolean | object> | undefined;
33
+ }>;
34
+ }[];
35
+ sockets: {
36
+ id: string;
37
+ scope: "global" | "session" | "workspace";
38
+ description: string;
39
+ availability?: string | undefined;
40
+ service: string;
41
+ member: string;
42
+ direction: "client-to-server" | "server-to-client";
43
+ kind: "channel" | "method" | "state";
44
+ input: boolean | object;
45
+ output?: boolean | object | undefined;
46
+ errors?: boolean | object | undefined;
47
+ }[];
48
+ dynamic: string[];
49
+ gaps?: string[] | undefined;
50
+ };
51
+ //#endregion
52
+ export { apiContracts as default };
53
+ //# sourceMappingURL=apiContracts.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiContracts.d.mts","names":[],"sources":["../../src/schemas/apiContracts.ts"],"mappings":";;qBAGa"}
@@ -0,0 +1,2 @@
1
+ import{defineApiContract as e}from"@agimon-ai/doompi-core/api-contracts";const t=e({version:1,http:[],sockets:[],dynamic:[]});export{t as apiContracts,t as default};
2
+ //# sourceMappingURL=apiContracts.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiContracts.mjs","names":[],"sources":["../../src/schemas/apiContracts.ts"],"sourcesContent":["import { defineApiContract } from '@agimon-ai/doompi-core/api-contracts';\n\n/** This facet contributes agent tools, commands or lifecycle only, with no separate HTTP or socket surface. */\nexport const apiContracts = defineApiContract({ version: 1, http: [], sockets: [], dynamic: [] });\nexport default apiContracts;\n"],"mappings":"yEAGA,MAAa,EAAe,EAAkB,CAAE,QAAS,EAAG,KAAM,CAAC,EAAG,QAAS,CAAC,EAAG,QAAS,CAAC,CAAE,CAAC"}
@@ -1,2 +1,2 @@
1
- let e=require("typebox");const t=[`Other`,`Type something.`,`Next`],n=e.Type.Object({label:e.Type.String({maxLength:60,description:`MAX 60 CHARACTERS hard limit. Concise option label shown to the user, ideally 1-5 words.`}),description:e.Type.String({description:`Explanation of what the option means and its important trade-offs.`}),preview:e.Type.Optional(e.Type.String({description:`Optional markdown preview rendered while this option is focused.`}))},{additionalProperties:!1}),r=e.Type.Object({question:e.Type.String({description:`The complete, clear question to ask the user.`}),header:e.Type.String({maxLength:16,description:`MAX 16 CHARACTERS short label used when navigating several questions.`}),options:e.Type.Array(n,{minItems:2,maxItems:4,description:`The 2-4 choices offered for this question.`}),multiSelect:e.Type.Optional(e.Type.Boolean({description:`Allow more than one option to be selected.`,default:!1}))},{additionalProperties:!1}),i=e.Type.Object({questions:e.Type.Array(r,{minItems:1,maxItems:4,description:`Questions to ask the user (1-4 questions).`})},{additionalProperties:!1});exports.OptionSchema=n,exports.QuestionParamsSchema=i,exports.QuestionSchema=r,exports.RESERVED_LABELS=t;
1
+ let e=require("typebox");const t=e.Type.Object({label:e.Type.String({maxLength:60,description:`MAX 60 CHARACTERS, hard limit. Concise option label shown to the user, ideally 1-5 words.`}),description:e.Type.String({description:`Explanation of what the option means and its important trade-offs.`}),preview:e.Type.Optional(e.Type.String({description:`Optional markdown preview rendered while this option is focused.`}))},{additionalProperties:!1}),n=e.Type.Object({question:e.Type.String({description:`The complete, clear question to ask the user.`}),header:e.Type.String({maxLength:16,description:`MAX 16 CHARACTERS, short label used when navigating several questions.`}),options:e.Type.Array(t,{minItems:2,maxItems:4,description:`The 2-4 choices offered for this question.`}),multiSelect:e.Type.Optional(e.Type.Boolean({description:`Allow more than one option to be selected.`,default:!1}))},{additionalProperties:!1}),r=e.Type.Object({questions:e.Type.Array(n,{minItems:1,maxItems:4,description:`Questions to ask the user (1-4 questions).`})},{additionalProperties:!1});exports.OptionSchema=t,exports.QuestionParamsSchema=r,exports.QuestionSchema=n;
2
2
  //# sourceMappingURL=questionnaire.cjs.map