@agimon-ai/doompi-minor-mode 0.0.1-alpha.70

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 (155) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/dist/api-contracts.cjs +7 -0
  4. package/dist/api-contracts.d.cts +2 -0
  5. package/dist/api-contracts.d.mts +2 -0
  6. package/dist/api-contracts.mjs +2 -0
  7. package/dist/catalog.cjs +223 -0
  8. package/dist/catalog.cjs.map +1 -0
  9. package/dist/catalog.d.cts +14 -0
  10. package/dist/catalog.d.cts.map +1 -0
  11. package/dist/catalog.d.mts +14 -0
  12. package/dist/catalog.d.mts.map +1 -0
  13. package/dist/catalog.mjs +222 -0
  14. package/dist/catalog.mjs.map +1 -0
  15. package/dist/command.cjs +134 -0
  16. package/dist/command.cjs.map +1 -0
  17. package/dist/command.d.cts +25 -0
  18. package/dist/command.d.cts.map +1 -0
  19. package/dist/command.d.mts +25 -0
  20. package/dist/command.d.mts.map +1 -0
  21. package/dist/command.mjs +129 -0
  22. package/dist/command.mjs.map +1 -0
  23. package/dist/constants/command.cjs +6 -0
  24. package/dist/constants/command.cjs.map +1 -0
  25. package/dist/constants/command.d.cts +4 -0
  26. package/dist/constants/command.d.cts.map +1 -0
  27. package/dist/constants/command.d.mts +4 -0
  28. package/dist/constants/command.d.mts.map +1 -0
  29. package/dist/constants/command.mjs +6 -0
  30. package/dist/constants/command.mjs.map +1 -0
  31. package/dist/controllers/catalogRuntime.cjs +123 -0
  32. package/dist/controllers/catalogRuntime.cjs.map +1 -0
  33. package/dist/controllers/catalogRuntime.mjs +123 -0
  34. package/dist/controllers/catalogRuntime.mjs.map +1 -0
  35. package/dist/extensions/pi.cjs +7 -0
  36. package/dist/extensions/pi.cjs.map +1 -0
  37. package/dist/extensions/pi.d.cts +4 -0
  38. package/dist/extensions/pi.d.cts.map +1 -0
  39. package/dist/extensions/pi.d.mts +5 -0
  40. package/dist/extensions/pi.d.mts.map +1 -0
  41. package/dist/extensions/pi.mjs +8 -0
  42. package/dist/extensions/pi.mjs.map +1 -0
  43. package/dist/extensions/server.cjs +85 -0
  44. package/dist/extensions/server.cjs.map +1 -0
  45. package/dist/extensions/server.d.cts +4 -0
  46. package/dist/extensions/server.d.cts.map +1 -0
  47. package/dist/extensions/server.d.mts +5 -0
  48. package/dist/extensions/server.d.mts.map +1 -0
  49. package/dist/extensions/server.mjs +86 -0
  50. package/dist/extensions/server.mjs.map +1 -0
  51. package/dist/index.cjs +53 -0
  52. package/dist/index.d.cts +10 -0
  53. package/dist/index.d.mts +10 -0
  54. package/dist/index.mjs +9 -0
  55. package/dist/minor-mode-command.cjs +37 -0
  56. package/dist/minor-mode-command.cjs.map +1 -0
  57. package/dist/minor-mode-command.d.cts +9 -0
  58. package/dist/minor-mode-command.d.cts.map +1 -0
  59. package/dist/minor-mode-command.d.mts +9 -0
  60. package/dist/minor-mode-command.d.mts.map +1 -0
  61. package/dist/minor-mode-command.mjs +33 -0
  62. package/dist/minor-mode-command.mjs.map +1 -0
  63. package/dist/mode-definition.cjs +47 -0
  64. package/dist/mode-definition.cjs.map +1 -0
  65. package/dist/mode-definition.d.cts +30 -0
  66. package/dist/mode-definition.d.cts.map +1 -0
  67. package/dist/mode-definition.d.mts +30 -0
  68. package/dist/mode-definition.d.mts.map +1 -0
  69. package/dist/mode-definition.mjs +46 -0
  70. package/dist/mode-definition.mjs.map +1 -0
  71. package/dist/mode.cjs +275 -0
  72. package/dist/mode.cjs.map +1 -0
  73. package/dist/mode.d.cts +491 -0
  74. package/dist/mode.d.cts.map +1 -0
  75. package/dist/mode.d.mts +491 -0
  76. package/dist/mode.d.mts.map +1 -0
  77. package/dist/mode.mjs +241 -0
  78. package/dist/mode.mjs.map +1 -0
  79. package/dist/owner.cjs +10 -0
  80. package/dist/owner.cjs.map +1 -0
  81. package/dist/owner.d.cts +7 -0
  82. package/dist/owner.d.cts.map +1 -0
  83. package/dist/owner.d.mts +7 -0
  84. package/dist/owner.d.mts.map +1 -0
  85. package/dist/owner.mjs +9 -0
  86. package/dist/owner.mjs.map +1 -0
  87. package/dist/projection.cjs +49 -0
  88. package/dist/projection.cjs.map +1 -0
  89. package/dist/projection.d.cts +12 -0
  90. package/dist/projection.d.cts.map +1 -0
  91. package/dist/projection.d.mts +12 -0
  92. package/dist/projection.d.mts.map +1 -0
  93. package/dist/projection.mjs +47 -0
  94. package/dist/projection.mjs.map +1 -0
  95. package/dist/reload-handoff.cjs +57 -0
  96. package/dist/reload-handoff.cjs.map +1 -0
  97. package/dist/reload-handoff.d.cts +11 -0
  98. package/dist/reload-handoff.d.cts.map +1 -0
  99. package/dist/reload-handoff.d.mts +11 -0
  100. package/dist/reload-handoff.d.mts.map +1 -0
  101. package/dist/reload-handoff.mjs +54 -0
  102. package/dist/reload-handoff.mjs.map +1 -0
  103. package/dist/schemas/apiContracts.cjs +13 -0
  104. package/dist/schemas/apiContracts.cjs.map +1 -0
  105. package/dist/schemas/apiContracts.d.cts +53 -0
  106. package/dist/schemas/apiContracts.d.cts.map +1 -0
  107. package/dist/schemas/apiContracts.d.mts +53 -0
  108. package/dist/schemas/apiContracts.d.mts.map +1 -0
  109. package/dist/schemas/apiContracts.mjs +13 -0
  110. package/dist/schemas/apiContracts.mjs.map +1 -0
  111. package/dist/schemas/headless.d.cts +16 -0
  112. package/dist/schemas/headless.d.cts.map +1 -0
  113. package/dist/schemas/headless.d.mts +16 -0
  114. package/dist/schemas/headless.d.mts.map +1 -0
  115. package/dist/services/client/index.cjs +18 -0
  116. package/dist/services/client/index.cjs.map +1 -0
  117. package/dist/services/client/index.d.cts +12 -0
  118. package/dist/services/client/index.d.cts.map +1 -0
  119. package/dist/services/client/index.d.mts +12 -0
  120. package/dist/services/client/index.d.mts.map +1 -0
  121. package/dist/services/client/index.mjs +18 -0
  122. package/dist/services/client/index.mjs.map +1 -0
  123. package/dist/services/headlessCommand/index.cjs +53 -0
  124. package/dist/services/headlessCommand/index.cjs.map +1 -0
  125. package/dist/services/headlessCommand/index.d.cts +6 -0
  126. package/dist/services/headlessCommand/index.d.cts.map +1 -0
  127. package/dist/services/headlessCommand/index.d.mts +6 -0
  128. package/dist/services/headlessCommand/index.d.mts.map +1 -0
  129. package/dist/services/headlessCommand/index.mjs +53 -0
  130. package/dist/services/headlessCommand/index.mjs.map +1 -0
  131. package/dist/services/piRegistration/index.cjs +86 -0
  132. package/dist/services/piRegistration/index.cjs.map +1 -0
  133. package/dist/services/piRegistration/index.d.cts +15 -0
  134. package/dist/services/piRegistration/index.d.cts.map +1 -0
  135. package/dist/services/piRegistration/index.d.mts +15 -0
  136. package/dist/services/piRegistration/index.d.mts.map +1 -0
  137. package/dist/services/piRegistration/index.mjs +86 -0
  138. package/dist/services/piRegistration/index.mjs.map +1 -0
  139. package/dist/services/serverRegistration/index.cjs +35 -0
  140. package/dist/services/serverRegistration/index.cjs.map +1 -0
  141. package/dist/services/serverRegistration/index.d.cts +7 -0
  142. package/dist/services/serverRegistration/index.d.cts.map +1 -0
  143. package/dist/services/serverRegistration/index.d.mts +7 -0
  144. package/dist/services/serverRegistration/index.d.mts.map +1 -0
  145. package/dist/services/serverRegistration/index.mjs +35 -0
  146. package/dist/services/serverRegistration/index.mjs.map +1 -0
  147. package/dist/services/validation/index.cjs +84 -0
  148. package/dist/services/validation/index.cjs.map +1 -0
  149. package/dist/services/validation/index.d.cts +8 -0
  150. package/dist/services/validation/index.d.cts.map +1 -0
  151. package/dist/services/validation/index.d.mts +8 -0
  152. package/dist/services/validation/index.d.mts.map +1 -0
  153. package/dist/services/validation/index.mjs +81 -0
  154. package/dist/services/validation/index.mjs.map +1 -0
  155. package/package.json +157 -0
@@ -0,0 +1,134 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_command = require("./constants/command.cjs");
3
+ //#region src/services/command/index.ts
4
+ const MINOR_MODE_COMMAND = "minor";
5
+ const REQUESTER_SOURCE = "@agimon-ai/doompi/mode-catalog/ui-command";
6
+ function isOn(record) {
7
+ return record.state.activation === "active" || record.state.activation === "deactivating";
8
+ }
9
+ /** A catalog mode addressed the way a person types it: id, label, or id stem. */
10
+ function matchMinorMode(records, query) {
11
+ const needle = query.toLowerCase();
12
+ return records.find((record) => record.descriptor.id.toLowerCase() === needle || record.descriptor.label.toLowerCase() === needle || record.descriptor.id.toLowerCase().split(".")[0] === needle);
13
+ }
14
+ /** The actions a session of this kind can currently take on a mode. */
15
+ function actionsFor(record, kind) {
16
+ return record.descriptor.actions.filter((action) => {
17
+ if (!action.contexts.includes(kind)) return false;
18
+ return record.state.actions.find((entry) => entry.id === action.id)?.enabled !== false;
19
+ });
20
+ }
21
+ async function promptArguments(ui, action) {
22
+ const collected = {};
23
+ for (const parameter of action.parameters) {
24
+ if (!parameter.required) continue;
25
+ const title = `${action.label}: ${parameter.label}`;
26
+ if (parameter.kind === "boolean") {
27
+ const value = await ui.confirm(title, parameter.description ?? "");
28
+ if (value === void 0) return void 0;
29
+ collected[parameter.name] = value;
30
+ continue;
31
+ }
32
+ if (parameter.kind === "enum") {
33
+ const options = parameter.choices.map((choice) => choice.label);
34
+ const picked = await ui.select(title, options);
35
+ const choice = picked === void 0 ? void 0 : parameter.choices[options.indexOf(picked)];
36
+ if (!choice) return void 0;
37
+ collected[parameter.name] = choice.value;
38
+ continue;
39
+ }
40
+ const raw = await ui.input(title, parameter.description ?? "");
41
+ if (raw === void 0 || raw === "") return void 0;
42
+ if (parameter.kind === "number") {
43
+ const value = Number(raw);
44
+ if (!Number.isFinite(value)) {
45
+ await ui.notify(`${parameter.label} must be a number.`, "warning");
46
+ return;
47
+ }
48
+ collected[parameter.name] = value;
49
+ } else collected[parameter.name] = raw;
50
+ }
51
+ return collected;
52
+ }
53
+ /** Runs the catalog command against an explicit interaction surface. */
54
+ async function executeMinorModeCommand(args, options) {
55
+ const { catalog, kind, ui } = options;
56
+ if (!catalog) {
57
+ await ui.notify("Minor modes are unavailable in this session.", "warning");
58
+ return;
59
+ }
60
+ const records = catalog.list();
61
+ const [modeQuery, actionQuery] = args.trim().split(/\s+/).filter(Boolean);
62
+ let record;
63
+ if (modeQuery) {
64
+ record = matchMinorMode(records, modeQuery);
65
+ if (!record) {
66
+ const known = records.map((entry) => entry.descriptor.label.toLowerCase()).join(", ") || "none";
67
+ await ui.notify(`No minor mode matches "${modeQuery}". Available: ${known}.`, "warning");
68
+ return;
69
+ }
70
+ } else {
71
+ const listed = records.filter((entry) => actionsFor(entry, kind).length > 0);
72
+ if (listed.length === 0) {
73
+ await ui.notify("No minor modes are available in this session.", "info");
74
+ return;
75
+ }
76
+ const labels = listed.map((entry) => `${isOn(entry) ? "[x]" : "[ ]"} ${entry.descriptor.label}: ${entry.descriptor.description}`);
77
+ const on = listed.filter(isOn).length;
78
+ const picked = await ui.select(`Minor modes (${String(on)} on)`, labels);
79
+ if (picked === void 0) return;
80
+ record = listed[labels.indexOf(picked)];
81
+ if (!record) return;
82
+ }
83
+ const actions = actionsFor(record, kind);
84
+ if (actions.length === 0) {
85
+ const reasons = [...new Set(record.state.actions.map((entry) => entry.disabledReason).filter((reason) => reason !== void 0 && reason.length > 0))];
86
+ const because = reasons.length > 0 ? ` ${reasons.join(" ")}` : "";
87
+ await ui.notify(`${record.descriptor.label} has no actions available in this session.${because}`, "warning");
88
+ return;
89
+ }
90
+ let action;
91
+ if (actionQuery) {
92
+ action = actions.find((entry) => entry.id.toLowerCase() === actionQuery.toLowerCase());
93
+ if (!action) {
94
+ const known = actions.map((entry) => entry.id).join(", ");
95
+ await ui.notify(`No "${actionQuery}" action on ${record.descriptor.label}. Available: ${known}.`, "warning");
96
+ return;
97
+ }
98
+ } else if (actions.length === 1) action = actions[0];
99
+ else {
100
+ const labels = actions.map((entry) => `${entry.label}: ${entry.description}`);
101
+ const picked = await ui.select(`${record.descriptor.label} (${record.state.activation})`, labels);
102
+ if (picked === void 0) return;
103
+ action = actions[labels.indexOf(picked)];
104
+ }
105
+ if (!action) return;
106
+ const argumentsValue = await promptArguments(ui, action);
107
+ if (argumentsValue === void 0) return;
108
+ try {
109
+ const response = await catalog.invoke({
110
+ operationId: globalThis.crypto.randomUUID(),
111
+ mode: {
112
+ source: record.descriptor.source,
113
+ id: record.descriptor.id,
114
+ ownerGeneration: record.ownerGeneration,
115
+ registrationId: record.registrationId
116
+ },
117
+ actionId: action.id,
118
+ arguments: argumentsValue
119
+ }, REQUESTER_SOURCE);
120
+ const state = response.mode.state;
121
+ const detail = state.detail ? `: ${state.detail}` : "";
122
+ await ui.notify(response.message ?? `${record.descriptor.label} is ${state.activation}${detail}.`, "info");
123
+ } catch (error) {
124
+ await ui.notify(`${record.descriptor.label} ${action.label} failed: ${error instanceof Error ? error.message : String(error)}`, "error");
125
+ }
126
+ }
127
+ //#endregion
128
+ exports.MINOR_MODE_COMMAND = MINOR_MODE_COMMAND;
129
+ exports.MINOR_MODE_COMMAND_DESCRIPTION = require_command.MINOR_MODE_COMMAND_DESCRIPTION;
130
+ exports.actionsFor = actionsFor;
131
+ exports.executeMinorModeCommand = executeMinorModeCommand;
132
+ exports.matchMinorMode = matchMinorMode;
133
+
134
+ //# sourceMappingURL=command.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.cjs","names":[],"sources":["../src/services/command/index.ts"],"sourcesContent":["import type {\n MinorModeActionDescriptor,\n MinorModeArguments,\n MinorModeCatalogService,\n MinorModeRecord,\n MinorModeSessionKind,\n} from '../../schemas/mode';\n\nexport const MINOR_MODE_COMMAND = 'minor';\nexport { MINOR_MODE_COMMAND_DESCRIPTION } from '../../constants/command';\nconst REQUESTER_SOURCE = '@agimon-ai/doompi/mode-catalog/ui-command';\n\ntype NotificationLevel = 'info' | 'warning' | 'error';\n\nexport interface MinorModeCommandPrompter {\n select(title: string, options: readonly string[]): Promise<string | undefined>;\n input(title: string, message: string): Promise<string | undefined>;\n confirm(title: string, message: string): Promise<boolean | undefined>;\n notify(message: string, level: NotificationLevel): void | Promise<void>;\n}\n\nfunction isOn(record: MinorModeRecord): boolean {\n return record.state.activation === 'active' || record.state.activation === 'deactivating';\n}\n\n/** A catalog mode addressed the way a person types it: id, label, or id stem. */\nexport function matchMinorMode(records: readonly MinorModeRecord[], query: string): MinorModeRecord | undefined {\n const needle = query.toLowerCase();\n return records.find(\n (record) =>\n record.descriptor.id.toLowerCase() === needle ||\n record.descriptor.label.toLowerCase() === needle ||\n record.descriptor.id.toLowerCase().split('.')[0] === needle,\n );\n}\n\n/** The actions a session of this kind can currently take on a mode. */\nexport function actionsFor(record: MinorModeRecord, kind: MinorModeSessionKind): MinorModeActionDescriptor[] {\n return record.descriptor.actions.filter((action) => {\n if (!action.contexts.includes(kind)) return false;\n const availability = record.state.actions.find((entry) => entry.id === action.id);\n return availability?.enabled !== false;\n });\n}\n\nasync function promptArguments(\n ui: MinorModeCommandPrompter,\n action: MinorModeActionDescriptor,\n): Promise<MinorModeArguments | undefined> {\n const collected: MinorModeArguments = {};\n for (const parameter of action.parameters) {\n if (!parameter.required) continue;\n const title = `${action.label}: ${parameter.label}`;\n if (parameter.kind === 'boolean') {\n const value = await ui.confirm(title, parameter.description ?? '');\n if (value === undefined) return undefined;\n collected[parameter.name] = value;\n continue;\n }\n if (parameter.kind === 'enum') {\n const options = parameter.choices.map((choice) => choice.label);\n const picked = await ui.select(title, options);\n const choice = picked === undefined ? undefined : parameter.choices[options.indexOf(picked)];\n if (!choice) return undefined;\n collected[parameter.name] = choice.value;\n continue;\n }\n const raw = await ui.input(title, parameter.description ?? '');\n if (raw === undefined || raw === '') return undefined;\n if (parameter.kind === 'number') {\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n await ui.notify(`${parameter.label} must be a number.`, 'warning');\n return undefined;\n }\n collected[parameter.name] = value;\n } else {\n collected[parameter.name] = raw;\n }\n }\n return collected;\n}\n\nexport interface ExecuteMinorModeCommandOptions {\n catalog: MinorModeCatalogService | undefined;\n kind: MinorModeSessionKind;\n ui: MinorModeCommandPrompter;\n}\n\n/** Runs the catalog command against an explicit interaction surface. */\nexport async function executeMinorModeCommand(args: string, options: ExecuteMinorModeCommandOptions): Promise<void> {\n const { catalog, kind, ui } = options;\n if (!catalog) {\n await ui.notify('Minor modes are unavailable in this session.', 'warning');\n return;\n }\n\n const records = catalog.list();\n const [modeQuery, actionQuery] = args.trim().split(/\\s+/).filter(Boolean);\n\n let record: MinorModeRecord | undefined;\n if (modeQuery) {\n record = matchMinorMode(records, modeQuery);\n if (!record) {\n const known = records.map((entry) => entry.descriptor.label.toLowerCase()).join(', ') || 'none';\n await ui.notify(`No minor mode matches \"${modeQuery}\". Available: ${known}.`, 'warning');\n return;\n }\n } else {\n const listed = records.filter((entry) => actionsFor(entry, kind).length > 0);\n if (listed.length === 0) {\n await ui.notify('No minor modes are available in this session.', 'info');\n return;\n }\n const labels = listed.map(\n (entry) => `${isOn(entry) ? '[x]' : '[ ]'} ${entry.descriptor.label}: ${entry.descriptor.description}`,\n );\n const on = listed.filter(isOn).length;\n const picked = await ui.select(`Minor modes (${String(on)} on)`, labels);\n if (picked === undefined) return;\n record = listed[labels.indexOf(picked)];\n if (!record) return;\n }\n\n const actions = actionsFor(record, kind);\n if (actions.length === 0) {\n // The mode already said why each action is unavailable. Repeating that\n // is the difference between a dead end and an explanation: \"requires\n // an interactive session\" tells a cockpit user to reach for the TUI,\n // where the bare sentence leaves them guessing.\n const reasons = [\n ...new Set(\n record.state.actions\n .map((entry) => entry.disabledReason)\n .filter((reason): reason is string => reason !== undefined && reason.length > 0),\n ),\n ];\n const because = reasons.length > 0 ? ` ${reasons.join(' ')}` : '';\n await ui.notify(`${record.descriptor.label} has no actions available in this session.${because}`, 'warning');\n return;\n }\n\n let action: MinorModeActionDescriptor | undefined;\n if (actionQuery) {\n action = actions.find((entry) => entry.id.toLowerCase() === actionQuery.toLowerCase());\n if (!action) {\n const known = actions.map((entry) => entry.id).join(', ');\n await ui.notify(`No \"${actionQuery}\" action on ${record.descriptor.label}. Available: ${known}.`, 'warning');\n return;\n }\n } else if (actions.length === 1) {\n action = actions[0];\n } else {\n const labels = actions.map((entry) => `${entry.label}: ${entry.description}`);\n const picked = await ui.select(`${record.descriptor.label} (${record.state.activation})`, labels);\n if (picked === undefined) return;\n action = actions[labels.indexOf(picked)];\n }\n if (!action) return;\n\n const argumentsValue = await promptArguments(ui, action);\n if (argumentsValue === undefined) return;\n\n try {\n const response = await catalog.invoke(\n {\n operationId: globalThis.crypto.randomUUID(),\n mode: {\n source: record.descriptor.source,\n id: record.descriptor.id,\n ownerGeneration: record.ownerGeneration,\n registrationId: record.registrationId,\n },\n actionId: action.id,\n arguments: argumentsValue,\n },\n REQUESTER_SOURCE,\n );\n const state = response.mode.state;\n const detail = state.detail ? `: ${state.detail}` : '';\n await ui.notify(response.message ?? `${record.descriptor.label} is ${state.activation}${detail}.`, 'info');\n } catch (error) {\n await ui.notify(\n `${record.descriptor.label} ${action.label} failed: ${error instanceof Error ? error.message : String(error)}`,\n 'error',\n );\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { MinorModeActionDescriptor, MinorModeCatalogService, MinorModeRecord, MinorModeSessionKind } from "./mode.cjs";
2
+ import { MINOR_MODE_COMMAND_DESCRIPTION } from "./constants/command.cjs";
3
+ //#region src/services/command/index.d.ts
4
+ export declare const MINOR_MODE_COMMAND = "minor";
5
+ type NotificationLevel = 'info' | 'warning' | 'error';
6
+ export interface MinorModeCommandPrompter {
7
+ select(title: string, options: readonly string[]): Promise<string | undefined>;
8
+ input(title: string, message: string): Promise<string | undefined>;
9
+ confirm(title: string, message: string): Promise<boolean | undefined>;
10
+ notify(message: string, level: NotificationLevel): void | Promise<void>;
11
+ }
12
+ /** A catalog mode addressed the way a person types it: id, label, or id stem. */
13
+ export declare function matchMinorMode(records: readonly MinorModeRecord[], query: string): MinorModeRecord | undefined;
14
+ /** The actions a session of this kind can currently take on a mode. */
15
+ export declare function actionsFor(record: MinorModeRecord, kind: MinorModeSessionKind): MinorModeActionDescriptor[];
16
+ export interface ExecuteMinorModeCommandOptions {
17
+ catalog: MinorModeCatalogService | undefined;
18
+ kind: MinorModeSessionKind;
19
+ ui: MinorModeCommandPrompter;
20
+ }
21
+ /** Runs the catalog command against an explicit interaction surface. */
22
+ export declare function executeMinorModeCommand(args: string, options: ExecuteMinorModeCommandOptions): Promise<void>;
23
+ //#endregion
24
+ export { MINOR_MODE_COMMAND_DESCRIPTION };
25
+ //# sourceMappingURL=command.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.d.cts","names":[],"sources":["../src/services/command/index.ts"],"mappings":";;;qBAQa;KAIR;iBAEY;EACf,OAAO,eAAe,6BAA6B;EACnD,MAAM,eAAe,kBAAkB;EACvC,QAAQ,eAAe,kBAAkB;EACzC,OAAO,iBAAiB,OAAO,2BAA2B;;;wBAQ5C,eAAe,kBAAkB,mBAAmB,gBAAgB;;wBAWpE,WAAW,QAAQ,iBAAiB,MAAM,uBAAuB;iBA8ChE;EACf,SAAS;EACT,MAAM;EACN,IAAI;;;wBAIgB,wBAAwB,cAAc,SAAS,iCAAiC"}
@@ -0,0 +1,25 @@
1
+ import { MinorModeActionDescriptor, MinorModeCatalogService, MinorModeRecord, MinorModeSessionKind } from "./mode.mjs";
2
+ import { MINOR_MODE_COMMAND_DESCRIPTION } from "./constants/command.mjs";
3
+ //#region src/services/command/index.d.ts
4
+ export declare const MINOR_MODE_COMMAND = "minor";
5
+ type NotificationLevel = 'info' | 'warning' | 'error';
6
+ export interface MinorModeCommandPrompter {
7
+ select(title: string, options: readonly string[]): Promise<string | undefined>;
8
+ input(title: string, message: string): Promise<string | undefined>;
9
+ confirm(title: string, message: string): Promise<boolean | undefined>;
10
+ notify(message: string, level: NotificationLevel): void | Promise<void>;
11
+ }
12
+ /** A catalog mode addressed the way a person types it: id, label, or id stem. */
13
+ export declare function matchMinorMode(records: readonly MinorModeRecord[], query: string): MinorModeRecord | undefined;
14
+ /** The actions a session of this kind can currently take on a mode. */
15
+ export declare function actionsFor(record: MinorModeRecord, kind: MinorModeSessionKind): MinorModeActionDescriptor[];
16
+ export interface ExecuteMinorModeCommandOptions {
17
+ catalog: MinorModeCatalogService | undefined;
18
+ kind: MinorModeSessionKind;
19
+ ui: MinorModeCommandPrompter;
20
+ }
21
+ /** Runs the catalog command against an explicit interaction surface. */
22
+ export declare function executeMinorModeCommand(args: string, options: ExecuteMinorModeCommandOptions): Promise<void>;
23
+ //#endregion
24
+ export { MINOR_MODE_COMMAND_DESCRIPTION };
25
+ //# sourceMappingURL=command.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.d.mts","names":[],"sources":["../src/services/command/index.ts"],"mappings":";;;qBAQa;KAIR;iBAEY;EACf,OAAO,eAAe,6BAA6B;EACnD,MAAM,eAAe,kBAAkB;EACvC,QAAQ,eAAe,kBAAkB;EACzC,OAAO,iBAAiB,OAAO,2BAA2B;;;wBAQ5C,eAAe,kBAAkB,mBAAmB,gBAAgB;;wBAWpE,WAAW,QAAQ,iBAAiB,MAAM,uBAAuB;iBA8ChE;EACf,SAAS;EACT,MAAM;EACN,IAAI;;;wBAIgB,wBAAwB,cAAc,SAAS,iCAAiC"}
@@ -0,0 +1,129 @@
1
+ import { MINOR_MODE_COMMAND_DESCRIPTION } from "./constants/command.mjs";
2
+ //#region src/services/command/index.ts
3
+ const MINOR_MODE_COMMAND = "minor";
4
+ const REQUESTER_SOURCE = "@agimon-ai/doompi/mode-catalog/ui-command";
5
+ function isOn(record) {
6
+ return record.state.activation === "active" || record.state.activation === "deactivating";
7
+ }
8
+ /** A catalog mode addressed the way a person types it: id, label, or id stem. */
9
+ function matchMinorMode(records, query) {
10
+ const needle = query.toLowerCase();
11
+ return records.find((record) => record.descriptor.id.toLowerCase() === needle || record.descriptor.label.toLowerCase() === needle || record.descriptor.id.toLowerCase().split(".")[0] === needle);
12
+ }
13
+ /** The actions a session of this kind can currently take on a mode. */
14
+ function actionsFor(record, kind) {
15
+ return record.descriptor.actions.filter((action) => {
16
+ if (!action.contexts.includes(kind)) return false;
17
+ return record.state.actions.find((entry) => entry.id === action.id)?.enabled !== false;
18
+ });
19
+ }
20
+ async function promptArguments(ui, action) {
21
+ const collected = {};
22
+ for (const parameter of action.parameters) {
23
+ if (!parameter.required) continue;
24
+ const title = `${action.label}: ${parameter.label}`;
25
+ if (parameter.kind === "boolean") {
26
+ const value = await ui.confirm(title, parameter.description ?? "");
27
+ if (value === void 0) return void 0;
28
+ collected[parameter.name] = value;
29
+ continue;
30
+ }
31
+ if (parameter.kind === "enum") {
32
+ const options = parameter.choices.map((choice) => choice.label);
33
+ const picked = await ui.select(title, options);
34
+ const choice = picked === void 0 ? void 0 : parameter.choices[options.indexOf(picked)];
35
+ if (!choice) return void 0;
36
+ collected[parameter.name] = choice.value;
37
+ continue;
38
+ }
39
+ const raw = await ui.input(title, parameter.description ?? "");
40
+ if (raw === void 0 || raw === "") return void 0;
41
+ if (parameter.kind === "number") {
42
+ const value = Number(raw);
43
+ if (!Number.isFinite(value)) {
44
+ await ui.notify(`${parameter.label} must be a number.`, "warning");
45
+ return;
46
+ }
47
+ collected[parameter.name] = value;
48
+ } else collected[parameter.name] = raw;
49
+ }
50
+ return collected;
51
+ }
52
+ /** Runs the catalog command against an explicit interaction surface. */
53
+ async function executeMinorModeCommand(args, options) {
54
+ const { catalog, kind, ui } = options;
55
+ if (!catalog) {
56
+ await ui.notify("Minor modes are unavailable in this session.", "warning");
57
+ return;
58
+ }
59
+ const records = catalog.list();
60
+ const [modeQuery, actionQuery] = args.trim().split(/\s+/).filter(Boolean);
61
+ let record;
62
+ if (modeQuery) {
63
+ record = matchMinorMode(records, modeQuery);
64
+ if (!record) {
65
+ const known = records.map((entry) => entry.descriptor.label.toLowerCase()).join(", ") || "none";
66
+ await ui.notify(`No minor mode matches "${modeQuery}". Available: ${known}.`, "warning");
67
+ return;
68
+ }
69
+ } else {
70
+ const listed = records.filter((entry) => actionsFor(entry, kind).length > 0);
71
+ if (listed.length === 0) {
72
+ await ui.notify("No minor modes are available in this session.", "info");
73
+ return;
74
+ }
75
+ const labels = listed.map((entry) => `${isOn(entry) ? "[x]" : "[ ]"} ${entry.descriptor.label}: ${entry.descriptor.description}`);
76
+ const on = listed.filter(isOn).length;
77
+ const picked = await ui.select(`Minor modes (${String(on)} on)`, labels);
78
+ if (picked === void 0) return;
79
+ record = listed[labels.indexOf(picked)];
80
+ if (!record) return;
81
+ }
82
+ const actions = actionsFor(record, kind);
83
+ if (actions.length === 0) {
84
+ const reasons = [...new Set(record.state.actions.map((entry) => entry.disabledReason).filter((reason) => reason !== void 0 && reason.length > 0))];
85
+ const because = reasons.length > 0 ? ` ${reasons.join(" ")}` : "";
86
+ await ui.notify(`${record.descriptor.label} has no actions available in this session.${because}`, "warning");
87
+ return;
88
+ }
89
+ let action;
90
+ if (actionQuery) {
91
+ action = actions.find((entry) => entry.id.toLowerCase() === actionQuery.toLowerCase());
92
+ if (!action) {
93
+ const known = actions.map((entry) => entry.id).join(", ");
94
+ await ui.notify(`No "${actionQuery}" action on ${record.descriptor.label}. Available: ${known}.`, "warning");
95
+ return;
96
+ }
97
+ } else if (actions.length === 1) action = actions[0];
98
+ else {
99
+ const labels = actions.map((entry) => `${entry.label}: ${entry.description}`);
100
+ const picked = await ui.select(`${record.descriptor.label} (${record.state.activation})`, labels);
101
+ if (picked === void 0) return;
102
+ action = actions[labels.indexOf(picked)];
103
+ }
104
+ if (!action) return;
105
+ const argumentsValue = await promptArguments(ui, action);
106
+ if (argumentsValue === void 0) return;
107
+ try {
108
+ const response = await catalog.invoke({
109
+ operationId: globalThis.crypto.randomUUID(),
110
+ mode: {
111
+ source: record.descriptor.source,
112
+ id: record.descriptor.id,
113
+ ownerGeneration: record.ownerGeneration,
114
+ registrationId: record.registrationId
115
+ },
116
+ actionId: action.id,
117
+ arguments: argumentsValue
118
+ }, REQUESTER_SOURCE);
119
+ const state = response.mode.state;
120
+ const detail = state.detail ? `: ${state.detail}` : "";
121
+ await ui.notify(response.message ?? `${record.descriptor.label} is ${state.activation}${detail}.`, "info");
122
+ } catch (error) {
123
+ await ui.notify(`${record.descriptor.label} ${action.label} failed: ${error instanceof Error ? error.message : String(error)}`, "error");
124
+ }
125
+ }
126
+ //#endregion
127
+ export { MINOR_MODE_COMMAND, MINOR_MODE_COMMAND_DESCRIPTION, actionsFor, executeMinorModeCommand, matchMinorMode };
128
+
129
+ //# sourceMappingURL=command.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.mjs","names":[],"sources":["../src/services/command/index.ts"],"sourcesContent":["import type {\n MinorModeActionDescriptor,\n MinorModeArguments,\n MinorModeCatalogService,\n MinorModeRecord,\n MinorModeSessionKind,\n} from '../../schemas/mode';\n\nexport const MINOR_MODE_COMMAND = 'minor';\nexport { MINOR_MODE_COMMAND_DESCRIPTION } from '../../constants/command';\nconst REQUESTER_SOURCE = '@agimon-ai/doompi/mode-catalog/ui-command';\n\ntype NotificationLevel = 'info' | 'warning' | 'error';\n\nexport interface MinorModeCommandPrompter {\n select(title: string, options: readonly string[]): Promise<string | undefined>;\n input(title: string, message: string): Promise<string | undefined>;\n confirm(title: string, message: string): Promise<boolean | undefined>;\n notify(message: string, level: NotificationLevel): void | Promise<void>;\n}\n\nfunction isOn(record: MinorModeRecord): boolean {\n return record.state.activation === 'active' || record.state.activation === 'deactivating';\n}\n\n/** A catalog mode addressed the way a person types it: id, label, or id stem. */\nexport function matchMinorMode(records: readonly MinorModeRecord[], query: string): MinorModeRecord | undefined {\n const needle = query.toLowerCase();\n return records.find(\n (record) =>\n record.descriptor.id.toLowerCase() === needle ||\n record.descriptor.label.toLowerCase() === needle ||\n record.descriptor.id.toLowerCase().split('.')[0] === needle,\n );\n}\n\n/** The actions a session of this kind can currently take on a mode. */\nexport function actionsFor(record: MinorModeRecord, kind: MinorModeSessionKind): MinorModeActionDescriptor[] {\n return record.descriptor.actions.filter((action) => {\n if (!action.contexts.includes(kind)) return false;\n const availability = record.state.actions.find((entry) => entry.id === action.id);\n return availability?.enabled !== false;\n });\n}\n\nasync function promptArguments(\n ui: MinorModeCommandPrompter,\n action: MinorModeActionDescriptor,\n): Promise<MinorModeArguments | undefined> {\n const collected: MinorModeArguments = {};\n for (const parameter of action.parameters) {\n if (!parameter.required) continue;\n const title = `${action.label}: ${parameter.label}`;\n if (parameter.kind === 'boolean') {\n const value = await ui.confirm(title, parameter.description ?? '');\n if (value === undefined) return undefined;\n collected[parameter.name] = value;\n continue;\n }\n if (parameter.kind === 'enum') {\n const options = parameter.choices.map((choice) => choice.label);\n const picked = await ui.select(title, options);\n const choice = picked === undefined ? undefined : parameter.choices[options.indexOf(picked)];\n if (!choice) return undefined;\n collected[parameter.name] = choice.value;\n continue;\n }\n const raw = await ui.input(title, parameter.description ?? '');\n if (raw === undefined || raw === '') return undefined;\n if (parameter.kind === 'number') {\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n await ui.notify(`${parameter.label} must be a number.`, 'warning');\n return undefined;\n }\n collected[parameter.name] = value;\n } else {\n collected[parameter.name] = raw;\n }\n }\n return collected;\n}\n\nexport interface ExecuteMinorModeCommandOptions {\n catalog: MinorModeCatalogService | undefined;\n kind: MinorModeSessionKind;\n ui: MinorModeCommandPrompter;\n}\n\n/** Runs the catalog command against an explicit interaction surface. */\nexport async function executeMinorModeCommand(args: string, options: ExecuteMinorModeCommandOptions): Promise<void> {\n const { catalog, kind, ui } = options;\n if (!catalog) {\n await ui.notify('Minor modes are unavailable in this session.', 'warning');\n return;\n }\n\n const records = catalog.list();\n const [modeQuery, actionQuery] = args.trim().split(/\\s+/).filter(Boolean);\n\n let record: MinorModeRecord | undefined;\n if (modeQuery) {\n record = matchMinorMode(records, modeQuery);\n if (!record) {\n const known = records.map((entry) => entry.descriptor.label.toLowerCase()).join(', ') || 'none';\n await ui.notify(`No minor mode matches \"${modeQuery}\". Available: ${known}.`, 'warning');\n return;\n }\n } else {\n const listed = records.filter((entry) => actionsFor(entry, kind).length > 0);\n if (listed.length === 0) {\n await ui.notify('No minor modes are available in this session.', 'info');\n return;\n }\n const labels = listed.map(\n (entry) => `${isOn(entry) ? '[x]' : '[ ]'} ${entry.descriptor.label}: ${entry.descriptor.description}`,\n );\n const on = listed.filter(isOn).length;\n const picked = await ui.select(`Minor modes (${String(on)} on)`, labels);\n if (picked === undefined) return;\n record = listed[labels.indexOf(picked)];\n if (!record) return;\n }\n\n const actions = actionsFor(record, kind);\n if (actions.length === 0) {\n // The mode already said why each action is unavailable. Repeating that\n // is the difference between a dead end and an explanation: \"requires\n // an interactive session\" tells a cockpit user to reach for the TUI,\n // where the bare sentence leaves them guessing.\n const reasons = [\n ...new Set(\n record.state.actions\n .map((entry) => entry.disabledReason)\n .filter((reason): reason is string => reason !== undefined && reason.length > 0),\n ),\n ];\n const because = reasons.length > 0 ? ` ${reasons.join(' ')}` : '';\n await ui.notify(`${record.descriptor.label} has no actions available in this session.${because}`, 'warning');\n return;\n }\n\n let action: MinorModeActionDescriptor | undefined;\n if (actionQuery) {\n action = actions.find((entry) => entry.id.toLowerCase() === actionQuery.toLowerCase());\n if (!action) {\n const known = actions.map((entry) => entry.id).join(', ');\n await ui.notify(`No \"${actionQuery}\" action on ${record.descriptor.label}. Available: ${known}.`, 'warning');\n return;\n }\n } else if (actions.length === 1) {\n action = actions[0];\n } else {\n const labels = actions.map((entry) => `${entry.label}: ${entry.description}`);\n const picked = await ui.select(`${record.descriptor.label} (${record.state.activation})`, labels);\n if (picked === undefined) return;\n action = actions[labels.indexOf(picked)];\n }\n if (!action) return;\n\n const argumentsValue = await promptArguments(ui, action);\n if (argumentsValue === undefined) return;\n\n try {\n const response = await catalog.invoke(\n {\n operationId: globalThis.crypto.randomUUID(),\n mode: {\n source: record.descriptor.source,\n id: record.descriptor.id,\n ownerGeneration: record.ownerGeneration,\n registrationId: record.registrationId,\n },\n actionId: action.id,\n arguments: argumentsValue,\n },\n REQUESTER_SOURCE,\n );\n const state = response.mode.state;\n const detail = state.detail ? `: ${state.detail}` : '';\n await ui.notify(response.message ?? `${record.descriptor.label} is ${state.activation}${detail}.`, 'info');\n } catch (error) {\n await ui.notify(\n `${record.descriptor.label} ${action.label} failed: ${error instanceof Error ? error.message : String(error)}`,\n 'error',\n );\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ //#region src/constants/command.ts
2
+ const MINOR_MODE_COMMAND_DESCRIPTION = "Toggle or drive minor modes";
3
+ //#endregion
4
+ exports.MINOR_MODE_COMMAND_DESCRIPTION = MINOR_MODE_COMMAND_DESCRIPTION;
5
+
6
+ //# sourceMappingURL=command.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.cjs","names":[],"sources":["../../src/constants/command.ts"],"sourcesContent":["export const MINOR_MODE_COMMAND_DESCRIPTION = 'Toggle or drive minor modes';\n"],"mappings":";AAAA,MAAa,iCAAiC"}
@@ -0,0 +1,4 @@
1
+ //#region src/constants/command.d.ts
2
+ export declare const MINOR_MODE_COMMAND_DESCRIPTION = "Toggle or drive minor modes";
3
+ //#endregion
4
+ //# sourceMappingURL=command.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.d.cts","names":[],"sources":["../../src/constants/command.ts"],"mappings":";qBAAa"}
@@ -0,0 +1,4 @@
1
+ //#region src/constants/command.d.ts
2
+ export declare const MINOR_MODE_COMMAND_DESCRIPTION = "Toggle or drive minor modes";
3
+ //#endregion
4
+ //# sourceMappingURL=command.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.d.mts","names":[],"sources":["../../src/constants/command.ts"],"mappings":";qBAAa"}
@@ -0,0 +1,6 @@
1
+ //#region src/constants/command.ts
2
+ const MINOR_MODE_COMMAND_DESCRIPTION = "Toggle or drive minor modes";
3
+ //#endregion
4
+ export { MINOR_MODE_COMMAND_DESCRIPTION };
5
+
6
+ //# sourceMappingURL=command.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.mjs","names":[],"sources":["../../src/constants/command.ts"],"sourcesContent":["export const MINOR_MODE_COMMAND_DESCRIPTION = 'Toggle or drive minor modes';\n"],"mappings":";AAAA,MAAa,iCAAiC"}
@@ -0,0 +1,123 @@
1
+ const require_mode = require("../mode.cjs");
2
+ const require_catalog = require("../catalog.cjs");
3
+ const require_projection = require("../projection.cjs");
4
+ const require_reload_handoff = require("../reload-handoff.cjs");
5
+ const require_minor_mode_command = require("../minor-mode-command.cjs");
6
+ let _agimon_ai_doompi_core_cordis_host = require("@agimon-ai/doompi-core/cordis-host");
7
+ let _agimon_ai_doompi_core_notification = require("@agimon-ai/doompi-core/notification");
8
+ let _agimon_ai_doompi_core_transition = require("@agimon-ai/doompi-core/transition");
9
+ //#region src/controllers/catalogRuntime.ts
10
+ /** Registrations and state flips arrive in bursts; one entry covers a burst. */
11
+ const PROJECTION_SETTLE_MS = 50;
12
+ /**
13
+ * Pi's rpc mode subscribes to session events only after bindExtensions()
14
+ * returns, which is after session_start has run, so an entry journaled during
15
+ * startup never reaches a client. A publish deferred past that point does.
16
+ */
17
+ const BOOT_PUBLISH_DELAY_MS = 500;
18
+ function notify(cordis, context, body, level) {
19
+ const service = (0, _agimon_ai_doompi_core_notification.readDoomNotificationService)(cordis);
20
+ if (service) {
21
+ try {
22
+ Promise.resolve(service.request({
23
+ body,
24
+ level
25
+ })).catch(() => void 0);
26
+ } catch {}
27
+ return;
28
+ }
29
+ context.ui.notify(body, level);
30
+ }
31
+ function transitionSource(requesterSource) {
32
+ if (requesterSource.includes("voice")) return "voice";
33
+ if (requesterSource.includes("leader")) return "leader";
34
+ if (requesterSource.includes("ui")) return "ui";
35
+ return "system";
36
+ }
37
+ /**
38
+ * The minor modes that are actually on, for the composition panel.
39
+ *
40
+ * Read from the bound catalog at publish time rather than passed in, because a
41
+ * mode switching on is one of the reasons a publish happens at all, and a
42
+ * remembered list would describe the composition before the switch.
43
+ */
44
+ function createMinorModePiRuntime(pi) {
45
+ const activeCatalog = { current: void 0 };
46
+ require_minor_mode_command.registerMinorModeCommand(pi, () => activeCatalog.current);
47
+ pi.on("session_start", () => {
48
+ setTimeout(() => {
49
+ activeCatalog.republish?.();
50
+ }, BOOT_PUBLISH_DELAY_MS);
51
+ });
52
+ return { services: [(cordis) => modeCatalogPlugin(cordis, pi, activeCatalog)] };
53
+ }
54
+ function modeCatalogPlugin(cordis, pi, activeCatalog) {
55
+ cordis.inject([_agimon_ai_doompi_core_cordis_host.DOOM_CORDIS_SESSION_SERVICE, _agimon_ai_doompi_core_transition.DOOM_TRANSITION_SERVICE], (sessionContext) => {
56
+ const session = sessionContext.get(_agimon_ai_doompi_core_cordis_host.DOOM_CORDIS_SESSION_SERVICE);
57
+ const context = session.context;
58
+ const coordinator = (0, _agimon_ai_doompi_core_transition.requireDoomTransitionCoordinator)(sessionContext);
59
+ const reloadSession = session.reason === "reload";
60
+ const sessionId = session.sessionId;
61
+ const restoreSnapshot = reloadSession ? require_reload_handoff.consumeMinorModeReloadHandoff(sessionId) : void 0;
62
+ if (!reloadSession) require_reload_handoff.discardMinorModeReloadHandoff(sessionId);
63
+ const sessionKind = context.hasUI && context.mode === "tui" ? "tui" : "headless";
64
+ const catalog = require_catalog.createMinorModeCatalogHost({
65
+ sessionKind,
66
+ context,
67
+ restoreSnapshot,
68
+ onRestorationError(error) {
69
+ notify(sessionContext, context, `Could not restore a minor mode after reload: ${error instanceof Error ? error.message : String(error)}`, "warning");
70
+ },
71
+ async routeInvocation(request, requesterSource, invoke) {
72
+ let response;
73
+ const result = await coordinator.execute({
74
+ sessionId: context.sessionManager.getSessionId(),
75
+ hostGeneration: coordinator.hostGeneration,
76
+ operationId: request.operationId,
77
+ source: transitionSource(requesterSource),
78
+ target: {
79
+ axis: "live",
80
+ capability: "minor-mode"
81
+ }
82
+ }, async () => {
83
+ response = await invoke();
84
+ return "applied";
85
+ });
86
+ if (result.outcome !== "applied" || !response) throw new Error(`Minor-mode transition was ${result.outcome}: ${result.diagnostics.join(", ")}`);
87
+ return response;
88
+ }
89
+ });
90
+ sessionContext.provide(require_mode.DOOM_MINOR_MODE_CATALOG_SERVICE, catalog);
91
+ activeCatalog.current = catalog;
92
+ activeCatalog.sessionId = sessionId;
93
+ let published;
94
+ let settle;
95
+ const publish = () => {
96
+ settle = void 0;
97
+ const projection = require_projection.projectMinorModes(catalog.getSnapshot(), sessionKind);
98
+ const serialized = JSON.stringify(projection);
99
+ if (serialized === published) return;
100
+ published = serialized;
101
+ pi.appendEntry(require_mode.DOOM_MINOR_MODE_ENTRY_TYPE, projection);
102
+ };
103
+ const unsubscribe = catalog.subscribe(() => {
104
+ settle ??= setTimeout(publish, PROJECTION_SETTLE_MS);
105
+ });
106
+ activeCatalog.republish = () => {
107
+ published = void 0;
108
+ publish();
109
+ };
110
+ return () => {
111
+ if (settle) clearTimeout(settle);
112
+ unsubscribe();
113
+ activeCatalog.republish = void 0;
114
+ activeCatalog.current = void 0;
115
+ activeCatalog.sessionId = void 0;
116
+ catalog.dispose();
117
+ };
118
+ });
119
+ }
120
+ //#endregion
121
+ exports.createMinorModePiRuntime = createMinorModePiRuntime;
122
+
123
+ //# sourceMappingURL=catalogRuntime.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalogRuntime.cjs","names":["readDoomNotificationService","DOOM_CORDIS_SESSION_SERVICE","DOOM_TRANSITION_SERVICE","requireDoomTransitionCoordinator","consumeMinorModeReloadHandoff","createMinorModeCatalogHost","DOOM_MINOR_MODE_CATALOG_SERVICE","projectMinorModes","DOOM_MINOR_MODE_ENTRY_TYPE"],"sources":["../../src/controllers/catalogRuntime.ts"],"sourcesContent":["import { DOOM_CORDIS_SESSION_SERVICE, type DoomCordisSessionService } from '@agimon-ai/doompi-core/cordis-host';\nimport { type DoomNotificationLevel, readDoomNotificationService } from '@agimon-ai/doompi-core/notification';\nimport type { PiPluginContributions } from '@agimon-ai/doompi-core/pi-extension';\nimport {\n DOOM_TRANSITION_SERVICE,\n requireDoomTransitionCoordinator,\n type TransitionSource,\n} from '@agimon-ai/doompi-core/transition';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';\n\nimport {\n DOOM_MINOR_MODE_CATALOG_SERVICE,\n DOOM_MINOR_MODE_ENTRY_TYPE,\n type MinorModeActionResponse,\n type MinorModeCatalogService,\n} from '../schemas/mode';\nimport { createMinorModeCatalogHost } from '../services/catalog';\nimport { projectMinorModes } from '../services/projection';\nimport { consumeMinorModeReloadHandoff, discardMinorModeReloadHandoff } from '../services/reloadHandoff';\nimport { registerMinorModeCommand } from './minorModeCommand';\n\n/** Registrations and state flips arrive in bursts; one entry covers a burst. */\nconst PROJECTION_SETTLE_MS = 50;\n/**\n * Pi's rpc mode subscribes to session events only after bindExtensions()\n * returns, which is after session_start has run, so an entry journaled during\n * startup never reaches a client. A publish deferred past that point does.\n */\nconst BOOT_PUBLISH_DELAY_MS = 500;\n\ninterface CatalogBinding {\n current: MinorModeCatalogService | undefined;\n /** Journals the projection even if unchanged; set while a session is bound. */\n republish?: () => void;\n /**\n * The bound session, which the detail store is keyed by.\n *\n * The server launches the agent with `--session-id`, so this is the same id\n * the cockpit and the session API know the session by, and a browser asking\n * for a row's detail looks under the file this session wrote.\n */\n sessionId?: string;\n}\n\nfunction notify(cordis: Context, context: ExtensionContext, body: string, level: DoomNotificationLevel): void {\n const service = readDoomNotificationService(cordis);\n if (service) {\n try {\n void Promise.resolve(service.request({ body, level })).catch(() => undefined);\n } catch {\n // Notification delivery is best effort and does not fall back after routing.\n }\n return;\n }\n context.ui.notify(body, level);\n}\n\nfunction transitionSource(requesterSource: string): TransitionSource {\n if (requesterSource.includes('voice')) return 'voice';\n if (requesterSource.includes('leader')) return 'leader';\n if (requesterSource.includes('ui')) return 'ui';\n return 'system';\n}\n\n/**\n * The minor modes that are actually on, for the composition panel.\n *\n * Read from the bound catalog at publish time rather than passed in, because a\n * mode switching on is one of the reasons a publish happens at all, and a\n * remembered list would describe the composition before the switch.\n */\nexport function createMinorModePiRuntime(pi: ExtensionAPI): PiPluginContributions {\n // The command outlives any one session: it is registered once and reads the\n // catalog binding the session-scoped inject below sets and clears.\n const activeCatalog: CatalogBinding = { current: undefined };\n registerMinorModeCommand(pi, () => activeCatalog.current);\n // The composition is only complete once every extension has registered, so\n // the inventory is read on the same deferred tick the catalog republishes on.\n pi.on('session_start', () => {\n setTimeout(() => {\n activeCatalog.republish?.();\n }, BOOT_PUBLISH_DELAY_MS);\n });\n return { services: [(cordis: Context) => modeCatalogPlugin(cordis, pi, activeCatalog)] };\n}\n\nfunction modeCatalogPlugin(\n cordis: Context,\n pi: Pick<ExtensionAPI, 'appendEntry'>,\n activeCatalog: CatalogBinding,\n): void {\n cordis.inject([DOOM_CORDIS_SESSION_SERVICE, DOOM_TRANSITION_SERVICE], (sessionContext) => {\n const session = sessionContext.get(DOOM_CORDIS_SESSION_SERVICE) as DoomCordisSessionService;\n const context = session.context;\n const coordinator = requireDoomTransitionCoordinator(sessionContext);\n const reloadSession = session.reason === 'reload';\n const sessionId = session.sessionId;\n const restoreSnapshot = reloadSession ? consumeMinorModeReloadHandoff(sessionId) : undefined;\n if (!reloadSession) discardMinorModeReloadHandoff(sessionId);\n const sessionKind = context.hasUI && context.mode === 'tui' ? 'tui' : 'headless';\n const catalog: MinorModeCatalogService = createMinorModeCatalogHost({\n sessionKind,\n context,\n restoreSnapshot,\n onRestorationError(error) {\n notify(\n sessionContext,\n context,\n `Could not restore a minor mode after reload: ${error instanceof Error ? error.message : String(error)}`,\n 'warning',\n );\n },\n async routeInvocation(request, requesterSource, invoke) {\n let response: MinorModeActionResponse | undefined;\n const result = await coordinator.execute(\n {\n sessionId: context.sessionManager.getSessionId(),\n hostGeneration: coordinator.hostGeneration,\n operationId: request.operationId,\n source: transitionSource(requesterSource),\n target: { axis: 'live', capability: 'minor-mode' },\n },\n async () => {\n response = await invoke();\n return 'applied';\n },\n );\n if (result.outcome !== 'applied' || !response) {\n throw new Error(`Minor-mode transition was ${result.outcome}: ${result.diagnostics.join(', ')}`);\n }\n return response;\n },\n });\n sessionContext.provide(DOOM_MINOR_MODE_CATALOG_SERVICE, catalog);\n activeCatalog.current = catalog;\n activeCatalog.sessionId = sessionId;\n\n // Journal the projection so RPC clients see catalog state live and on\n // replay; identical projections are skipped so detail churn stays cheap.\n let published: string | undefined;\n let settle: NodeJS.Timeout | undefined;\n const publish = (): void => {\n settle = undefined;\n const projection = projectMinorModes(catalog.getSnapshot(), sessionKind);\n const serialized = JSON.stringify(projection);\n if (serialized === published) return;\n published = serialized;\n pi.appendEntry(DOOM_MINOR_MODE_ENTRY_TYPE, projection);\n // A mode changing is the usual reason the toolbox changed too.\n };\n const unsubscribe = catalog.subscribe(() => {\n settle ??= setTimeout(publish, PROJECTION_SETTLE_MS);\n });\n activeCatalog.republish = () => {\n published = undefined;\n publish();\n };\n\n return () => {\n if (settle) clearTimeout(settle);\n unsubscribe();\n activeCatalog.republish = undefined;\n activeCatalog.current = undefined;\n activeCatalog.sessionId = undefined;\n catalog.dispose();\n };\n });\n}\n"],"mappings":"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"}