@genesislcap/ai-assistant 15.24.2 → 15.26.0

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 (47) hide show
  1. package/dist/ai-assistant.api.json +1065 -33
  2. package/dist/ai-assistant.d.ts +288 -102
  3. package/dist/chat-driver.cjs +630 -378
  4. package/dist/chat-driver.cjs.map +4 -4
  5. package/dist/chat-driver.mjs +630 -378
  6. package/dist/chat-driver.mjs.map +4 -4
  7. package/dist/custom-elements.json +1198 -388
  8. package/dist/dts/components/agent-session/agent-session.d.ts +134 -0
  9. package/dist/dts/components/agent-session/agent-session.d.ts.map +1 -0
  10. package/dist/dts/components/agent-session/index.d.ts +2 -0
  11. package/dist/dts/components/agent-session/index.d.ts.map +1 -0
  12. package/dist/dts/components/ai-driver/ai-driver.d.ts +31 -4
  13. package/dist/dts/components/ai-driver/ai-driver.d.ts.map +1 -1
  14. package/dist/dts/components/chat-driver/chat-driver.constants.d.ts +52 -0
  15. package/dist/dts/components/chat-driver/chat-driver.constants.d.ts.map +1 -0
  16. package/dist/dts/components/chat-driver/chat-driver.d.ts +100 -82
  17. package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
  18. package/dist/dts/components/chat-driver/fold-controller.d.ts +82 -0
  19. package/dist/dts/components/chat-driver/fold-controller.d.ts.map +1 -0
  20. package/dist/dts/components/chat-driver/turn-failures.d.ts +20 -1
  21. package/dist/dts/components/chat-driver/turn-failures.d.ts.map +1 -1
  22. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +14 -24
  23. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
  24. package/dist/dts/index.d.ts +1 -0
  25. package/dist/dts/index.d.ts.map +1 -1
  26. package/dist/dts/main/main.d.ts +6 -16
  27. package/dist/dts/main/main.d.ts.map +1 -1
  28. package/dist/dts/suggestions/generate-suggestions.d.ts +35 -0
  29. package/dist/dts/suggestions/generate-suggestions.d.ts.map +1 -0
  30. package/dist/dts/utils/agents-key.d.ts +10 -0
  31. package/dist/dts/utils/agents-key.d.ts.map +1 -0
  32. package/dist/dts/utils/resolve-turn-input.d.ts +9 -0
  33. package/dist/dts/utils/resolve-turn-input.d.ts.map +1 -0
  34. package/dist/esm/components/agent-session/agent-session.js +208 -0
  35. package/dist/esm/components/agent-session/index.js +1 -0
  36. package/dist/esm/components/chat-driver/chat-driver.constants.js +72 -0
  37. package/dist/esm/components/chat-driver/chat-driver.js +190 -395
  38. package/dist/esm/components/chat-driver/fold-controller.js +184 -0
  39. package/dist/esm/components/chat-driver/turn-failures.js +14 -0
  40. package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +48 -131
  41. package/dist/esm/index.js +1 -0
  42. package/dist/esm/main/main.js +11 -38
  43. package/dist/esm/suggestions/generate-suggestions.js +123 -0
  44. package/dist/esm/utils/agents-key.js +24 -0
  45. package/dist/esm/utils/resolve-turn-input.js +16 -0
  46. package/docs/migration-GENC-1580.md +44 -36
  47. package/package.json +16 -16
@@ -0,0 +1,123 @@
1
+ import { __awaiter } from "tslib";
2
+ import { logger } from '../utils/logger';
3
+ /** Turns of transcript given to the model when suggesting follow-ups. */
4
+ const HISTORY_WINDOW = 8;
5
+ /**
6
+ * The capabilities block: what the assistant can do, so suggestions cannot promise more.
7
+ *
8
+ * Prefers the host's full agent list; falls back to the active agent's own tools when the caller
9
+ * has no cross-agent view.
10
+ */
11
+ function capabilities(ctx, allAgentInfo) {
12
+ let agentContext = '';
13
+ let toolContext = '';
14
+ if (allAgentInfo === null || allAgentInfo === void 0 ? void 0 : allAgentInfo.length) {
15
+ const agentDescriptions = allAgentInfo
16
+ .map((agent) => {
17
+ const tools = agent.tools.map((t) => t.name).join(', ');
18
+ return `- ${agent.name} (${agent.description}): ${tools ? `(Tools: ${tools})` : 'No tools'}`;
19
+ })
20
+ .join('\n');
21
+ agentContext = `The assistant has the following capabilities:\n${agentDescriptions}`;
22
+ const allToolNames = [
23
+ ...new Set(allAgentInfo.flatMap((agent) => agent.tools.map((t) => t.name))),
24
+ ].join(', ');
25
+ toolContext = allToolNames
26
+ ? `You have access to the following tools across all agents: ${allToolNames}.`
27
+ : '';
28
+ }
29
+ else if (ctx.activeAgentName) {
30
+ const toolNames = ctx.toolDefinitions.map((tool) => tool.name).join(', ');
31
+ agentContext = `You are currently acting as the "${ctx.activeAgentName}" agent.`;
32
+ toolContext = toolNames ? `You have access to the following tools: ${toolNames}.` : '';
33
+ }
34
+ return agentContext || toolContext
35
+ ? `\n\n<capabilities>\n${[agentContext, toolContext]
36
+ .filter(Boolean)
37
+ .join('\n')}\n</capabilities>\nSuggestions must only cover what the agent is capable of. Do not suggest anything outside these capabilities.`
38
+ : '';
39
+ }
40
+ const OUTPUT_FORMAT = (count) => `\n\nOutput format (strict):\n` +
41
+ `- Exactly ${count} lines.\n` +
42
+ `- One suggestion per line.\n` +
43
+ `- No numbering, bullets, markdown, emojis, code blocks, or quotes around the suggestion.\n` +
44
+ `- No preamble, headings, summary, or commentary before or after the list.`;
45
+ /**
46
+ * The system prompt sets the role only.
47
+ *
48
+ * Concrete instructions live in the user message *after* the transcript, so the context
49
+ * immediately preceding the model is "here is data, now do X" rather than "here is a dialogue,
50
+ * continue it". This matters for Anthropic — without it, Claude reads the transcript as an
51
+ * in-progress conversation and produces a normal assistant turn instead of a list.
52
+ */
53
+ export const SUGGESTIONS_SYSTEM_PROMPT = 'You generate suggested prompts that a user could send to an AI assistant. ' +
54
+ 'You never continue the conversation or roleplay as the assistant.';
55
+ /** Build the user message: starter prompts for an empty transcript, follow-ups otherwise. */
56
+ export function buildSuggestionsPrompt(ctx, history, prompt, count, allAgentInfo) {
57
+ const capabilitiesBlock = capabilities(ctx, allAgentInfo);
58
+ const guidanceBlock = prompt ? `\n\nAdditional guidance: "${prompt}"` : '';
59
+ if (history.length === 0) {
60
+ return (`Generate exactly ${count} brief starter prompts to show a user what an AI assistant can do. ` +
61
+ `Phrase each one as the user would write it. Keep them short and generic — do not invent specific names, IDs, or data (e.g. prefer "Search for a trade" over "Find all trades with Client A").` +
62
+ capabilitiesBlock +
63
+ guidanceBlock +
64
+ OUTPUT_FORMAT(count));
65
+ }
66
+ const conversationContext = history
67
+ .filter((m) => {
68
+ var _a, _b;
69
+ return (m.role === 'user' || m.role === 'assistant') &&
70
+ !((_a = m.toolCalls) === null || _a === void 0 ? void 0 : _a.length) &&
71
+ !m.thinking &&
72
+ !!((_b = m.content) === null || _b === void 0 ? void 0 : _b.trim());
73
+ })
74
+ .slice(-HISTORY_WINDOW)
75
+ .map((m) => `${m.role === 'user' ? 'User' : 'Assistant'}: ${m.content}`)
76
+ .join('\n');
77
+ return (`<conversation_history>\n${conversationContext}\n</conversation_history>\n\n` +
78
+ `The conversation above has ended. Do not continue it or respond as the assistant. ` +
79
+ `Generate exactly ${count} follow-up prompts the *user* might send next, phrased in first person as the user would write them. ` +
80
+ `The first ${Math.max(0, count - 1)} should be natural follow-ups to the last turn. ` +
81
+ `Do not invent specific names, IDs, or data values that do not appear in <conversation_history>.` +
82
+ capabilitiesBlock +
83
+ guidanceBlock +
84
+ OUTPUT_FORMAT(count));
85
+ }
86
+ /**
87
+ * Lenient parsing, as a backstop: even with the strict format above, models slip in numbering,
88
+ * bullets or surrounding markdown.
89
+ */
90
+ export function parseSuggestions(text, count) {
91
+ return (text
92
+ .split('\n')
93
+ .map((s) => s.trim())
94
+ // leading bullets/numbers ("- ", "* ", "• ", "1. ", "1) ")
95
+ .map((s) => s.replace(/^([-*•]|\d+[.)])\s+/, ''))
96
+ // surrounding bold/italic markers
97
+ .map((s) => s.replace(/^[*_]{1,3}|[*_]{1,3}$/g, '').trim())
98
+ .filter((s) => s.length > 0)
99
+ .filter((s) => !/^[-=]{2,}$/.test(s))
100
+ .filter((s) => !/^here (are|is) /i.test(s))
101
+ .filter((s) => !s.endsWith(':'))
102
+ .slice(0, count));
103
+ }
104
+ /**
105
+ * Suggested prompts for the composer.
106
+ *
107
+ * Best-effort throughout: a default provider with no `prompt()` means no suggestions, not an
108
+ * error, because this is an out-of-turn UI helper.
109
+ */
110
+ export function generateSuggestions(ctx, history, prompt, count, allAgentInfo) {
111
+ return __awaiter(this, void 0, void 0, function* () {
112
+ const defaultProvider = ctx.registry.default();
113
+ if (!defaultProvider.prompt) {
114
+ logger.warn('ChatDriver: default AI provider does not implement prompt()');
115
+ return [];
116
+ }
117
+ const userMessage = buildSuggestionsPrompt(ctx, history, prompt, count, allAgentInfo);
118
+ const text = yield defaultProvider.prompt(userMessage, {
119
+ systemPrompt: SUGGESTIONS_SYSTEM_PROMPT,
120
+ });
121
+ return parseSuggestions(text, count);
122
+ });
123
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * A stable fingerprint for an agents array, compared against the installed driver's to decide
3
+ * whether an `agents` change needs the driver rebuilt.
4
+ *
5
+ * So it must change when the driver's tool surface would, and not otherwise: handler key order is
6
+ * sorted out, agent order is significant.
7
+ */
8
+ export function agentsKey(agents) {
9
+ if (!(agents === null || agents === void 0 ? void 0 : agents.length))
10
+ return '';
11
+ return agents
12
+ .map((a) => {
13
+ var _a;
14
+ // Function-form handlers resolve per turn, so their key set is unknowable here. The marker
15
+ // still separates static from dynamic; two different resolvers are indistinguishable.
16
+ const toolNames = typeof a.toolHandlers === 'function'
17
+ ? '<fn>'
18
+ : Object.keys((_a = a.toolHandlers) !== null && _a !== void 0 ? _a : {})
19
+ .sort()
20
+ .join('+');
21
+ return `${a.name}[${toolNames}]`;
22
+ })
23
+ .join(',');
24
+ }
@@ -0,0 +1,16 @@
1
+ import { __awaiter } from "tslib";
2
+ /**
3
+ * Resolve a config input that may be a static value or a per-turn resolver.
4
+ *
5
+ * `undefined` in, `undefined` out — an unset input stays unset rather than resolving to a default
6
+ * the caller never asked for.
7
+ */
8
+ export function resolveTurnInput(input, ctx) {
9
+ return __awaiter(this, void 0, void 0, function* () {
10
+ if (input === undefined)
11
+ return undefined;
12
+ return typeof input === 'function'
13
+ ? input(ctx)
14
+ : input;
15
+ });
16
+ }
@@ -1,22 +1,29 @@
1
1
  # Migration Guide — GENC-1580 (agent lifecycle leaves the orchestrator)
2
2
 
3
- > ## ⚠️ Not shipped yet — do not action this guide
3
+ > ## ⚠️ Partly shipped — the rename has not landed, so do not action the rename
4
4
  >
5
- > **Nothing described below has landed.** This guide was written ahead of the change so the public
6
- > delta could be agreed before any code moved, and it is published early only so that agreement is
7
- > reviewable. It describes an *intended* contract.
5
+ > The structural half of this change is in. What is **not** in is the one part that would break
6
+ > you: `ChatDriver.applyAgent` has not been renamed. It was held back deliberately, to land alone
7
+ > as the last PR of GENC-1580 — the rename cannot be caught by a compiler in a JavaScript caller,
8
+ > so it is worth scheduling rather than absorbing alongside a refactor.
8
9
  >
9
- > **In the package you are reading this in, the old API is still the live one:**
10
+ > **What the package you are reading this in actually has:**
10
11
  >
11
12
  > | This guide says | Actually available today |
12
13
  > | --- | --- |
13
- > | `ChatDriver.installAgent` | ✗ — still `ChatDriver.applyAgent`, unchanged |
14
- > | `ChatDriver.activateAgent` | ✗ — does not exist |
15
- > | `ChatDriver.agentSession` | ✗ — does not exist |
16
- > | `pinned-changed` is dispatched | ✗ — still dead wiring |
14
+ > | `ChatDriver.activateAgent` | ✓ — available, fires the lifecycle hooks |
15
+ > | `ChatDriver.agentSession` | ✓ — available (`@internal`) |
16
+ > | `getActiveAgent` / `primeRestoredAgentState` implemented by both drivers | ✓ — and `setPinnedAgent` / `setFlowOwner` are on the interface now, all four **optional** |
17
+ > | `ChatDriver.installAgent` | ✗ — **still `ChatDriver.applyAgent`, unchanged** |
18
+ > | `pinned-changed` is dispatched | ✗ — still dead wiring (§7.2 of `code-health.md`) |
17
19
  >
18
20
  > **Self-check, whatever version you are on:** if `installAgent` is not present on `ChatDriver`,
19
- > none of this has landed and you should change nothing.
21
+ > the rename has not landed and you should not touch your `applyAgent` calls. §4 is also **not**
22
+ > shipped — it carries its own notice. Everything else here is current.
23
+ >
24
+ > **Nothing you have today breaks on this release.** `applyAgent` keeps working, and every
25
+ > `AiDriver` member this ticket touched is declared optional, so an implementor of that interface
26
+ > is unaffected too.
20
27
  >
21
28
  > GENC-1580 ships in more than one PR. The first delivered an internal decomposition of `main.ts`
22
29
  > only — five feature controllers, no public API change of any kind. **The agent-lifecycle work
@@ -110,45 +117,46 @@ session per conversation, and the inner driver outlives any routing decision mad
110
117
  also puts `getAgentReleaseRequested()` and the session that responds to it in the same object,
111
118
  which is the coupling that used to span two classes.
112
119
 
113
- ### 3. Four `AiDriver` members are no longer optional
120
+ ### 3. Four `AiDriver` members are implemented by both drivers
114
121
 
115
- These moved from optional to required, because `ChatDriver` now implements all four:
122
+ `ChatDriver` now implements all four, so they are no longer orchestrator-only:
116
123
 
117
124
  | Member | Was | Now |
118
125
  | --- | --- | --- |
119
- | `getActiveAgent()` | `OrchestratingDriver` only | both |
120
- | `setPinnedAgent(name)` | `OrchestratingDriver` only | both |
121
- | `setFlowOwner(name)` | `OrchestratingDriver` only | both |
122
- | `primeRestoredAgentState(snapshots)` | `OrchestratingDriver` only | both |
126
+ | `getActiveAgent?()` | `OrchestratingDriver` only | both |
127
+ | `setPinnedAgent?(name)` | not on the interface | both |
128
+ | `setFlowOwner?(name)` | not on the interface | both |
129
+ | `primeRestoredAgentState?(snapshots)` | `OrchestratingDriver` only | both |
123
130
 
124
- **If you implement `AiDriver` yourself, this is a breaking change** — you must add all four.
125
- Delegate to an `AgentSession` if you want the standard semantics, or return `undefined` / no-op if
126
- your driver genuinely owns no agents.
131
+ **Nothing to do, and nothing breaks.** All four are declared **optional**, so a third party
132
+ implementing `AiDriver` is unaffected: two were already optional and two are new additions. Keep
133
+ your `?.()` guards — they still type-check, and the interface makes no promise a caller can lean
134
+ on yet.
127
135
 
128
- Callers can drop the optional-call guards:
136
+ The `?` comes off in the same release as the `installAgent` rename, which is the only breaking
137
+ change in this ticket. That is when the guards can go:
129
138
 
130
139
  ```ts
131
- // before
132
- const reserve = driver.getActiveAgent?.()?.contextReserve;
133
-
134
- // after
135
- const reserve = driver.getActiveAgent()?.contextReserve;
140
+ // after the breaking release, not before
141
+ const reserve = driver.getActiveAgent().contextReserve;
136
142
  ```
137
143
 
138
- The remaining ten optional members on `AiDriver` are unchanged. They are a separate problem, and
139
- `ai-driver.ts` still carries a doc comment claiming the element depends only on this interface,
140
- which is not yet true.
144
+ Fourteen members on `AiDriver` remain optional. They are a separate problem, and `ai-driver.ts`
145
+ still carries a doc comment claiming the element depends only on this interface, which is not yet
146
+ true.
147
+
148
+ ### 4. `pinned-changed` — still not dispatched, still nothing to do
141
149
 
142
- ### 4. `pinned-changed` is now actually dispatched
150
+ > **Not in this release.** Described here because it is part of the intended contract; the code is
151
+ > not written. Do not act on this section.
143
152
 
144
- `setPinnedAgent()` was a bare assignment that emitted nothing, while the element registered a
145
- listener for `pinned-changed` and a comment asserting the driver did emit it. Neither half was
146
- right.
153
+ `setPinnedAgent` is a bare write that emits nothing, while the element registers a listener for
154
+ `pinned-changed` and a comment asserting the driver does emit it. Neither half is right, and
155
+ GENC-1580 did not change it: `AgentSession.setPinned` is still a plain assignment, and nothing in
156
+ the package calls `dispatchEvent` with `pinned-changed`.
147
157
 
148
- `AgentSession.setPinnedAgent()` now emits `pinned-changed` with `string | null`, on the owning
149
- driver. If you were listening for it and quietly receiving nothing, you will now receive events.
150
- If you were not, nothing changes — the element's own pin path writes through its
151
- `pinnedAgentName` setter and guards against the redundant round-trip.
158
+ The intended fix is for the pin write to report a transition the owning driver dispatches, the way
159
+ `flow-owner-changed` already works. Until then, a listener receives nothing — as before.
152
160
 
153
161
  ### 5. Stateful agents no longer depend on the orchestrator
154
162
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@genesislcap/ai-assistant",
3
3
  "description": "Genesis AI Assistant micro-frontend",
4
- "version": "15.24.2",
4
+ "version": "15.26.0",
5
5
  "license": "SEE LICENSE IN license.txt",
6
6
  "main": "dist/esm/index.js",
7
7
  "types": "dist/ai-assistant.d.ts",
@@ -74,26 +74,26 @@
74
74
  }
75
75
  },
76
76
  "devDependencies": {
77
- "@genesislcap/foundation-testing": "15.24.2",
78
- "@genesislcap/genx": "15.24.2",
79
- "@genesislcap/rollup-builder": "15.24.2",
80
- "@genesislcap/ts-builder": "15.24.2",
81
- "@genesislcap/uvu-playwright-builder": "15.24.2",
82
- "@genesislcap/vite-builder": "15.24.2",
83
- "@genesislcap/webpack-builder": "15.24.2",
77
+ "@genesislcap/foundation-testing": "15.26.0",
78
+ "@genesislcap/genx": "15.26.0",
79
+ "@genesislcap/rollup-builder": "15.26.0",
80
+ "@genesislcap/ts-builder": "15.26.0",
81
+ "@genesislcap/uvu-playwright-builder": "15.26.0",
82
+ "@genesislcap/vite-builder": "15.26.0",
83
+ "@genesislcap/webpack-builder": "15.26.0",
84
84
  "@types/dompurify": "^3.0.5",
85
85
  "@types/marked": "^5.0.2",
86
86
  "esbuild": "0.25.12"
87
87
  },
88
88
  "dependencies": {
89
- "@genesislcap/foundation-ai": "15.24.2",
90
- "@genesislcap/foundation-logger": "15.24.2",
91
- "@genesislcap/foundation-notifications": "15.24.2",
92
- "@genesislcap/foundation-redux": "15.24.2",
93
- "@genesislcap/foundation-ui": "15.24.2",
94
- "@genesislcap/foundation-utils": "15.24.2",
95
- "@genesislcap/rapid-design-system": "15.24.2",
96
- "@genesislcap/web-core": "15.24.2",
89
+ "@genesislcap/foundation-ai": "15.26.0",
90
+ "@genesislcap/foundation-logger": "15.26.0",
91
+ "@genesislcap/foundation-notifications": "15.26.0",
92
+ "@genesislcap/foundation-redux": "15.26.0",
93
+ "@genesislcap/foundation-ui": "15.26.0",
94
+ "@genesislcap/foundation-utils": "15.26.0",
95
+ "@genesislcap/rapid-design-system": "15.26.0",
96
+ "@genesislcap/web-core": "15.26.0",
97
97
  "dompurify": "^3.3.1",
98
98
  "marked": "^17.0.3"
99
99
  },